From aa48a74f910b9fb32331b2fc1bcc9fbc73611878 Mon Sep 17 00:00:00 2001 From: dillyns <49765217+dillyns@users.noreply.github.com> Date: Tue, 3 Feb 2026 16:09:46 -0500 Subject: [PATCH 01/25] Add aggressive targeting strategy and related actions for PlayerBots --- src/Ai/Base/ActionContext.h | 2 + src/Ai/Base/Actions/ChooseTargetActions.h | 8 +++ src/Ai/Base/Strategy/AggressiveStrategy.cpp | 21 ++++++ src/Ai/Base/Strategy/AggressiveStrategy.h | 22 ++++++ src/Ai/Base/StrategyContext.h | 3 + src/Ai/Base/Value/AggressiveTargetValue.cpp | 74 +++++++++++++++++++++ src/Ai/Base/Value/AggressiveTargetValue.h | 25 +++++++ src/Ai/Base/ValueContext.h | 3 + 8 files changed, 158 insertions(+) create mode 100644 src/Ai/Base/Strategy/AggressiveStrategy.cpp create mode 100644 src/Ai/Base/Strategy/AggressiveStrategy.h create mode 100644 src/Ai/Base/Value/AggressiveTargetValue.cpp create mode 100644 src/Ai/Base/Value/AggressiveTargetValue.h diff --git a/src/Ai/Base/ActionContext.h b/src/Ai/Base/ActionContext.h index 93bba4afee..6431ee87ca 100644 --- a/src/Ai/Base/ActionContext.h +++ b/src/Ai/Base/ActionContext.h @@ -125,6 +125,7 @@ class ActionContext : public NamedObjectContext creators["runaway"] = &ActionContext::runaway; creators["stay"] = &ActionContext::stay; creators["sit"] = &ActionContext::sit; + creators["aggressive target"] = &ActionContext::aggressive_target; creators["attack anything"] = &ActionContext::attack_anything; creators["attack least hp target"] = &ActionContext::attack_least_hp_target; creators["attack enemy player"] = &ActionContext::attack_enemy_player; @@ -315,6 +316,7 @@ class ActionContext : public NamedObjectContext static Action* suggest_what_to_do(PlayerbotAI* botAI) { return new SuggestWhatToDoAction(botAI); } static Action* suggest_trade(PlayerbotAI* botAI) { return new SuggestTradeAction(botAI); } static Action* suggest_dungeon(PlayerbotAI* botAI) { return new SuggestDungeonAction(botAI); } + static Action* aggressive_target(PlayerbotAI* botAI) { return new AggressiveTargetAction(botAI); } static Action* attack_anything(PlayerbotAI* botAI) { return new AttackAnythingAction(botAI); } static Action* attack_least_hp_target(PlayerbotAI* botAI) { return new AttackLeastHpTargetAction(botAI); } static Action* attack_enemy_player(PlayerbotAI* botAI) { return new AttackEnemyPlayerAction(botAI); } diff --git a/src/Ai/Base/Actions/ChooseTargetActions.h b/src/Ai/Base/Actions/ChooseTargetActions.h index 5f90501943..f09a429b60 100644 --- a/src/Ai/Base/Actions/ChooseTargetActions.h +++ b/src/Ai/Base/Actions/ChooseTargetActions.h @@ -35,6 +35,14 @@ class TankAssistAction : public AttackAction std::string const GetTargetName() override { return "tank target"; } }; +class AggressiveTargetAction : public AttackAction +{ +public: + AggressiveTargetAction(PlayerbotAI* botAI) : AttackAction(botAI, "aggressive target") {} + + std::string const GetTargetName() override { return "aggressive target"; } +}; + class AttackAnythingAction : public AttackAction { public: diff --git a/src/Ai/Base/Strategy/AggressiveStrategy.cpp b/src/Ai/Base/Strategy/AggressiveStrategy.cpp new file mode 100644 index 0000000000..557fa580eb --- /dev/null +++ b/src/Ai/Base/Strategy/AggressiveStrategy.cpp @@ -0,0 +1,21 @@ +/* + * 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 "AggressiveStrategy.h" + +#include "Playerbots.h" + +void AggressiveStrategy::InitTriggers(std::vector& triggers) +{ + NonCombatStrategy::InitTriggers(triggers); + triggers.push_back( + new TriggerNode( + "no target", + { + NextAction("aggressive target", 4.0f) + } + ) + ); +} diff --git a/src/Ai/Base/Strategy/AggressiveStrategy.h b/src/Ai/Base/Strategy/AggressiveStrategy.h new file mode 100644 index 0000000000..8a81192e70 --- /dev/null +++ b/src/Ai/Base/Strategy/AggressiveStrategy.h @@ -0,0 +1,22 @@ +/* + * 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_AGGRESSIVESTRATEGY_H +#define _PLAYERBOT_AGGRESSIVESTRATEGY_H + +#include "NonCombatStrategy.h" + +class PlayerbotAI; + +class AggressiveStrategy : public NonCombatStrategy +{ +public: + AggressiveStrategy(PlayerbotAI* botAI) : NonCombatStrategy(botAI) {} + + std::string const getName() override { return "aggressive"; } + void InitTriggers(std::vector& triggers) override; +}; + +#endif diff --git a/src/Ai/Base/StrategyContext.h b/src/Ai/Base/StrategyContext.h index 0cc6855f34..60e6808ca7 100644 --- a/src/Ai/Base/StrategyContext.h +++ b/src/Ai/Base/StrategyContext.h @@ -6,6 +6,7 @@ #ifndef _PLAYERBOT_STRATEGYCONTEXT_H #define _PLAYERBOT_STRATEGYCONTEXT_H +#include "AggressiveStrategy.h" #include "AttackEnemyPlayersStrategy.h" #include "BattlegroundStrategy.h" #include "CastTimeStrategy.h" @@ -61,6 +62,7 @@ class StrategyContext : public NamedObjectContext creators["gather"] = &StrategyContext::gather; creators["emote"] = &StrategyContext::emote; creators["passive"] = &StrategyContext::passive; + creators["aggressive"] = &StrategyContext::aggressive; creators["save mana"] = &StrategyContext::auto_save_mana; creators["food"] = &StrategyContext::food; creators["chat"] = &StrategyContext::chat; @@ -144,6 +146,7 @@ class StrategyContext : public NamedObjectContext static Strategy* gather(PlayerbotAI* botAI) { return new GatherStrategy(botAI); } static Strategy* emote(PlayerbotAI* botAI) { return new EmoteStrategy(botAI); } static Strategy* passive(PlayerbotAI* botAI) { return new PassiveStrategy(botAI); } + static Strategy* aggressive(PlayerbotAI* botAI) { return new AggressiveStrategy(botAI); } // static Strategy* conserve_mana(PlayerbotAI* botAI) { return new ConserveManaStrategy(botAI); } static Strategy* auto_save_mana(PlayerbotAI* botAI) { return new HealerAutoSaveManaStrategy(botAI); } static Strategy* food(PlayerbotAI* botAI) { return new UseFoodStrategy(botAI); } diff --git a/src/Ai/Base/Value/AggressiveTargetValue.cpp b/src/Ai/Base/Value/AggressiveTargetValue.cpp new file mode 100644 index 0000000000..b91247e875 --- /dev/null +++ b/src/Ai/Base/Value/AggressiveTargetValue.cpp @@ -0,0 +1,74 @@ +/* + * 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 "AggressiveTargetValue.h" + +#include "Playerbots.h" +#include "ServerFacade.h" +#include "SharedDefines.h" + +Unit* AggressiveTargetValue::Calculate() +{ + if (bot->IsInCombat()) + return nullptr; + + return FindTargetForAggressive(); +} + +Unit* AggressiveTargetValue::FindTargetForAggressive() +{ + Player* master = GetMaster(); + + if (master && (master == bot || master->GetMapId() != bot->GetMapId() || master->IsBeingTeleported() || + !GET_PLAYERBOT_AI(master))) + master = nullptr; + + GuidVector targets = *context->GetValue("possible targets"); + if (targets.empty()) + return nullptr; + + float aggroRange = 30.0f; + float distance = 0; + Unit* result = nullptr; + + for (ObjectGuid const guid : targets) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit) + continue; + + if (!unit->IsInWorld() || unit->IsDuringRemoveFromWorld()) + continue; + + 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 (abs(bot->GetPositionZ() - unit->GetPositionZ()) > INTERACTION_DISTANCE) + continue; + + if (!bot->InBattleground() && master && botAI->HasStrategy("follow", BotState::BOT_STATE_NON_COMBAT) && + ServerFacade::instance().GetDistance2d(master, unit) > aggroRange) + continue; + + if (!bot->IsWithinLOSInMap(unit)) + continue; + + if (bot->GetDistance(unit) > aggroRange) + continue; + + float newdistance = bot->GetDistance(unit); + if (!result || (newdistance < distance)) + { + distance = newdistance; + result = unit; + } + } + + return result; +} diff --git a/src/Ai/Base/Value/AggressiveTargetValue.h b/src/Ai/Base/Value/AggressiveTargetValue.h new file mode 100644 index 0000000000..22bf5577a9 --- /dev/null +++ b/src/Ai/Base/Value/AggressiveTargetValue.h @@ -0,0 +1,25 @@ +/* + * 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_AGGRESSIVETARGETVALUE_H +#define _PLAYERBOT_AGGRESSIVETARGETVALUE_H + +#include "TargetValue.h" + +class PlayerbotAI; +class Unit; + +class AggressiveTargetValue : public TargetValue +{ +public: + AggressiveTargetValue(PlayerbotAI* botAI, std::string const name = "aggressive target") : TargetValue(botAI, name) {} + + Unit* Calculate() override; + +private: + Unit* FindTargetForAggressive(); +}; + +#endif diff --git a/src/Ai/Base/ValueContext.h b/src/Ai/Base/ValueContext.h index ab93bcea47..ad9711a953 100644 --- a/src/Ai/Base/ValueContext.h +++ b/src/Ai/Base/ValueContext.h @@ -7,6 +7,7 @@ #define _PLAYERBOT_VALUECONTEXT_H #include "ActiveSpellValue.h" +#include "AggressiveTargetValue.h" #include "AlwaysLootListValue.h" #include "AoeHealValues.h" #include "AoeValues.h" @@ -143,6 +144,7 @@ class ValueContext : public NamedObjectContext creators["pet target"] = &ValueContext::pet_target; creators["old target"] = &ValueContext::old_target; creators["grind target"] = &ValueContext::grind_target; + creators["aggressive target"] = &ValueContext::aggressive_target; creators["rti target"] = &ValueContext::rti_target; creators["rti cc target"] = &ValueContext::rti_cc_target; creators["duel target"] = &ValueContext::duel_target; @@ -455,6 +457,7 @@ class ValueContext : public NamedObjectContext static UntypedValue* current_cc_target(PlayerbotAI* botAI) { return new CurrentCcTargetValue(botAI); } static UntypedValue* pet_target(PlayerbotAI* botAI) { return new PetTargetValue(botAI); } static UntypedValue* grind_target(PlayerbotAI* botAI) { return new GrindTargetValue(botAI); } + static UntypedValue* aggressive_target(PlayerbotAI* botAI) { return new AggressiveTargetValue(botAI); } static UntypedValue* rti_target(PlayerbotAI* botAI) { return new RtiTargetValue(botAI); } static UntypedValue* rti_cc_target(PlayerbotAI* botAI) { return new RtiCcTargetValue(botAI); } static UntypedValue* duel_target(PlayerbotAI* botAI) { return new DuelTargetValue(botAI); } From af5e90d17a463f7de5cc17b44312fa80a3f3555b Mon Sep 17 00:00:00 2001 From: dillyns <49765217+dillyns@users.noreply.github.com> Date: Tue, 3 Feb 2026 17:09:37 -0500 Subject: [PATCH 02/25] Remove call to NonCombatStrategy::InitTriggers in AggressiveStrategy --- src/Ai/Base/Strategy/AggressiveStrategy.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Ai/Base/Strategy/AggressiveStrategy.cpp b/src/Ai/Base/Strategy/AggressiveStrategy.cpp index 557fa580eb..385e94087a 100644 --- a/src/Ai/Base/Strategy/AggressiveStrategy.cpp +++ b/src/Ai/Base/Strategy/AggressiveStrategy.cpp @@ -9,7 +9,6 @@ void AggressiveStrategy::InitTriggers(std::vector& triggers) { - NonCombatStrategy::InitTriggers(triggers); triggers.push_back( new TriggerNode( "no target", From 09075387694b1a6150473f4e8a536cba9fdec9b2 Mon Sep 17 00:00:00 2001 From: dillyns <49765217+dillyns@users.noreply.github.com> Date: Thu, 5 Feb 2026 10:35:41 -0500 Subject: [PATCH 03/25] pr comment fixes --- src/Ai/Base/Value/AggressiveTargetValue.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ai/Base/Value/AggressiveTargetValue.cpp b/src/Ai/Base/Value/AggressiveTargetValue.cpp index b91247e875..7f3e171fdb 100644 --- a/src/Ai/Base/Value/AggressiveTargetValue.cpp +++ b/src/Ai/Base/Value/AggressiveTargetValue.cpp @@ -25,7 +25,7 @@ Unit* AggressiveTargetValue::FindTargetForAggressive() !GET_PLAYERBOT_AI(master))) master = nullptr; - GuidVector targets = *context->GetValue("possible targets"); + GuidVector targets = AI_VALUE(GuidVector, "possible targets"); if (targets.empty()) return nullptr; @@ -36,7 +36,7 @@ Unit* AggressiveTargetValue::FindTargetForAggressive() for (ObjectGuid const guid : targets) { Unit* unit = botAI->GetUnit(guid); - if (!unit) + if (!unit || !unit->IsAlive()) continue; if (!unit->IsInWorld() || unit->IsDuringRemoveFromWorld()) From bebac60c51cb764f65cdf29878a77470accc223b Mon Sep 17 00:00:00 2001 From: kadeshar Date: Fri, 6 Feb 2026 20:55:28 +0100 Subject: [PATCH 04/25] test-staging alignment (#2121) # Pull Request Describe what this change does and why it is needed... --- ## 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. --------- Co-authored-by: Crow --- PULL_REQUEST_TEMPLATE.md | 44 +++++++++++++--------------------- conf/playerbots.conf.dist | 2 +- src/Bot/RandomPlayerbotMgr.cpp | 2 +- 3 files changed, 18 insertions(+), 30 deletions(-) diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index a23a7e67fa..d26b1b1cd7 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -67,46 +67,34 @@ Please answer the following: ## Complexity & Impact Does this change add new decision branches? -``` -[ ] No -[ ] Yes (**explain below**) -``` +- - [ ] No +- - [ ] Yes (**explain below**) Does this change increase per-bot or per-tick processing? -``` -[ ] No -[ ] Yes (**describe and justify impact**) -``` +- - [ ] No +- - [ ] Yes (**describe and justify impact**) Could this logic scale poorly under load? -``` -[ ] No -[ ] Yes (**explain why**) -``` +- - [ ] No +- - [ ] Yes (**explain why**) --- ## Defaults & Configuration Does this change modify default bot behavior? -``` -[ ] No -[ ] Yes (**explain why**) -``` +- - [ ] 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**) -``` +- - [ ] No +- - [ ] Yes (**explain below**) If yes, please specify: @@ -123,10 +111,10 @@ 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 +- - [ ] Stability is not compromised +- - [ ] Performance impact is understood, tested, and acceptable +- - [ ] Added logic complexity is justified and explained +- - [ ] Documentation updated if needed --- diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 22615b4071..9073773167 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -990,7 +990,7 @@ AiPlayerbot.ZoneBracket.3433 = 10,22 AiPlayerbot.ZoneBracket.3525 = 10,21 # Classic WoW - High-level zones: -# Deadwind Pass (Zone ID: 10 Default Min,Max: 19,33) +# Duskwood (Zone ID: 10 Default Min,Max: 19,33) # Wetlands (Zone ID: 11 Default Min,Max: 21,30) # Redridge Mountains (Zone ID: 44 Default Min,Max: 16,28) # Hillsbrad Foothills (Zone ID: 267 Default Min,Max: 20,34) diff --git a/src/Bot/RandomPlayerbotMgr.cpp b/src/Bot/RandomPlayerbotMgr.cpp index 4707d854ff..892368c66f 100644 --- a/src/Bot/RandomPlayerbotMgr.cpp +++ b/src/Bot/RandomPlayerbotMgr.cpp @@ -1784,7 +1784,7 @@ void RandomPlayerbotMgr::PrepareZone2LevelBracket() zone2LevelBracket[3525] = {10, 21}; // Bloodmyst Isle // Classic WoW - High - level zones - zone2LevelBracket[10] = {19, 33}; // Deadwind Pass + zone2LevelBracket[10] = {19, 33}; // Duskwood zone2LevelBracket[11] = {21, 30}; // Wetlands zone2LevelBracket[44] = {16, 28}; // Redridge Mountains zone2LevelBracket[267] = {20, 34}; // Hillsbrad Foothills From b31bda85eedc70cd0c7e2618fda3916b1ca17f0c Mon Sep 17 00:00:00 2001 From: Crow Date: Fri, 6 Feb 2026 13:55:43 -0600 Subject: [PATCH 05/25] Refactor raid strategy framework (#2069) # Pull Request The purposes of this PR are to (1) establish a general raid helper framework for the benefit of future raid strategies and (2) make some improvements to problematic areas of the raid strategy code. List of changes: 1. Added new RaidBossHelpers.cpp and RaidBossHelpers.h files in the Raid folder. 3. Moved reused helpers from Karazhan, Gruul, and Magtheridon strategies to the new helper files. 4. Modified the prior function that assigned a DPS bot to store and erase timers and trackers in associative containers--the function now includes parameters for mapId (so a bot that is not in the instance will not be assigned) and for the ability to exclude a bot (useful for excluding particular important roles, such as a Warlock tank, so they are not bogged down by these extra tasks at critical moments). I also renamed it from IsInstanceTimerManager to IsMechanicTrackerBot. 5. Moved all helper files in raid strategies to Util folders (was needed for ICC, MC, and Ulduar). 6. Renamed and reordered includes of Ulduar files in AiObjectContext.cpp to match other raid strategies. a. This initially caused compile errors which made me realize that the existing code had several problems with missing includes and was compiling only due to the prior ordering in AiObjectContext.cpp. Therefore, I added the missing includes to Molten Core, Ulduar, and Vault of Archavon strategies. b. Ulduar and Old Kingdom were also using the same constant name for a spell--the reordering caused a compile error here as well, which just highlighted an existing problem that was being hidden. I renamed the constant for Ulduar to fix this, but I think the better approach going forward would be to use a namespace or enum class. But that is for another time and probably another person. 7. Several changes with respect to Ulduar files: a. The position constants and enums for spells and NPCs and such were in the trigger header file. I did not think that made sense so moved them to existing helper files. b. Since the strategy does not use multipliers, I removed all files and references to multipliers in it. c. I removed some unneeded includes. I did not do a detailed review to determine what else could be removed--I just took some out that I could tell right away were not needed. d. I renamed the ingame strategy name from "uld" to "ulduar," which I think is clearer and is still plenty short. 8. Partial refactor of Gruul and Magtheridon strategies: a. I did not due a full refactoring but made some quick changes to things I did previously that were rather stupid like repeating calculations, having useless logic like pointless IsAlive() checks for creatures already on the hostile references list, and not using the existing Position class for coordinates. b. There were a few substantive changes, such as allowing players to pick Maulgar mage and moonkin tanks with the assistant flag, but a greater refactoring of the strategies themselves is beyond this PR. c. I was clearing some containers used for Gruul and Magtheridon strategies; the methods are now fixed to erase only the applicable keys so that in the unlikely event that one server has multiple groups running Gruul or Magtheridon at the same time, there won't be timer or position tracker conflicts. ## How to Test the Changes 1. Enter any raid instance that has any code impacted by this PR 2. Engage bosses and observe if any strategies are now broken I personally tested Maulgar, Gruul, and Magtheridon and confirmed that they still work as intended. ## Complexity & Impact I do not expect this PR to have any relevant changes to in-game performance, but I will defer to those more knowledgeable than I if there are concerns in this area. As I've mentioned before, you can consider me to be like a person who has taken half an intro C++ course at best. ## AI Assistance None beyond autocomplete of repetitive changes. --------- Co-authored-by: bashermens <31279994+hermensbas@users.noreply.github.com> --- .../Action/RaidGruulsLairActions.cpp | 218 +++++------ .../GruulsLair/Action/RaidGruulsLairActions.h | 4 +- .../Multiplier/RaidGruulsLairMultipliers.cpp | 25 +- .../GruulsLair/RaidGruulsLairActionContext.h | 4 +- .../GruulsLair/RaidGruulsLairTriggerContext.h | 8 +- .../Strategy/RaidGruulsLairStrategy.cpp | 6 +- .../Trigger/RaidGruulsLairTriggers.cpp | 45 ++- .../Trigger/RaidGruulsLairTriggers.h | 8 +- .../GruulsLair/Util/RaidGruulsLairHelpers.cpp | 135 +++---- .../GruulsLair/Util/RaidGruulsLairHelpers.h | 39 +- .../Raid/Icecrown/{ => Util}/RaidIccScripts.h | 0 .../Karazhan/Action/RaidKarazhanActions.cpp | 27 +- .../Multiplier/RaidKarazhanMultipliers.cpp | 4 + .../Karazhan/Trigger/RaidKarazhanTriggers.cpp | 13 +- .../Karazhan/Util/RaidKarazhanHelpers.cpp | 108 ------ .../Raid/Karazhan/Util/RaidKarazhanHelpers.h | 19 +- .../Action/RaidMagtheridonActions.cpp | 160 ++++---- .../Action/RaidMagtheridonActions.h | 6 +- .../Multiplier/RaidMagtheridonMultipliers.cpp | 20 +- .../Trigger/RaidMagtheridonTriggers.cpp | 22 +- .../Util/RaidMagtheridonHelpers.cpp | 91 +---- .../Magtheridon/Util/RaidMagtheridonHelpers.h | 35 +- src/Ai/Raid/MoltenCore/RaidMcActionContext.h | 1 + src/Ai/Raid/MoltenCore/RaidMcTriggerContext.h | 1 + .../MoltenCore/{ => Util}/RaidMcHelpers.h | 0 src/Ai/Raid/RaidBossHelpers.cpp | 142 ++++++++ src/Ai/Raid/RaidBossHelpers.h | 21 ++ src/Ai/Raid/RaidStrategyContext.h | 4 +- .../Raid/Ulduar/Action/RaidUlduarActions.cpp | 3 - .../Multiplier/RaidUlduarMultipliers.cpp | 28 -- .../Ulduar/Multiplier/RaidUlduarMultipliers.h | 17 - src/Ai/Raid/Ulduar/RaidUlduarBossHelper.h | 169 --------- .../Ulduar/Strategy/RaidUlduarStrategy.cpp | 7 - .../Raid/Ulduar/Strategy/RaidUlduarStrategy.h | 4 +- .../Ulduar/Trigger/RaidUlduarTriggers.cpp | 2 +- .../Raid/Ulduar/Trigger/RaidUlduarTriggers.h | 178 --------- .../{ => Util}/RaidUlduarBossHelper.cpp | 39 +- .../Raid/Ulduar/Util/RaidUlduarBossHelper.h | 341 ++++++++++++++++++ .../Ulduar/{ => Util}/RaidUlduarScripts.h | 0 .../VaultOfArchavon/RaidVoAActionContext.h | 1 + .../VaultOfArchavon/RaidVoATriggerContext.h | 1 + src/Bot/Engine/AiObjectContext.cpp | 4 +- src/Bot/PlayerbotAI.cpp | 2 +- 43 files changed, 886 insertions(+), 1076 deletions(-) rename src/Ai/Raid/Icecrown/{ => Util}/RaidIccScripts.h (100%) rename src/Ai/Raid/MoltenCore/{ => Util}/RaidMcHelpers.h (100%) create mode 100644 src/Ai/Raid/RaidBossHelpers.cpp create mode 100644 src/Ai/Raid/RaidBossHelpers.h delete mode 100644 src/Ai/Raid/Ulduar/Multiplier/RaidUlduarMultipliers.cpp delete mode 100644 src/Ai/Raid/Ulduar/Multiplier/RaidUlduarMultipliers.h delete mode 100644 src/Ai/Raid/Ulduar/RaidUlduarBossHelper.h rename src/Ai/Raid/Ulduar/{ => Util}/RaidUlduarBossHelper.cpp (63%) create mode 100644 src/Ai/Raid/Ulduar/Util/RaidUlduarBossHelper.h rename src/Ai/Raid/Ulduar/{ => Util}/RaidUlduarScripts.h (100%) diff --git a/src/Ai/Raid/GruulsLair/Action/RaidGruulsLairActions.cpp b/src/Ai/Raid/GruulsLair/Action/RaidGruulsLairActions.cpp index 1a98135cac..2af82ebe99 100644 --- a/src/Ai/Raid/GruulsLair/Action/RaidGruulsLairActions.cpp +++ b/src/Ai/Raid/GruulsLair/Action/RaidGruulsLairActions.cpp @@ -2,6 +2,7 @@ #include "RaidGruulsLairHelpers.h" #include "CreatureAI.h" #include "Playerbots.h" +#include "RaidBossHelpers.h" #include "Unit.h" using namespace GruulsLairHelpers; @@ -12,6 +13,8 @@ using namespace GruulsLairHelpers; bool HighKingMaulgarMainTankAttackMaulgarAction::Execute(Event event) { Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar"); + if (!maulgar) + return false; MarkTargetWithSquare(bot, maulgar); SetRtiTarget(botAI, "square", maulgar); @@ -21,31 +24,20 @@ bool HighKingMaulgarMainTankAttackMaulgarAction::Execute(Event event) if (maulgar->GetVictim() == bot) { - const Location& tankPosition = GruulsLairLocations::MaulgarTankPosition; + const Position& position = MAULGAR_TANK_POSITION; const float maxDistance = 3.0f; - float distanceToTankPosition = bot->GetExactDist2d(tankPosition.x, tankPosition.y); + float distanceToPosition = bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()); - if (distanceToTankPosition > maxDistance) + if (distanceToPosition > maxDistance) { - float dX = tankPosition.x - bot->GetPositionX(); - float dY = tankPosition.y - bot->GetPositionY(); - float dist = sqrt(dX * dX + dY * dY); - float moveX = bot->GetPositionX() + (dX / dist) * maxDistance; - float moveY = bot->GetPositionY() + (dY / dist) * maxDistance; - return MoveTo(bot->GetMapId(), moveX, moveY, tankPosition.z, false, false, false, false, - MovementPriority::MOVEMENT_COMBAT, true, false); + float dX = position.GetPositionX() - bot->GetPositionX(); + float dY = position.GetPositionY() - bot->GetPositionY(); + float moveX = bot->GetPositionX() + (dX / distanceToPosition) * maxDistance; + float moveY = bot->GetPositionY() + (dY / distanceToPosition) * maxDistance; + return MoveTo(GRUULS_LAIR_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_COMBAT, true, true); } - - float orientation = atan2(maulgar->GetPositionY() - bot->GetPositionY(), - maulgar->GetPositionX() - bot->GetPositionX()); - bot->SetFacingTo(orientation); - } - else if (!bot->IsWithinMeleeRange(maulgar)) - { - return MoveTo(maulgar->GetMapId(), maulgar->GetPositionX(), maulgar->GetPositionY(), - maulgar->GetPositionZ(), false, false, false, false, - MovementPriority::MOVEMENT_COMBAT, true, false); } return false; @@ -55,6 +47,8 @@ bool HighKingMaulgarMainTankAttackMaulgarAction::Execute(Event event) bool HighKingMaulgarFirstAssistTankAttackOlmAction::Execute(Event event) { Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner"); + if (!olm) + return false; MarkTargetWithCircle(bot, olm); SetRtiTarget(botAI, "circle", olm); @@ -64,29 +58,22 @@ bool HighKingMaulgarFirstAssistTankAttackOlmAction::Execute(Event event) if (olm->GetVictim() == bot) { - const Location& tankPosition = GruulsLairLocations::OlmTankPosition; + const Position& position = OLM_TANK_POSITION; const float maxDistance = 3.0f; const float olmTankLeeway = 30.0f; - float distanceOlmToTankPosition = olm->GetExactDist2d(tankPosition.x, tankPosition.y); + float distanceOlmToPosition = olm->GetExactDist2d(position.GetPositionX(), position.GetPositionY()); - if (distanceOlmToTankPosition > olmTankLeeway) + if (distanceOlmToPosition > olmTankLeeway) { - float dX = tankPosition.x - bot->GetPositionX(); - float dY = tankPosition.y - bot->GetPositionY(); - float dist = sqrt(dX * dX + dY * dY); - float moveX = bot->GetPositionX() + (dX / dist) * maxDistance; - float moveY = bot->GetPositionY() + (dY / dist) * maxDistance; - return MoveTo(bot->GetMapId(), moveX, moveY, tankPosition.z, false, false, false, false, + float dX = position.GetPositionX() - bot->GetPositionX(); + float dY = position.GetPositionY() - bot->GetPositionY(); + float moveX = bot->GetPositionX() + (dX / distanceOlmToPosition) * maxDistance; + float moveY = bot->GetPositionY() + (dY / distanceOlmToPosition) * maxDistance; + return MoveTo(GRUULS_LAIR_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); } } - else if (!bot->IsWithinMeleeRange(olm)) - { - return MoveTo(olm->GetMapId(), olm->GetPositionX(), olm->GetPositionY(), - olm->GetPositionZ(), false, false, false, false, - MovementPriority::MOVEMENT_COMBAT, true, false); - } return false; } @@ -95,6 +82,8 @@ bool HighKingMaulgarFirstAssistTankAttackOlmAction::Execute(Event event) bool HighKingMaulgarSecondAssistTankAttackBlindeyeAction::Execute(Event event) { Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer"); + if (!blindeye) + return false; MarkTargetWithStar(bot, blindeye); SetRtiTarget(botAI, "star", blindeye); @@ -104,31 +93,20 @@ bool HighKingMaulgarSecondAssistTankAttackBlindeyeAction::Execute(Event event) if (blindeye->GetVictim() == bot) { - const Location& tankPosition = GruulsLairLocations::BlindeyeTankPosition; + const Position& position = BLINDEYE_TANK_POSITION; const float maxDistance = 3.0f; - float distanceToTankPosition = bot->GetExactDist2d(tankPosition.x, tankPosition.y); + float distanceToPosition = bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()); - if (distanceToTankPosition > maxDistance) + if (distanceToPosition > maxDistance) { - float dX = tankPosition.x - bot->GetPositionX(); - float dY = tankPosition.y - bot->GetPositionY(); - float dist = sqrt(dX * dX + dY * dY); - float moveX = bot->GetPositionX() + (dX / dist) * maxDistance; - float moveY = bot->GetPositionY() + (dY / dist) * maxDistance; - return MoveTo(bot->GetMapId(), moveX, moveY, tankPosition.z, false, false, false, false, + float dX = position.GetPositionX() - bot->GetPositionX(); + float dY = position.GetPositionY() - bot->GetPositionY(); + float moveX = bot->GetPositionX() + (dX / distanceToPosition) * maxDistance; + float moveY = bot->GetPositionY() + (dY / distanceToPosition) * maxDistance; + return MoveTo(GRUULS_LAIR_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); } - - float orientation = atan2(blindeye->GetPositionY() - bot->GetPositionY(), - blindeye->GetPositionX() - bot->GetPositionX()); - bot->SetFacingTo(orientation); - } - else if (!bot->IsWithinMeleeRange(blindeye)) - { - return MoveTo(blindeye->GetMapId(), blindeye->GetPositionX(), blindeye->GetPositionY(), - blindeye->GetPositionZ(), false, false, false, false, - MovementPriority::MOVEMENT_COMBAT, true, false); } return false; @@ -138,6 +116,8 @@ bool HighKingMaulgarSecondAssistTankAttackBlindeyeAction::Execute(Event event) bool HighKingMaulgarMageTankAttackKroshAction::Execute(Event event) { Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand"); + if (!krosh) + return false; MarkTargetWithTriangle(bot, krosh); SetRtiTarget(botAI, "triangle", krosh); @@ -149,25 +129,22 @@ bool HighKingMaulgarMageTankAttackKroshAction::Execute(Event event) return botAI->CastSpell("fire ward", bot); if (bot->GetTarget() != krosh->GetGUID()) - { - bot->SetSelection(krosh->GetGUID()); - return true; - } + return Attack(krosh); if (krosh->GetVictim() == bot) { - const Location& tankPosition = GruulsLairLocations::KroshTankPosition; - float distanceToKrosh = krosh->GetExactDist2d(tankPosition.x, tankPosition.y); + const Position& position = KROSH_TANK_POSITION; + float distanceToKrosh = krosh->GetExactDist2d(position.GetPositionX(), position.GetPositionY()); const float minDistance = 16.0f; const float maxDistance = 29.0f; const float tankPositionLeeway = 1.0f; if (distanceToKrosh > minDistance && distanceToKrosh < maxDistance) { - if (!bot->IsWithinDist2d(tankPosition.x, tankPosition.y, tankPositionLeeway)) + if (!bot->IsWithinDist2d(position.GetPositionX(), position.GetPositionY(), tankPositionLeeway)) { - return MoveTo(bot->GetMapId(), tankPosition.x, tankPosition.y, tankPosition.z, false, - false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); + return MoveTo(GRUULS_LAIR_MAP_ID, position.GetPositionX(), position.GetPositionY(), position.GetPositionZ(), + false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); } float orientation = atan2(krosh->GetPositionY() - bot->GetPositionY(), @@ -179,7 +156,7 @@ bool HighKingMaulgarMageTankAttackKroshAction::Execute(Event event) Position safePos; if (TryGetNewSafePosition(botAI, bot, safePos)) { - return MoveTo(krosh->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ, + return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); } } @@ -192,20 +169,19 @@ bool HighKingMaulgarMageTankAttackKroshAction::Execute(Event event) bool HighKingMaulgarMoonkinTankAttackKigglerAction::Execute(Event event) { Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed"); + if (!kiggler) + return false; MarkTargetWithDiamond(bot, kiggler); SetRtiTarget(botAI, "diamond", kiggler); if (bot->GetTarget() != kiggler->GetGUID()) - { - bot->SetSelection(kiggler->GetGUID()); - return true; - } + return Attack(kiggler); Position safePos; if (TryGetNewSafePosition(botAI, bot, safePos)) { - return MoveTo(kiggler->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ, + return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); } @@ -216,120 +192,105 @@ bool HighKingMaulgarAssignDPSPriorityAction::Execute(Event event) { // Target priority 1: Blindeye Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer"); - if (blindeye && blindeye->IsAlive()) + if (blindeye) { Position safePos; if (TryGetNewSafePosition(botAI, bot, safePos)) { bot->AttackStop(); bot->InterruptNonMeleeSpells(false); - return MoveTo(blindeye->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ, + return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); } SetRtiTarget(botAI, "star", blindeye); if (bot->GetTarget() != blindeye->GetGUID()) - { - bot->SetSelection(blindeye->GetGUID()); return Attack(blindeye); - } return false; } // Target priority 2: Olm Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner"); - if (olm && olm->IsAlive()) + if (olm) { Position safePos; if (TryGetNewSafePosition(botAI, bot, safePos)) { bot->AttackStop(); bot->InterruptNonMeleeSpells(false); - return MoveTo(olm->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ, + return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); } SetRtiTarget(botAI, "circle", olm); if (bot->GetTarget() != olm->GetGUID()) - { - bot->SetSelection(olm->GetGUID()); return Attack(olm); - } return false; } // Target priority 3a: Krosh (ranged only) Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand"); - if (krosh && krosh->IsAlive() && botAI->IsRanged(bot)) + if (krosh && botAI->IsRanged(bot)) { Position safePos; if (TryGetNewSafePosition(botAI, bot, safePos)) { bot->AttackStop(); bot->InterruptNonMeleeSpells(false); - return MoveTo(krosh->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ, + return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); } SetRtiTarget(botAI, "triangle", krosh); if (bot->GetTarget() != krosh->GetGUID()) - { - bot->SetSelection(krosh->GetGUID()); return Attack(krosh); - } return false; } // Target priority 3b: Kiggler Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed"); - if (kiggler && kiggler->IsAlive()) + if (kiggler) { Position safePos; if (TryGetNewSafePosition(botAI, bot, safePos)) { bot->AttackStop(); bot->InterruptNonMeleeSpells(false); - return MoveTo(kiggler->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ, + return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); } SetRtiTarget(botAI, "diamond", kiggler); if (bot->GetTarget() != kiggler->GetGUID()) - { - bot->SetSelection(kiggler->GetGUID()); return Attack(kiggler); - } return false; } // Target priority 4: Maulgar Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar"); - if (maulgar && maulgar->IsAlive()) + if (maulgar) { Position safePos; if (TryGetNewSafePosition(botAI, bot, safePos)) { bot->AttackStop(); bot->InterruptNonMeleeSpells(false); - return MoveTo(maulgar->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ, + return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); } SetRtiTarget(botAI, "square", maulgar); if (bot->GetTarget() != maulgar->GetGUID()) - { - bot->SetSelection(maulgar->GetGUID()); return Attack(maulgar); - } } return false; @@ -338,22 +299,22 @@ bool HighKingMaulgarAssignDPSPriorityAction::Execute(Event event) // Avoid Whirlwind and Blast Wave and generally try to stay near the center of the room bool HighKingMaulgarHealerFindSafePositionAction::Execute(Event event) { - const Location& fightCenter = GruulsLairLocations::MaulgarRoomCenter; - const float maxDistanceFromFight = 50.0f; - float distToFight = bot->GetExactDist2d(fightCenter.x, fightCenter.y); + const Position& center = MAULGAR_ROOM_CENTER; + const float maxDistanceFromCenter = 50.0f; + float distToCenter = bot->GetExactDist2d(center.GetPositionX(), center.GetPositionY()); - if (distToFight > maxDistanceFromFight) + if (distToCenter > maxDistanceFromCenter) { - float angle = atan2(bot->GetPositionY() - fightCenter.y, bot->GetPositionX() - fightCenter.x); - float destX = fightCenter.x + 40.0f * cos(angle); - float destY = fightCenter.y + 40.0f * sin(angle); - float destZ = fightCenter.z; + float angle = atan2(bot->GetPositionY() - center.GetPositionY(), bot->GetPositionX() - center.GetPositionX()); + float destX = center.GetPositionX() + 40.0f * cos(angle); + float destY = center.GetPositionY() + 40.0f * sin(angle); + float destZ = center.GetPositionZ(); if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), destX, destY, destZ)) return false; - return MoveTo(bot->GetMapId(), destX, destY, destZ, false, false, false, false, + return MoveTo(GRUULS_LAIR_MAP_ID, destX, destY, destZ, false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); } @@ -362,7 +323,7 @@ bool HighKingMaulgarHealerFindSafePositionAction::Execute(Event event) { bot->AttackStop(); bot->InterruptNonMeleeSpells(false); - return MoveTo(bot->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ, + return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); } @@ -373,6 +334,8 @@ bool HighKingMaulgarHealerFindSafePositionAction::Execute(Event event) bool HighKingMaulgarRunAwayFromWhirlwindAction::Execute(Event event) { Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar"); + if (!maulgar) + return false; const float safeDistance = 10.0f; float distance = bot->GetExactDist2d(maulgar); @@ -395,7 +358,7 @@ bool HighKingMaulgarRunAwayFromWhirlwindAction::Execute(Event event) { bot->AttackStop(); bot->InterruptNonMeleeSpells(true); - return MoveTo(maulgar->GetMapId(), destX, destY, destZ, false, false, false, false, + return MoveTo(GRUULS_LAIR_MAP_ID, destX, destY, destZ, false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); } } @@ -439,7 +402,7 @@ bool HighKingMaulgarBanishFelstalkerAction::Execute(Event event) if (warlockIndex >= 0 && warlockIndex < felStalkers.size()) { Unit* assignedFelStalker = felStalkers[warlockIndex]; - if (!assignedFelStalker->HasAura(SPELL_BANISH) && botAI->CanCastSpell(SPELL_BANISH, assignedFelStalker, true)) + if (!botAI->HasAura("banish", assignedFelStalker) && botAI->CanCastSpell("banish", assignedFelStalker)) return botAI->CastSpell("banish", assignedFelStalker); } @@ -528,40 +491,33 @@ bool HighKingMaulgarMisdirectOlmAndBlindeyeAction::Execute(Event event) // Gruul the Dragonkiller Actions // Position in center of the room -bool GruulTheDragonkillerMainTankPositionBossAction::Execute(Event event) +bool GruulTheDragonkillerTanksPositionBossAction::Execute(Event event) { Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller"); + if (!gruul) + return false; if (bot->GetVictim() != gruul) return Attack(gruul); if (gruul->GetVictim() == bot) { - const Location& tankPosition = GruulsLairLocations::GruulTankPosition; - const float maxDistance = 3.0f; + const Position& position = GRUUL_TANK_POSITION; + const float maxDistance = 5.0f; - float dX = tankPosition.x - bot->GetPositionX(); - float dY = tankPosition.y - bot->GetPositionY(); - float distanceToTankPosition = bot->GetExactDist2d(tankPosition.x, tankPosition.y); + float dX = position.GetPositionX() - bot->GetPositionX(); + float dY = position.GetPositionY() - bot->GetPositionY(); + float distanceToTankPosition = bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()); if (distanceToTankPosition > maxDistance) { float step = std::min(maxDistance, distanceToTankPosition); float moveX = bot->GetPositionX() + (dX / distanceToTankPosition) * maxDistance; float moveY = bot->GetPositionY() + (dY / distanceToTankPosition) * maxDistance; - const float moveZ = tankPosition.z; - return MoveTo(bot->GetMapId(), moveX, moveY, moveZ, false, false, false, false, + const float moveZ = position.GetPositionZ(); + return MoveTo(GRUULS_LAIR_MAP_ID, moveX, moveY, moveZ, false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); } - - float orientation = atan2(gruul->GetPositionY() - bot->GetPositionY(), - gruul->GetPositionX() - bot->GetPositionX()); - bot->SetFacingTo(orientation); - } - else if (!bot->IsWithinMeleeRange(gruul)) - { - return MoveTo(gruul->GetMapId(), gruul->GetPositionX(), gruul->GetPositionY(), gruul->GetPositionZ(), - false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); } return false; @@ -579,16 +535,16 @@ bool GruulTheDragonkillerSpreadRangedAction::Execute(Event event) static std::unordered_map hasReachedInitialPosition; Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller"); - if (gruul && gruul->IsAlive() && gruul->GetHealth() == gruul->GetMaxHealth()) + if (gruul && gruul->GetHealth() == gruul->GetMaxHealth()) { - initialPositions.clear(); - hasReachedInitialPosition.clear(); + initialPositions.erase(bot->GetGUID()); + hasReachedInitialPosition.erase(bot->GetGUID()); } - const Location& tankPosition = GruulsLairLocations::GruulTankPosition; - const float centerX = tankPosition.x; - const float centerY = tankPosition.y; - float centerZ = bot->GetPositionZ(); + const Position& position = GRUUL_TANK_POSITION; + const float centerX = position.GetPositionX(); + const float centerY = position.GetPositionY(); + const float centerZ = position.GetPositionZ(); const float minRadius = 25.0f; const float maxRadius = 40.0f; @@ -642,7 +598,7 @@ bool GruulTheDragonkillerSpreadRangedAction::Execute(Event event) bot->GetPositionY(), bot->GetPositionZ(), destX, destY, destZ)) return false; - return MoveTo(bot->GetMapId(), destX, destY, destZ, false, false, false, false, + return MoveTo(GRUULS_LAIR_MAP_ID, destX, destY, destZ, false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); } diff --git a/src/Ai/Raid/GruulsLair/Action/RaidGruulsLairActions.h b/src/Ai/Raid/GruulsLair/Action/RaidGruulsLairActions.h index 6faf7ed3ec..525e0ee6df 100644 --- a/src/Ai/Raid/GruulsLair/Action/RaidGruulsLairActions.h +++ b/src/Ai/Raid/GruulsLair/Action/RaidGruulsLairActions.h @@ -85,10 +85,10 @@ class HighKingMaulgarMisdirectOlmAndBlindeyeAction : public AttackAction bool Execute(Event event) override; }; -class GruulTheDragonkillerMainTankPositionBossAction : public AttackAction +class GruulTheDragonkillerTanksPositionBossAction : public AttackAction { public: - GruulTheDragonkillerMainTankPositionBossAction(PlayerbotAI* botAI, std::string const name = "gruul the dragonkiller main tank position boss") : AttackAction(botAI, name) {}; + GruulTheDragonkillerTanksPositionBossAction(PlayerbotAI* botAI, std::string const name = "gruul the dragonkiller tanks position boss") : AttackAction(botAI, name) {}; bool Execute(Event event) override; }; diff --git a/src/Ai/Raid/GruulsLair/Multiplier/RaidGruulsLairMultipliers.cpp b/src/Ai/Raid/GruulsLair/Multiplier/RaidGruulsLairMultipliers.cpp index 5ca2de9327..6604fc5250 100644 --- a/src/Ai/Raid/GruulsLair/Multiplier/RaidGruulsLairMultipliers.cpp +++ b/src/Ai/Raid/GruulsLair/Multiplier/RaidGruulsLairMultipliers.cpp @@ -8,18 +8,11 @@ #include "HunterActions.h" #include "MageActions.h" #include "Playerbots.h" +#include "ReachTargetActions.h" #include "WarriorActions.h" using namespace GruulsLairHelpers; -static bool IsChargeAction(Action* action) -{ - return dynamic_cast(action) || - dynamic_cast(action) || - dynamic_cast(action) || - dynamic_cast(action); -} - float HighKingMaulgarDisableTankAssistMultiplier::GetValue(Action* action) { if (IsAnyOgreBossAlive(botAI) && dynamic_cast(action)) @@ -38,12 +31,10 @@ float HighKingMaulgarAvoidWhirlwindMultiplier::GetValue(Action* action) Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer"); if (maulgar && maulgar->HasAura(SPELL_WHIRLWIND) && - (!kiggler || !kiggler->IsAlive()) && - (!krosh || !krosh->IsAlive()) && - (!olm || !olm->IsAlive()) && - (!blindeye || !blindeye->IsAlive())) + !kiggler && !krosh && !olm && !blindeye) { - if (IsChargeAction(action) || (dynamic_cast(action) && + if (dynamic_cast(action) || + (dynamic_cast(action) && !dynamic_cast(action))) return 0.0f; } @@ -57,7 +48,8 @@ float HighKingMaulgarDisableArcaneShotOnKroshMultiplier::GetValue(Action* action Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand"); Unit* target = AI_VALUE(Unit*, "current target"); - if (krosh && target && target->GetGUID() == krosh->GetGUID() && dynamic_cast(action)) + if (krosh && target && target->GetGUID() == krosh->GetGUID() && + dynamic_cast(action)) return 0.0f; return 1.0f; @@ -101,8 +93,9 @@ float GruulTheDragonkillerGroundSlamMultiplier::GetValue(Action* action) if (bot->HasAura(SPELL_GROUND_SLAM_1) || bot->HasAura(SPELL_GROUND_SLAM_2)) { - if ((dynamic_cast(action) && !dynamic_cast(action)) || - IsChargeAction(action)) + if ((dynamic_cast(action) && + !dynamic_cast(action)) || + dynamic_cast(action)) return 0.0f; } diff --git a/src/Ai/Raid/GruulsLair/RaidGruulsLairActionContext.h b/src/Ai/Raid/GruulsLair/RaidGruulsLairActionContext.h index 809fadf034..3850f58c6d 100644 --- a/src/Ai/Raid/GruulsLair/RaidGruulsLairActionContext.h +++ b/src/Ai/Raid/GruulsLair/RaidGruulsLairActionContext.h @@ -22,7 +22,7 @@ class RaidGruulsLairActionContext : public NamedObjectContext creators["high king maulgar misdirect olm and blindeye"] = &RaidGruulsLairActionContext::high_king_maulgar_misdirect_olm_and_blindeye; // Gruul the Dragonkiller - creators["gruul the dragonkiller main tank position boss"] = &RaidGruulsLairActionContext::gruul_the_dragonkiller_main_tank_position_boss; + creators["gruul the dragonkiller tanks position boss"] = &RaidGruulsLairActionContext::gruul_the_dragonkiller_tanks_position_boss; creators["gruul the dragonkiller spread ranged"] = &RaidGruulsLairActionContext::gruul_the_dragonkiller_spread_ranged; creators["gruul the dragonkiller shatter spread"] = &RaidGruulsLairActionContext::gruul_the_dragonkiller_shatter_spread; } @@ -41,7 +41,7 @@ class RaidGruulsLairActionContext : public NamedObjectContext static Action* high_king_maulgar_misdirect_olm_and_blindeye(PlayerbotAI* botAI) { return new HighKingMaulgarMisdirectOlmAndBlindeyeAction(botAI); } // Gruul the Dragonkiller - static Action* gruul_the_dragonkiller_main_tank_position_boss(PlayerbotAI* botAI) { return new GruulTheDragonkillerMainTankPositionBossAction(botAI); } + static Action* gruul_the_dragonkiller_tanks_position_boss(PlayerbotAI* botAI) { return new GruulTheDragonkillerTanksPositionBossAction(botAI); } static Action* gruul_the_dragonkiller_spread_ranged(PlayerbotAI* botAI) { return new GruulTheDragonkillerSpreadRangedAction(botAI); } static Action* gruul_the_dragonkiller_shatter_spread(PlayerbotAI* botAI) { return new GruulTheDragonkillerShatterSpreadAction(botAI); } }; diff --git a/src/Ai/Raid/GruulsLair/RaidGruulsLairTriggerContext.h b/src/Ai/Raid/GruulsLair/RaidGruulsLairTriggerContext.h index fa8e76f58b..d12b0ce467 100644 --- a/src/Ai/Raid/GruulsLair/RaidGruulsLairTriggerContext.h +++ b/src/Ai/Raid/GruulsLair/RaidGruulsLairTriggerContext.h @@ -22,8 +22,8 @@ class RaidGruulsLairTriggerContext : public NamedObjectContext creators["high king maulgar pulling olm and blindeye"] = &RaidGruulsLairTriggerContext::high_king_maulgar_pulling_olm_and_blindeye; // Gruul the Dragonkiller - creators["gruul the dragonkiller boss engaged by main tank"] = &RaidGruulsLairTriggerContext::gruul_the_dragonkiller_boss_engaged_by_main_tank; - creators["gruul the dragonkiller boss engaged by range"] = &RaidGruulsLairTriggerContext::gruul_the_dragonkiller_boss_engaged_by_range; + creators["gruul the dragonkiller boss engaged by tanks"] = &RaidGruulsLairTriggerContext::gruul_the_dragonkiller_boss_engaged_by_tanks; + creators["gruul the dragonkiller boss engaged by ranged"] = &RaidGruulsLairTriggerContext::gruul_the_dragonkiller_boss_engaged_by_ranged; creators["gruul the dragonkiller incoming shatter"] = &RaidGruulsLairTriggerContext::gruul_the_dragonkiller_incoming_shatter; } @@ -41,8 +41,8 @@ class RaidGruulsLairTriggerContext : public NamedObjectContext static Trigger* high_king_maulgar_pulling_olm_and_blindeye(PlayerbotAI* botAI) { return new HighKingMaulgarPullingOlmAndBlindeyeTrigger(botAI); } // Gruul the Dragonkiller - static Trigger* gruul_the_dragonkiller_boss_engaged_by_main_tank(PlayerbotAI* botAI) { return new GruulTheDragonkillerBossEngagedByMainTankTrigger(botAI); } - static Trigger* gruul_the_dragonkiller_boss_engaged_by_range(PlayerbotAI* botAI) { return new GruulTheDragonkillerBossEngagedByRangeTrigger(botAI); } + static Trigger* gruul_the_dragonkiller_boss_engaged_by_tanks(PlayerbotAI* botAI) { return new GruulTheDragonkillerBossEngagedByTanksTrigger(botAI); } + static Trigger* gruul_the_dragonkiller_boss_engaged_by_ranged(PlayerbotAI* botAI) { return new GruulTheDragonkillerBossEngagedByRangedTrigger(botAI); } static Trigger* gruul_the_dragonkiller_incoming_shatter(PlayerbotAI* botAI) { return new GruulTheDragonkillerIncomingShatterTrigger(botAI); } }; diff --git a/src/Ai/Raid/GruulsLair/Strategy/RaidGruulsLairStrategy.cpp b/src/Ai/Raid/GruulsLair/Strategy/RaidGruulsLairStrategy.cpp index 9ec264ea00..249c8e8a80 100644 --- a/src/Ai/Raid/GruulsLair/Strategy/RaidGruulsLairStrategy.cpp +++ b/src/Ai/Raid/GruulsLair/Strategy/RaidGruulsLairStrategy.cpp @@ -35,10 +35,10 @@ void RaidGruulsLairStrategy::InitTriggers(std::vector& triggers) NextAction("high king maulgar misdirect olm and blindeye", ACTION_RAID + 2) })); // Gruul the Dragonkiller - triggers.push_back(new TriggerNode("gruul the dragonkiller boss engaged by main tank", { - NextAction("gruul the dragonkiller main tank position boss", ACTION_RAID + 1) })); + triggers.push_back(new TriggerNode("gruul the dragonkiller boss engaged by tanks", { + NextAction("gruul the dragonkiller tanks position boss", ACTION_RAID + 1) })); - triggers.push_back(new TriggerNode("gruul the dragonkiller boss engaged by range", { + triggers.push_back(new TriggerNode("gruul the dragonkiller boss engaged by ranged", { NextAction("gruul the dragonkiller spread ranged", ACTION_RAID + 1) })); triggers.push_back(new TriggerNode("gruul the dragonkiller incoming shatter", { diff --git a/src/Ai/Raid/GruulsLair/Trigger/RaidGruulsLairTriggers.cpp b/src/Ai/Raid/GruulsLair/Trigger/RaidGruulsLairTriggers.cpp index 35d9f9a1da..4bc5efe99c 100644 --- a/src/Ai/Raid/GruulsLair/Trigger/RaidGruulsLairTriggers.cpp +++ b/src/Ai/Raid/GruulsLair/Trigger/RaidGruulsLairTriggers.cpp @@ -10,35 +10,35 @@ bool HighKingMaulgarIsMainTankTrigger::IsActive() { Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar"); - return botAI->IsMainTank(bot) && maulgar && maulgar->IsAlive(); + return botAI->IsMainTank(bot) && maulgar; } bool HighKingMaulgarIsFirstAssistTankTrigger::IsActive() { Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner"); - return botAI->IsAssistTankOfIndex(bot, 0) && olm && olm->IsAlive(); + return botAI->IsAssistTankOfIndex(bot, 0, false) && olm; } bool HighKingMaulgarIsSecondAssistTankTrigger::IsActive() { Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer"); - return botAI->IsAssistTankOfIndex(bot, 1) && blindeye && blindeye->IsAlive(); + return botAI->IsAssistTankOfIndex(bot, 1, false) && blindeye; } bool HighKingMaulgarIsMageTankTrigger::IsActive() { Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand"); - return IsKroshMageTank(botAI, bot) && krosh && krosh->IsAlive(); + return IsKroshMageTank(botAI, bot) && krosh; } bool HighKingMaulgarIsMoonkinTankTrigger::IsActive() { Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed"); - return IsKigglerMoonkinTank(botAI, bot) && kiggler && kiggler->IsAlive(); + return IsKigglerMoonkinTank(botAI, bot) && kiggler; } bool HighKingMaulgarDeterminingKillOrderTrigger::IsActive() @@ -50,11 +50,11 @@ bool HighKingMaulgarDeterminingKillOrderTrigger::IsActive() Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand"); return (botAI->IsDps(bot) || botAI->IsTank(bot)) && - !(botAI->IsMainTank(bot) && maulgar && maulgar->IsAlive()) && - !(botAI->IsAssistTankOfIndex(bot, 0) && olm && olm->IsAlive()) && - !(botAI->IsAssistTankOfIndex(bot, 1) && blindeye && blindeye->IsAlive()) && - !(IsKroshMageTank(botAI, bot) && krosh && krosh->IsAlive()) && - !(IsKigglerMoonkinTank(botAI, bot) && kiggler && kiggler->IsAlive()); + !(botAI->IsMainTank(bot) && maulgar) && + !(botAI->IsAssistTankOfIndex(bot, 0, false) && olm) && + !(botAI->IsAssistTankOfIndex(bot, 1, false) && blindeye) && + !(IsKroshMageTank(botAI, bot) && krosh) && + !(IsKigglerMoonkinTank(botAI, bot) && kiggler); } bool HighKingMaulgarHealerInDangerTrigger::IsActive() @@ -66,7 +66,7 @@ bool HighKingMaulgarBossChannelingWhirlwindTrigger::IsActive() { Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar"); - return maulgar && maulgar->IsAlive() && maulgar->HasAura(SPELL_WHIRLWIND) && + return maulgar && maulgar->HasAura(SPELL_WHIRLWIND) && !botAI->IsMainTank(bot); } @@ -74,7 +74,7 @@ bool HighKingMaulgarWildFelstalkerSpawnedTrigger::IsActive() { Unit* felStalker = AI_VALUE2(Unit*, "find target", "wild fel stalker"); - return felStalker && felStalker->IsAlive() && bot->getClass() == CLASS_WARLOCK; + return felStalker && bot->getClass() == CLASS_WARLOCK; } bool HighKingMaulgarPullingOlmAndBlindeyeTrigger::IsActive() @@ -120,12 +120,12 @@ bool HighKingMaulgarPullingOlmAndBlindeyeTrigger::IsActive() switch (hunterIndex) { case 0: - return olm && olm->IsAlive() && olm->GetHealthPct() > 98.0f && - olmTank && olmTank->IsAlive() && botAI->CanCastSpell("misdirection", olmTank); + return olm && olm->GetHealthPct() > 98.0f && + olmTank && botAI->CanCastSpell("misdirection", olmTank); case 1: - return blindeye && blindeye->IsAlive() && blindeye->GetHealthPct() > 90.0f && - blindeyeTank && blindeyeTank->IsAlive() && botAI->CanCastSpell("misdirection", blindeyeTank); + return blindeye && blindeye->GetHealthPct() > 90.0f && + blindeyeTank && botAI->CanCastSpell("misdirection", blindeyeTank); default: break; @@ -136,25 +136,24 @@ bool HighKingMaulgarPullingOlmAndBlindeyeTrigger::IsActive() // Gruul the Dragonkiller Triggers -bool GruulTheDragonkillerBossEngagedByMainTankTrigger::IsActive() +bool GruulTheDragonkillerBossEngagedByTanksTrigger::IsActive() { Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller"); - return gruul && gruul->IsAlive() && botAI->IsMainTank(bot); + return gruul && botAI->IsTank(bot); } -bool GruulTheDragonkillerBossEngagedByRangeTrigger::IsActive() +bool GruulTheDragonkillerBossEngagedByRangedTrigger::IsActive() { Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller"); - return gruul && gruul->IsAlive() && botAI->IsRanged(bot); + return gruul && botAI->IsRanged(bot); } bool GruulTheDragonkillerIncomingShatterTrigger::IsActive() { Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller"); - return gruul && gruul->IsAlive() && - (bot->HasAura(SPELL_GROUND_SLAM_1) || - bot->HasAura(SPELL_GROUND_SLAM_2)); + return gruul && (bot->HasAura(SPELL_GROUND_SLAM_1) || + bot->HasAura(SPELL_GROUND_SLAM_2)); } diff --git a/src/Ai/Raid/GruulsLair/Trigger/RaidGruulsLairTriggers.h b/src/Ai/Raid/GruulsLair/Trigger/RaidGruulsLairTriggers.h index f3f3285361..583b8b75f7 100644 --- a/src/Ai/Raid/GruulsLair/Trigger/RaidGruulsLairTriggers.h +++ b/src/Ai/Raid/GruulsLair/Trigger/RaidGruulsLairTriggers.h @@ -73,17 +73,17 @@ class HighKingMaulgarPullingOlmAndBlindeyeTrigger : public Trigger bool IsActive() override; }; -class GruulTheDragonkillerBossEngagedByMainTankTrigger : public Trigger +class GruulTheDragonkillerBossEngagedByTanksTrigger : public Trigger { public: - GruulTheDragonkillerBossEngagedByMainTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "gruul the dragonkiller boss engaged by main tank") {} + GruulTheDragonkillerBossEngagedByTanksTrigger(PlayerbotAI* botAI) : Trigger(botAI, "gruul the dragonkiller boss engaged by tanks") {} bool IsActive() override; }; -class GruulTheDragonkillerBossEngagedByRangeTrigger : public Trigger +class GruulTheDragonkillerBossEngagedByRangedTrigger : public Trigger { public: - GruulTheDragonkillerBossEngagedByRangeTrigger(PlayerbotAI* botAI) : Trigger(botAI, "gruul the dragonkiller boss engaged by range") {} + GruulTheDragonkillerBossEngagedByRangedTrigger(PlayerbotAI* botAI) : Trigger(botAI, "gruul the dragonkiller boss engaged by ranged") {} bool IsActive() override; }; diff --git a/src/Ai/Raid/GruulsLair/Util/RaidGruulsLairHelpers.cpp b/src/Ai/Raid/GruulsLair/Util/RaidGruulsLairHelpers.cpp index 0c8a23a19c..27c703aecc 100644 --- a/src/Ai/Raid/GruulsLair/Util/RaidGruulsLairHelpers.cpp +++ b/src/Ai/Raid/GruulsLair/Util/RaidGruulsLairHelpers.cpp @@ -6,19 +6,16 @@ namespace GruulsLairHelpers { - namespace GruulsLairLocations - { - // Olm does not chase properly due to the Core's caster movement issues - // Thus, the below "OlmTankPosition" is beyond the actual desired tanking location - // It is the spot to which the OlmTank runs to to pull Olm to a decent tanking location - // "MaulgarRoomCenter" is to keep healers in a centralized location - const Location MaulgarTankPosition = { 90.686f, 167.047f, -13.234f }; - const Location OlmTankPosition = { 87.485f, 234.942f, -3.635f }; - const Location BlindeyeTankPosition = { 99.681f, 213.989f, -10.345f }; - const Location KroshTankPosition = { 116.880f, 166.208f, -14.231f }; - const Location MaulgarRoomCenter = { 88.754f, 150.759f, -11.569f }; - const Location GruulTankPosition = { 241.238f, 365.025f, -4.220f }; - } + // Olm does not chase properly due to the Core's caster movement issues + // Thus, the below "OlmTankPosition" is beyond the actual desired tanking location + // It is the spot to which the OlmTank runs to to pull Olm to a decent tanking location + // "MaulgarRoomCenter" is to keep healers in a centralized location + const Position MAULGAR_TANK_POSITION = { 90.686f, 167.047f, -13.234f }; + const Position OLM_TANK_POSITION = { 87.485f, 234.942f, -3.635f }; + const Position BLINDEYE_TANK_POSITION = { 99.681f, 213.989f, -10.345f }; + const Position KROSH_TANK_POSITION = { 116.880f, 166.208f, -14.231f }; + const Position MAULGAR_ROOM_CENTER = { 88.754f, 150.759f, -11.569f }; + const Position GRUUL_TANK_POSITION = { 241.238f, 365.025f, -4.220f }; bool IsAnyOgreBossAlive(PlayerbotAI* botAI) { @@ -42,84 +39,43 @@ namespace GruulsLairHelpers return false; } - void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId) + bool IsKroshMageTank(PlayerbotAI* botAI, Player* bot) { Group* group = bot->GetGroup(); - if (!target || !group) - return; + if (!group) + return false; - ObjectGuid currentGuid = group->GetTargetIcon(iconId); - if (currentGuid != target->GetGUID()) + // (1) First loop: Return the first assistant Mage (real player or bot) + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) { - group->SetTargetIcon(iconId, bot->GetGUID(), target->GetGUID()); - } - } - - void MarkTargetWithSquare(Player* bot, Unit* target) - { - MarkTargetWithIcon(bot, target, RtiTargetValue::squareIndex); - } - - void MarkTargetWithStar(Player* bot, Unit* target) - { - MarkTargetWithIcon(bot, target, RtiTargetValue::starIndex); - } - - void MarkTargetWithCircle(Player* bot, Unit* target) - { - MarkTargetWithIcon(bot, target, RtiTargetValue::circleIndex); - } - - void MarkTargetWithDiamond(Player* bot, Unit* target) - { - MarkTargetWithIcon(bot, target, RtiTargetValue::diamondIndex); - } - - void MarkTargetWithTriangle(Player* bot, Unit* target) - { - MarkTargetWithIcon(bot, target, RtiTargetValue::triangleIndex); - } - - void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target) - { - if (!target) - return; - - std::string currentRti = botAI->GetAiObjectContext()->GetValue("rti")->Get(); - Unit* currentTarget = botAI->GetAiObjectContext()->GetValue("rti target")->Get(); + Player* member = ref->GetSource(); + if (!member || !member->IsAlive() || member->getClass() != CLASS_MAGE) + continue; - if (currentRti != rtiName || currentTarget != target) - { - botAI->GetAiObjectContext()->GetValue("rti")->Set(rtiName); - botAI->GetAiObjectContext()->GetValue("rti target")->Set(target); + if (group->IsAssistant(member->GetGUID())) + return member == bot; } - } - - bool IsKroshMageTank(PlayerbotAI* botAI, Player* bot) - { - Group* group = bot->GetGroup(); - if (!group) - return false; + // (2) Fall back to bot Mage with highest HP Player* highestHpMage = nullptr; uint32 highestHp = 0; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) { Player* member = ref->GetSource(); - if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member)) + if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) || + member->getClass() != CLASS_MAGE) continue; - if (member->getClass() == CLASS_MAGE) + uint32 hp = member->GetMaxHealth(); + if (!highestHpMage || hp > highestHp) { - uint32 hp = member->GetMaxHealth(); - if (!highestHpMage || hp > highestHp) - { - highestHpMage = member; - highestHp = hp; - } + highestHpMage = member; + highestHp = hp; } } + // (3) Return the found Mage tank, or nullptr if none found return highestHpMage == bot; } @@ -129,30 +85,37 @@ namespace GruulsLairHelpers if (!group) return false; + // (1) First loop: Return the first assistant Moonkin (real player or bot) + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive() || member->getClass() != CLASS_DRUID) + continue; + + if (group->IsAssistant(member->GetGUID()) && + AiFactory::GetPlayerSpecTab(member) == DRUID_TAB_BALANCE) + return member == bot; + } + + // (2) Fall back to bot Moonkin with highest HP Player* highestHpMoonkin = nullptr; uint32 highestHp = 0; - for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) { Player* member = ref->GetSource(); - if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member)) + if (!member || !member->IsAlive() || member->getClass() != CLASS_DRUID || + !GET_PLAYERBOT_AI(member) || AiFactory::GetPlayerSpecTab(member) != DRUID_TAB_BALANCE) continue; - if (member->getClass() == CLASS_DRUID) + uint32 hp = member->GetMaxHealth(); + if (!highestHpMoonkin || hp > highestHp) { - int tab = AiFactory::GetPlayerSpecTab(member); - if (tab == DRUID_TAB_BALANCE) - { - uint32 hp = member->GetMaxHealth(); - if (!highestHpMoonkin || hp > highestHp) - { - highestHpMoonkin = member; - highestHp = hp; - } - } + highestHpMoonkin = member; + highestHp = hp; } } + // (3) Return the found Moonkin tank, or nullptr if none found return highestHpMoonkin == bot; } diff --git a/src/Ai/Raid/GruulsLair/Util/RaidGruulsLairHelpers.h b/src/Ai/Raid/GruulsLair/Util/RaidGruulsLairHelpers.h index c7becc8362..4615a9b7ac 100644 --- a/src/Ai/Raid/GruulsLair/Util/RaidGruulsLairHelpers.h +++ b/src/Ai/Raid/GruulsLair/Util/RaidGruulsLairHelpers.h @@ -2,23 +2,19 @@ #define RAID_GRUULSLAIRHELPERS_H #include "PlayerbotAI.h" -#include "RtiTargetValue.h" namespace GruulsLairHelpers { enum GruulsLairSpells { // High King Maulgar - SPELL_WHIRLWIND = 33238, + SPELL_WHIRLWIND = 33238, // Krosh Firehand - SPELL_SPELL_SHIELD = 33054, + SPELL_SPELL_SHIELD = 33054, // Hunter - SPELL_MISDIRECTION = 35079, - - // Warlock - SPELL_BANISH = 18647, // Rank 2 + SPELL_MISDIRECTION = 35079, // Gruul the Dragonkiller SPELL_GROUND_SLAM_1 = 33525, @@ -30,33 +26,20 @@ namespace GruulsLairHelpers NPC_WILD_FEL_STALKER = 18847, }; + constexpr uint32 GRUULS_LAIR_MAP_ID = 565; + bool IsAnyOgreBossAlive(PlayerbotAI* botAI); - void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId); - void MarkTargetWithSquare(Player* bot, Unit* target); - void MarkTargetWithStar(Player* bot, Unit* target); - void MarkTargetWithCircle(Player* bot, Unit* target); - void MarkTargetWithDiamond(Player* bot, Unit* target); - void MarkTargetWithTriangle(Player* bot, Unit* target); - void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target); bool IsKroshMageTank(PlayerbotAI* botAI, Player* bot); bool IsKigglerMoonkinTank(PlayerbotAI* botAI, Player* bot); bool IsPositionSafe(PlayerbotAI* botAI, Player* bot, Position pos); bool TryGetNewSafePosition(PlayerbotAI* botAI, Player* bot, Position& outPos); - struct Location - { - float x, y, z; - }; - - namespace GruulsLairLocations - { - extern const Location MaulgarTankPosition; - extern const Location OlmTankPosition; - extern const Location BlindeyeTankPosition; - extern const Location KroshTankPosition; - extern const Location MaulgarRoomCenter; - extern const Location GruulTankPosition; - } + extern const Position MAULGAR_TANK_POSITION; + extern const Position OLM_TANK_POSITION; + extern const Position BLINDEYE_TANK_POSITION; + extern const Position KROSH_TANK_POSITION; + extern const Position MAULGAR_ROOM_CENTER; + extern const Position GRUUL_TANK_POSITION; } #endif diff --git a/src/Ai/Raid/Icecrown/RaidIccScripts.h b/src/Ai/Raid/Icecrown/Util/RaidIccScripts.h similarity index 100% rename from src/Ai/Raid/Icecrown/RaidIccScripts.h rename to src/Ai/Raid/Icecrown/Util/RaidIccScripts.h diff --git a/src/Ai/Raid/Karazhan/Action/RaidKarazhanActions.cpp b/src/Ai/Raid/Karazhan/Action/RaidKarazhanActions.cpp index adf0eac488..4d40a9eedb 100644 --- a/src/Ai/Raid/Karazhan/Action/RaidKarazhanActions.cpp +++ b/src/Ai/Raid/Karazhan/Action/RaidKarazhanActions.cpp @@ -2,6 +2,7 @@ #include "RaidKarazhanHelpers.h" #include "Playerbots.h" #include "PlayerbotTextMgr.h" +#include "RaidBossHelpers.h" using namespace KarazhanHelpers; @@ -44,7 +45,7 @@ bool AttumenTheHuntsmanMarkTargetAction::Execute(Event event) Unit* attumenMounted = GetFirstAliveUnitByEntry(botAI, NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED); if (attumenMounted) { - if (IsInstanceTimerManager(botAI, bot)) + if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr)) MarkTargetWithStar(bot, attumenMounted); SetRtiTarget(botAI, "star", attumenMounted); @@ -57,7 +58,7 @@ bool AttumenTheHuntsmanMarkTargetAction::Execute(Event event) } else if (Unit* midnight = AI_VALUE2(Unit*, "find target", "midnight")) { - if (IsInstanceTimerManager(botAI, bot)) + if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr)) MarkTargetWithStar(bot, midnight); if (!botAI->IsAssistTankOfIndex(bot, 0)) @@ -180,7 +181,7 @@ bool MoroesMarkTargetAction::Execute(Event event) if (target) { - if (IsInstanceTimerManager(botAI, bot)) + if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr)) MarkTargetWithSkull(bot, target); SetRtiTarget(botAI, "skull", target); @@ -405,7 +406,7 @@ bool TheCuratorMarkAstralFlareAction::Execute(Event event) if (!flare) return false; - if (IsInstanceTimerManager(botAI, bot)) + if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr)) MarkTargetWithSkull(bot, flare); SetRtiTarget(botAI, "skull", flare); @@ -469,11 +470,11 @@ bool TheCuratorSpreadRangedAction::Execute(Event event) // Prioritize (1) Demon Chains, (2) Kil'rek, (3) Illhoof bool TerestianIllhoofMarkTargetAction::Execute(Event event) { - Unit* demonChains = AI_VALUE2(Unit*, "find target", "demon chains"); - Unit* kilrek = AI_VALUE2(Unit*, "find target", "kil'rek"); + Unit* demonChains = GetFirstAliveUnitByEntry(botAI, NPC_DEMON_CHAINS); + Unit* kilrek = GetFirstAliveUnitByEntry(botAI, NPC_KILREK); Unit* illhoof = AI_VALUE2(Unit*, "find target", "terestian illhoof"); - Unit* target = GetFirstAliveUnit({demonChains, kilrek, illhoof}); + Unit* target = GetFirstAliveUnit({demonChains, kilrek, illhoof}); if (target) MarkTargetWithSkull(bot, target); @@ -1007,7 +1008,7 @@ bool NetherspiteManageTimersAndTrackersAction::Execute(Event event) if (netherspite->GetHealth() == netherspite->GetMaxHealth() && !netherspite->HasAura(SPELL_GREEN_BEAM_HEAL)) { - if (IsInstanceTimerManager(botAI, bot)) + if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr)) netherspiteDpsWaitTimer.insert_or_assign(instanceId, now); if (botAI->IsTank(bot) && !bot->HasAura(SPELL_RED_BEAM_DEBUFF)) @@ -1018,7 +1019,7 @@ bool NetherspiteManageTimersAndTrackersAction::Execute(Event event) } else if (netherspite->HasAura(SPELL_NETHERSPITE_BANISHED)) { - if (IsInstanceTimerManager(botAI, bot)) + if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr)) netherspiteDpsWaitTimer.erase(instanceId); if (botAI->IsTank(bot)) @@ -1029,7 +1030,7 @@ bool NetherspiteManageTimersAndTrackersAction::Execute(Event event) } else if (!netherspite->HasAura(SPELL_NETHERSPITE_BANISHED)) { - if (IsInstanceTimerManager(botAI, bot)) + if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr)) netherspiteDpsWaitTimer.try_emplace(instanceId, now); if (botAI->IsTank(bot) && bot->HasAura(SPELL_RED_BEAM_DEBUFF)) @@ -1458,7 +1459,7 @@ bool NightbaneManageTimersAndTrackersAction::Execute(Event event) if (botAI->IsRanged(bot)) nightbaneRangedStep.erase(botGuid); - if (IsInstanceTimerManager(botAI, bot)) + if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr)) nightbaneDpsWaitTimer.erase(instanceId); } // Erase flight phase timer and Rain of Bones tracker on ground phase and start DPS wait timer @@ -1466,7 +1467,7 @@ bool NightbaneManageTimersAndTrackersAction::Execute(Event event) { nightbaneRainOfBonesHit.erase(botGuid); - if (IsInstanceTimerManager(botAI, bot)) + if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr)) { nightbaneFlightPhaseStartTimer.erase(instanceId); nightbaneDpsWaitTimer.try_emplace(instanceId, now); @@ -1482,7 +1483,7 @@ bool NightbaneManageTimersAndTrackersAction::Execute(Event event) if (botAI->IsRanged(bot)) nightbaneRangedStep.erase(botGuid); - if (IsInstanceTimerManager(botAI, bot)) + if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr)) { nightbaneDpsWaitTimer.erase(instanceId); nightbaneFlightPhaseStartTimer.try_emplace(instanceId, now); diff --git a/src/Ai/Raid/Karazhan/Multiplier/RaidKarazhanMultipliers.cpp b/src/Ai/Raid/Karazhan/Multiplier/RaidKarazhanMultipliers.cpp index 117a17f38a..06a3335c39 100644 --- a/src/Ai/Raid/Karazhan/Multiplier/RaidKarazhanMultipliers.cpp +++ b/src/Ai/Raid/Karazhan/Multiplier/RaidKarazhanMultipliers.cpp @@ -10,6 +10,7 @@ #include "MageActions.h" #include "Playerbots.h" #include "PriestActions.h" +#include "RaidBossHelpers.h" #include "ReachTargetActions.h" #include "RogueActions.h" #include "ShamanActions.h" @@ -242,6 +243,9 @@ float PrinceMalchezaarEnfeebleKeepDistanceMultiplier::GetValue(Action* action) if (bot->HasAura(SPELL_ENFEEBLE)) { + if (dynamic_cast(action)) + return 0.0f; + if (dynamic_cast(action) && !dynamic_cast(action)) return 0.0f; diff --git a/src/Ai/Raid/Karazhan/Trigger/RaidKarazhanTriggers.cpp b/src/Ai/Raid/Karazhan/Trigger/RaidKarazhanTriggers.cpp index 2fb7d5af0f..3c43aa898c 100644 --- a/src/Ai/Raid/Karazhan/Trigger/RaidKarazhanTriggers.cpp +++ b/src/Ai/Raid/Karazhan/Trigger/RaidKarazhanTriggers.cpp @@ -2,6 +2,7 @@ #include "RaidKarazhanHelpers.h" #include "RaidKarazhanActions.h" #include "Playerbots.h" +#include "RaidBossHelpers.h" using namespace KarazhanHelpers; @@ -40,7 +41,7 @@ bool AttumenTheHuntsmanAttumenIsMountedTrigger::IsActive() bool AttumenTheHuntsmanBossWipesAggroWhenMountingTrigger::IsActive() { - if (!IsInstanceTimerManager(botAI, bot)) + if (!IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr)) return false; Unit* midnight = AI_VALUE2(Unit*, "find target", "midnight"); @@ -110,7 +111,7 @@ bool BigBadWolfBossIsChasingLittleRedRidingHoodTrigger::IsActive() bool RomuloAndJulianneBothBossesRevivedTrigger::IsActive() { - if (!IsInstanceTimerManager(botAI, bot)) + if (!IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr)) return false; Unit* romulo = AI_VALUE2(Unit*, "find target", "romulo"); @@ -126,7 +127,7 @@ bool RomuloAndJulianneBothBossesRevivedTrigger::IsActive() bool WizardOfOzNeedTargetPriorityTrigger::IsActive() { - if (!IsInstanceTimerManager(botAI, bot)) + if (!IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr)) return false; Unit* dorothee = AI_VALUE2(Unit*, "find target", "dorothee"); @@ -178,7 +179,7 @@ bool TheCuratorBossAstralFlaresCastArcingSearTrigger::IsActive() bool TerestianIllhoofNeedTargetPriorityTrigger::IsActive() { - if (!IsInstanceTimerManager(botAI, bot)) + if (!IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr)) return false; Unit* illhoof = AI_VALUE2(Unit*, "find target", "terestian illhoof"); @@ -202,7 +203,7 @@ bool ShadeOfAranFlameWreathIsActiveTrigger::IsActive() // Exclusion of Banish is so the player may Banish elementals if they wish bool ShadeOfAranConjuredElementalsSummonedTrigger::IsActive() { - if (!IsInstanceTimerManager(botAI, bot)) + if (!IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr)) return false; Unit* elemental = AI_VALUE2(Unit*, "find target", "conjured elemental"); @@ -279,7 +280,7 @@ bool NetherspiteBossIsBanishedTrigger::IsActive() bool NetherspiteNeedToManageTimersAndTrackersTrigger::IsActive() { - if (!botAI->IsTank(bot) && !IsInstanceTimerManager(botAI, bot)) + if (!botAI->IsTank(bot) && !IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr)) return false; Unit* netherspite = AI_VALUE2(Unit*, "find target", "netherspite"); diff --git a/src/Ai/Raid/Karazhan/Util/RaidKarazhanHelpers.cpp b/src/Ai/Raid/Karazhan/Util/RaidKarazhanHelpers.cpp index 821cc67019..ea989dcf31 100644 --- a/src/Ai/Raid/Karazhan/Util/RaidKarazhanHelpers.cpp +++ b/src/Ai/Raid/Karazhan/Util/RaidKarazhanHelpers.cpp @@ -1,7 +1,6 @@ #include "RaidKarazhanHelpers.h" #include "RaidKarazhanActions.h" #include "Playerbots.h" -#include "RtiTargetValue.h" namespace KarazhanHelpers { @@ -52,75 +51,6 @@ namespace KarazhanHelpers const Position NIGHTBANE_FLIGHT_STACK_POSITION = { -11159.555f, -1893.526f, 91.473f }; // Broken Barrel const Position NIGHTBANE_RAIN_OF_BONES_POSITION = { -11165.233f, -1911.123f, 91.473f }; - void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId) - { - if (!target) - return; - - if (Group* group = bot->GetGroup()) - { - ObjectGuid currentGuid = group->GetTargetIcon(iconId); - if (currentGuid != target->GetGUID()) - group->SetTargetIcon(iconId, bot->GetGUID(), target->GetGUID()); - } - } - - void MarkTargetWithSkull(Player* bot, Unit* target) - { - MarkTargetWithIcon(bot, target, RtiTargetValue::skullIndex); - } - - void MarkTargetWithSquare(Player* bot, Unit* target) - { - MarkTargetWithIcon(bot, target, RtiTargetValue::squareIndex); - } - - void MarkTargetWithStar(Player* bot, Unit* target) - { - MarkTargetWithIcon(bot, target, RtiTargetValue::starIndex); - } - - void MarkTargetWithCircle(Player* bot, Unit* target) - { - MarkTargetWithIcon(bot, target, RtiTargetValue::circleIndex); - } - - void MarkTargetWithMoon(Player* bot, Unit* target) - { - MarkTargetWithIcon(bot, target, RtiTargetValue::moonIndex); - } - - void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target) - { - if (!target) - return; - - std::string currentRti = botAI->GetAiObjectContext()->GetValue("rti")->Get(); - Unit* currentTarget = botAI->GetAiObjectContext()->GetValue("rti target")->Get(); - - if (currentRti != rtiName || currentTarget != target) - { - botAI->GetAiObjectContext()->GetValue("rti")->Set(rtiName); - botAI->GetAiObjectContext()->GetValue("rti target")->Set(target); - } - } - - // Only one bot is needed to set/reset instance-wide timers - bool IsInstanceTimerManager(PlayerbotAI* botAI, Player* bot) - { - if (Group* group = bot->GetGroup()) - { - for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) - { - Player* member = ref->GetSource(); - if (member && member->IsAlive() && botAI->IsDps(member) && GET_PLAYERBOT_AI(member)) - return member == bot; - } - } - - return false; - } - Unit* GetFirstAliveUnit(const std::vector& units) { for (Unit* unit : units) @@ -132,44 +62,6 @@ namespace KarazhanHelpers return nullptr; } - Unit* GetFirstAliveUnitByEntry(PlayerbotAI* botAI, uint32 entry) - { - const GuidVector npcs = botAI->GetAiObjectContext()->GetValue("nearest hostile npcs")->Get(); - for (auto const& npcGuid : npcs) - { - Unit* unit = botAI->GetUnit(npcGuid); - if (unit && unit->IsAlive() && unit->GetEntry() == entry) - return unit; - } - - return nullptr; - } - - Unit* GetNearestPlayerInRadius(Player* bot, float radius) - { - Unit* nearestPlayer = nullptr; - float nearestDistance = radius; - - if (Group* group = bot->GetGroup()) - { - for (GroupReference* ref = group->GetFirstMember(); ref != nullptr; ref = ref->next()) - { - Player* member = ref->GetSource(); - if (!member || !member->IsAlive() || member == bot) - continue; - - float distance = bot->GetExactDist2d(member); - if (distance < nearestDistance) - { - nearestDistance = distance; - nearestPlayer = member; - } - } - } - - return nearestPlayer; - } - bool IsFlameWreathActive(PlayerbotAI* botAI, Player* bot) { Unit* aran = botAI->GetAiObjectContext()->GetValue("find target", "shade of aran")->Get(); diff --git a/src/Ai/Raid/Karazhan/Util/RaidKarazhanHelpers.h b/src/Ai/Raid/Karazhan/Util/RaidKarazhanHelpers.h index 394693b2e4..ad7e8c3d5f 100644 --- a/src/Ai/Raid/Karazhan/Util/RaidKarazhanHelpers.h +++ b/src/Ai/Raid/Karazhan/Util/RaidKarazhanHelpers.h @@ -61,6 +61,11 @@ namespace KarazhanHelpers NPC_ATTUMEN_THE_HUNTSMAN = 15550, NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED = 16152, + // Terestian Illhoof + NPC_TERESTIAN_ILLHOOF = 15688, + NPC_DEMON_CHAINS = 17248, + NPC_KILREK = 17229, + // Shade of Aran NPC_CONJURED_ELEMENTAL = 17167, @@ -74,8 +79,8 @@ namespace KarazhanHelpers NPC_NETHERSPITE_INFERNAL = 17646, }; - const uint32 KARAZHAN_MAP_ID = 532; - const float NIGHTBANE_FLIGHT_Z = 95.0f; + constexpr uint32 KARAZHAN_MAP_ID = 532; + constexpr float NIGHTBANE_FLIGHT_Z = 95.0f; // Attumen the Huntsman extern std::unordered_map attumenDpsWaitTimer; @@ -105,17 +110,7 @@ namespace KarazhanHelpers extern const Position NIGHTBANE_FLIGHT_STACK_POSITION; extern const Position NIGHTBANE_RAIN_OF_BONES_POSITION; - void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId); - void MarkTargetWithSkull(Player* bot, Unit* target); - void MarkTargetWithSquare(Player* bot, Unit* target); - void MarkTargetWithStar(Player* bot, Unit* target); - void MarkTargetWithCircle(Player* bot, Unit* target); - void MarkTargetWithMoon(Player* bot, Unit* target); - void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target); - bool IsInstanceTimerManager(PlayerbotAI* botAI, Player* bot); Unit* GetFirstAliveUnit(const std::vector& units); - Unit* GetFirstAliveUnitByEntry(PlayerbotAI* botAI, uint32 entry); - Unit* GetNearestPlayerInRadius(Player* bot, float radius); bool IsFlameWreathActive(PlayerbotAI* botAI, Player* bot); std::vector GetRedBlockers(PlayerbotAI* botAI, Player* bot); std::vector GetBlueBlockers(PlayerbotAI* botAI, Player* bot); diff --git a/src/Ai/Raid/Magtheridon/Action/RaidMagtheridonActions.cpp b/src/Ai/Raid/Magtheridon/Action/RaidMagtheridonActions.cpp index 69fc862442..dab7efab97 100644 --- a/src/Ai/Raid/Magtheridon/Action/RaidMagtheridonActions.cpp +++ b/src/Ai/Raid/Magtheridon/Action/RaidMagtheridonActions.cpp @@ -4,6 +4,7 @@ #include "ObjectAccessor.h" #include "ObjectGuid.h" #include "Playerbots.h" +#include "RaidBossHelpers.h" using namespace MagtheridonHelpers; @@ -14,46 +15,45 @@ bool MagtheridonMainTankAttackFirstThreeChannelersAction::Execute(Event event) return false; Creature* channelerSquare = GetChanneler(bot, SOUTH_CHANNELER); - if (channelerSquare && channelerSquare->IsAlive()) + if (channelerSquare) MarkTargetWithSquare(bot, channelerSquare); Creature* channelerStar = GetChanneler(bot, WEST_CHANNELER); - if (channelerStar && channelerStar->IsAlive()) + if (channelerStar) MarkTargetWithStar(bot, channelerStar); Creature* channelerCircle = GetChanneler(bot, EAST_CHANNELER); - if (channelerCircle && channelerCircle->IsAlive()) + if (channelerCircle) MarkTargetWithCircle(bot, channelerCircle); // After first three channelers are dead, wait for Magtheridon to activate - if ((!channelerSquare || !channelerSquare->IsAlive()) && - (!channelerStar || !channelerStar->IsAlive()) && - (!channelerCircle || !channelerCircle->IsAlive())) + if (!channelerSquare && !channelerStar && !channelerCircle) { - const Location& position = MagtheridonsLairLocations::WaitingForMagtheridonPosition; - if (!bot->IsWithinDist2d(position.x, position.y, 2.0f)) + const Position& position = WAITING_FOR_MAGTHERIDON_POSITION; + if (!bot->IsWithinDist2d(position.GetPositionX(), position.GetPositionY(), 2.0f)) { - return MoveTo(bot->GetMapId(), position.x, position.y, position.z, false, false, false, false, + return MoveTo(MAGTHERIDON_MAP_ID, position.GetPositionX(), position.GetPositionY(), + position.GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); } - bot->SetFacingTo(position.orientation); + bot->SetFacingTo(position.GetOrientation()); return true; } Creature* currentTarget = nullptr; std::string rtiName; - if (channelerSquare && channelerSquare->IsAlive()) + if (channelerSquare) { currentTarget = channelerSquare; rtiName = "square"; } - else if (channelerStar && channelerStar->IsAlive()) + else if (channelerStar) { currentTarget = channelerStar; rtiName = "star"; } - else if (channelerCircle && channelerCircle->IsAlive()) + else if (channelerCircle) { currentTarget = channelerCircle; rtiName = "circle"; @@ -70,7 +70,7 @@ bool MagtheridonMainTankAttackFirstThreeChannelersAction::Execute(Event event) bool MagtheridonFirstAssistTankAttackNWChannelerAction::Execute(Event event) { Creature* channelerDiamond = GetChanneler(bot, NORTHWEST_CHANNELER); - if (!channelerDiamond || !channelerDiamond->IsAlive()) + if (!channelerDiamond) return false; MarkTargetWithDiamond(bot, channelerDiamond); @@ -81,18 +81,18 @@ bool MagtheridonFirstAssistTankAttackNWChannelerAction::Execute(Event event) if (channelerDiamond->GetVictim() == bot) { - const Location& position = MagtheridonsLairLocations::NWChannelerTankPosition; + const Position& position = NW_CHANNELER_TANK_POSITION; const float maxDistance = 3.0f; + float distanceToPosition = bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()); - if (bot->GetExactDist2d(position.x, position.y) > maxDistance) + if (distanceToPosition > maxDistance) { - float dX = position.x - bot->GetPositionX(); - float dY = position.y - bot->GetPositionY(); - float dist = sqrt(dX * dX + dY * dY); - float moveX = bot->GetPositionX() + (dX / dist) * maxDistance; - float moveY = bot->GetPositionY() + (dY / dist) * maxDistance; + float dX = position.GetPositionX() - bot->GetPositionX(); + float dY = position.GetPositionY() - bot->GetPositionY(); + float moveX = bot->GetPositionX() + (dX / distanceToPosition) * maxDistance; + float moveY = bot->GetPositionY() + (dY / distanceToPosition) * maxDistance; - return MoveTo(bot->GetMapId(), moveX, moveY, position.z, false, false, false, false, + return MoveTo(MAGTHERIDON_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); } } @@ -103,7 +103,7 @@ bool MagtheridonFirstAssistTankAttackNWChannelerAction::Execute(Event event) bool MagtheridonSecondAssistTankAttackNEChannelerAction::Execute(Event event) { Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER); - if (!channelerTriangle || !channelerTriangle->IsAlive()) + if (!channelerTriangle) return false; MarkTargetWithTriangle(bot, channelerTriangle); @@ -114,18 +114,18 @@ bool MagtheridonSecondAssistTankAttackNEChannelerAction::Execute(Event event) if (channelerTriangle->GetVictim() == bot) { - const Location& position = MagtheridonsLairLocations::NEChannelerTankPosition; + const Position& position = NE_CHANNELER_TANK_POSITION; const float maxDistance = 3.0f; + float distanceToPosition = bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()); - if (bot->GetExactDist2d(position.x, position.y) > maxDistance) + if (distanceToPosition > maxDistance) { - float dX = position.x - bot->GetPositionX(); - float dY = position.y - bot->GetPositionY(); - float dist = sqrt(dX * dX + dY * dY); - float moveX = bot->GetPositionX() + (dX / dist) * maxDistance; - float moveY = bot->GetPositionY() + (dY / dist) * maxDistance; + float dX = position.GetPositionX() - bot->GetPositionX(); + float dY = position.GetPositionY() - bot->GetPositionY(); + float moveX = bot->GetPositionX() + (dX / distanceToPosition) * maxDistance; + float moveY = bot->GetPositionY() + (dY / distanceToPosition) * maxDistance; - return MoveTo(bot->GetMapId(), moveX, moveY, position.z, false, false, false, false, + return MoveTo(MAGTHERIDON_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); } } @@ -175,7 +175,7 @@ bool MagtheridonMisdirectHellfireChannelers::Execute(Event event) switch (hunterIndex) { case 0: - if (mainTank && channelerStar && channelerStar->IsAlive() && + if (mainTank && channelerStar && channelerStar->GetVictim() != mainTank) { if (botAI->CanCastSpell("misdirection", mainTank)) @@ -190,7 +190,7 @@ bool MagtheridonMisdirectHellfireChannelers::Execute(Event event) break; case 1: - if (mainTank && channelerCircle && channelerCircle->IsAlive() && + if (mainTank && channelerCircle && channelerCircle->GetVictim() != mainTank) { if (botAI->CanCastSpell("misdirection", mainTank)) @@ -215,90 +215,69 @@ bool MagtheridonAssignDPSPriorityAction::Execute(Event event) { // Listed in order of priority Creature* channelerSquare = GetChanneler(bot, SOUTH_CHANNELER); - if (channelerSquare && channelerSquare->IsAlive()) + if (channelerSquare) { SetRtiTarget(botAI, "square", channelerSquare); if (bot->GetTarget() != channelerSquare->GetGUID()) - { - bot->SetSelection(channelerSquare->GetGUID()); return Attack(channelerSquare); - } return false; } Creature* channelerStar = GetChanneler(bot, WEST_CHANNELER); - if (channelerStar && channelerStar->IsAlive()) + if (channelerStar) { SetRtiTarget(botAI, "star", channelerStar); if (bot->GetTarget() != channelerStar->GetGUID()) - { - bot->SetSelection(channelerStar->GetGUID()); return Attack(channelerStar); - } return false; } Creature* channelerCircle = GetChanneler(bot, EAST_CHANNELER); - if (channelerCircle && channelerCircle->IsAlive()) + if (channelerCircle) { SetRtiTarget(botAI, "circle", channelerCircle); if (bot->GetTarget() != channelerCircle->GetGUID()) - { - bot->SetSelection(channelerCircle->GetGUID()); return Attack(channelerCircle); - } return false; } Creature* channelerDiamond = GetChanneler(bot, NORTHWEST_CHANNELER); - if (channelerDiamond && channelerDiamond->IsAlive()) + if (channelerDiamond) { SetRtiTarget(botAI, "diamond", channelerDiamond); if (bot->GetTarget() != channelerDiamond->GetGUID()) - { - bot->SetSelection(channelerDiamond->GetGUID()); return Attack(channelerDiamond); - } return false; } Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER); - if (channelerTriangle && channelerTriangle->IsAlive()) + if (channelerTriangle) { SetRtiTarget(botAI, "triangle", channelerTriangle); if (bot->GetTarget() != channelerTriangle->GetGUID()) - { - bot->SetSelection(channelerTriangle->GetGUID()); return Attack(channelerTriangle); - } return false; } Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon"); if (magtheridon && !magtheridon->HasAura(SPELL_SHADOW_CAGE) && - (!channelerSquare || !channelerSquare->IsAlive()) && - (!channelerStar || !channelerStar->IsAlive()) && - (!channelerCircle || !channelerCircle->IsAlive()) && - (!channelerDiamond || !channelerDiamond->IsAlive()) && - (!channelerTriangle || !channelerTriangle->IsAlive())) + !channelerSquare && !channelerStar && !channelerCircle && + !channelerDiamond && !channelerTriangle) { SetRtiTarget(botAI, "cross", magtheridon); if (bot->GetTarget() != magtheridon->GetGUID()) - { - bot->SetSelection(magtheridon->GetGUID()); return Attack(magtheridon); - } } return false; @@ -343,15 +322,15 @@ bool MagtheridonWarlockCCBurningAbyssalAction::Execute(Event event) if (warlockIndex >= 0 && warlockIndex < abyssals.size()) { Unit* assignedAbyssal = abyssals[warlockIndex]; - if (!assignedAbyssal->HasAura(SPELL_BANISH) && botAI->CanCastSpell(SPELL_BANISH, assignedAbyssal, true)) + if (!botAI->HasAura("banish", assignedAbyssal) && botAI->CanCastSpell("banish", assignedAbyssal)) return botAI->CastSpell("banish", assignedAbyssal); } for (size_t i = warlocks.size(); i < abyssals.size(); ++i) { Unit* excessAbyssal = abyssals[i]; - if (!excessAbyssal->HasAura(SPELL_BANISH) && !excessAbyssal->HasAura(SPELL_FEAR) && - botAI->CanCastSpell(SPELL_FEAR, excessAbyssal, true)) + if (!botAI->HasAura("banish", excessAbyssal) && !botAI->HasAura("fear", excessAbyssal) && + botAI->CanCastSpell("fear", excessAbyssal)) return botAI->CastSpell("fear", excessAbyssal); } @@ -373,22 +352,20 @@ bool MagtheridonMainTankPositionBossAction::Execute(Event event) if (magtheridon->GetVictim() == bot) { - const Location& position = MagtheridonsLairLocations::MagtheridonTankPosition; + const Position& position = MAGTHERIDON_TANK_POSITION; const float maxDistance = 2.0f; + float distanceToPosition = bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()); - if (bot->GetExactDist2d(position.x, position.y) > maxDistance) + if (distanceToPosition > maxDistance) { - float dX = position.x - bot->GetPositionX(); - float dY = position.y - bot->GetPositionY(); - float dist = sqrt(dX * dX + dY * dY); - float moveX = bot->GetPositionX() + (dX / dist) * maxDistance; - float moveY = bot->GetPositionY() + (dY / dist) * maxDistance; + float dX = position.GetPositionX() - bot->GetPositionX(); + float dY = position.GetPositionY() - bot->GetPositionY(); + float moveX = bot->GetPositionX() + (dX / distanceToPosition) * maxDistance; + float moveY = bot->GetPositionY() + (dY / distanceToPosition) * maxDistance; - return MoveTo(bot->GetMapId(), moveX, moveY, position.z, false, false, false, false, + return MoveTo(MAGTHERIDON_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, true); } - - bot->SetFacingTo(position.orientation); } return false; @@ -440,13 +417,13 @@ bool MagtheridonSpreadRangedAction::Execute(Event event) } bool isHealer = botAI->IsHeal(bot); - const Location& center = isHealer - ? MagtheridonsLairLocations::HealerSpreadPosition - : MagtheridonsLairLocations::RangedSpreadPosition; + const Position& center = isHealer + ? HEALER_SPREAD_POSITION + : RANGED_SPREAD_POSITION; float maxSpreadRadius = isHealer ? 15.0f : 20.0f; - float centerX = center.x; - float centerY = center.y; - float centerZ = bot->GetPositionZ(); + float centerX = center.GetPositionX(); + float centerY = center.GetPositionY(); + float centerZ = center.GetPositionZ(); const float radiusBuffer = 3.0f; if (!initialPositions.count(bot->GetGUID())) @@ -479,7 +456,7 @@ bool MagtheridonSpreadRangedAction::Execute(Event event) bot->AttackStop(); bot->InterruptNonMeleeSpells(false); - return MoveTo(bot->GetMapId(), destX, destY, destZ, false, false, false, false, + return MoveTo(MAGTHERIDON_MAP_ID, destX, destY, destZ, false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); } hasReachedInitialPosition[bot->GetGUID()] = true; @@ -499,7 +476,7 @@ bool MagtheridonSpreadRangedAction::Execute(Event event) { bot->AttackStop(); bot->InterruptNonMeleeSpells(false); - return MoveTo(bot->GetMapId(), targetX, targetY, centerZ, false, false, false, false, + return MoveTo(MAGTHERIDON_MAP_ID, targetX, targetY, centerZ, false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); } } @@ -593,7 +570,7 @@ bool MagtheridonUseManticronCubeAction::HandleWaitingPhase(const CubeInfo& cubeI { bot->AttackStop(); bot->InterruptNonMeleeSpells(true); - return MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, false, + return MoveTo(MAGTHERIDON_MAP_ID, targetX, targetY, targetZ, false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); } } @@ -603,7 +580,7 @@ bool MagtheridonUseManticronCubeAction::HandleWaitingPhase(const CubeInfo& cubeI float fallbackY = cubeInfo.y + sin(angle) * safeWaitDistance; float fallbackZ = bot->GetPositionZ(); - return MoveTo(bot->GetMapId(), fallbackX, fallbackY, fallbackZ, false, false, false, false, + return MoveTo(MAGTHERIDON_MAP_ID, fallbackX, fallbackY, fallbackZ, false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); } @@ -638,7 +615,7 @@ bool MagtheridonUseManticronCubeAction::HandleCubeInteraction(const CubeInfo& cu bot->AttackStop(); bot->InterruptNonMeleeSpells(true); - return MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, false, + return MoveTo(MAGTHERIDON_MAP_ID, targetX, targetY, targetZ, false, false, false, false, MovementPriority::MOVEMENT_FORCED, true, false); } @@ -663,14 +640,14 @@ bool MagtheridonManageTimersAndAssignmentsAction::Execute(Event event) magtheridon->FindCurrentSpellBySpellId(SPELL_BLAST_NOVA); bool lastBlastNova = lastBlastNovaState[instanceId]; - if (lastBlastNova && !blastNovaActive && IsInstanceTimerManager(botAI, bot)) + if (lastBlastNova && !blastNovaActive && IsMechanicTrackerBot(botAI, bot, MAGTHERIDON_MAP_ID, nullptr)) blastNovaTimer[instanceId] = now; lastBlastNovaState[instanceId] = blastNovaActive; if (!magtheridon->HasAura(SPELL_SHADOW_CAGE)) { - if (IsInstanceTimerManager(botAI, bot)) + if (IsMechanicTrackerBot(botAI, bot, MAGTHERIDON_MAP_ID, nullptr)) { spreadWaitTimer.try_emplace(instanceId, now); blastNovaTimer.try_emplace(instanceId, now); @@ -679,11 +656,12 @@ bool MagtheridonManageTimersAndAssignmentsAction::Execute(Event event) } else { - MagtheridonSpreadRangedAction::initialPositions.clear(); - MagtheridonSpreadRangedAction::hasReachedInitialPosition.clear(); - botToCubeAssignment.clear(); + ObjectGuid guid = bot->GetGUID(); + MagtheridonSpreadRangedAction::initialPositions.erase(guid); + MagtheridonSpreadRangedAction::hasReachedInitialPosition.erase(guid); + botToCubeAssignment.erase(guid); - if (IsInstanceTimerManager(botAI, bot)) + if (IsMechanicTrackerBot(botAI, bot, MAGTHERIDON_MAP_ID, nullptr)) { spreadWaitTimer.erase(instanceId); blastNovaTimer.erase(instanceId); diff --git a/src/Ai/Raid/Magtheridon/Action/RaidMagtheridonActions.h b/src/Ai/Raid/Magtheridon/Action/RaidMagtheridonActions.h index 6c4ed84c22..d47d06459a 100644 --- a/src/Ai/Raid/Magtheridon/Action/RaidMagtheridonActions.h +++ b/src/Ai/Raid/Magtheridon/Action/RaidMagtheridonActions.h @@ -6,8 +6,6 @@ #include "AttackAction.h" #include "MovementActions.h" -using namespace MagtheridonHelpers; - class MagtheridonMainTankAttackFirstThreeChannelersAction : public AttackAction { public: @@ -85,8 +83,8 @@ class MagtheridonUseManticronCubeAction : public MovementAction private: bool HandleCubeRelease(Unit* magtheridon, GameObject* cube); bool ShouldActivateCubeLogic(Unit* magtheridon); - bool HandleWaitingPhase(const CubeInfo& cubeInfo); - bool HandleCubeInteraction(const CubeInfo& cubeInfo, GameObject* cube); + bool HandleWaitingPhase(const MagtheridonHelpers::CubeInfo& cubeInfo); + bool HandleCubeInteraction(const MagtheridonHelpers::CubeInfo& cubeInfo, GameObject* cube); }; class MagtheridonManageTimersAndAssignmentsAction : public Action diff --git a/src/Ai/Raid/Magtheridon/Multiplier/RaidMagtheridonMultipliers.cpp b/src/Ai/Raid/Magtheridon/Multiplier/RaidMagtheridonMultipliers.cpp index 9580fd923a..55aaf90e94 100644 --- a/src/Ai/Raid/Magtheridon/Multiplier/RaidMagtheridonMultipliers.cpp +++ b/src/Ai/Raid/Magtheridon/Multiplier/RaidMagtheridonMultipliers.cpp @@ -8,6 +8,7 @@ #include "GenericSpellActions.h" #include "Playerbots.h" #include "WarlockActions.h" +#include "WipeAction.h" using namespace MagtheridonHelpers; @@ -24,10 +25,10 @@ float MagtheridonUseManticronCubeMultiplier::GetValue(Action* action) auto it = botToCubeAssignment.find(bot->GetGUID()); if (it != botToCubeAssignment.end()) { - if (dynamic_cast(action)) + if (dynamic_cast(action)) return 1.0f; - - return 0.0f; + else if (!dynamic_cast(action)) + return 0.0f; } } @@ -41,28 +42,31 @@ float MagtheridonWaitToAttackMultiplier::GetValue(Action* action) if (!magtheridon || magtheridon->HasAura(SPELL_SHADOW_CAGE)) return 1.0f; + if (botAI->IsMainTank(bot)) + return 1.0f; + const uint8 dpsWaitSeconds = 6; auto it = dpsWaitTimer.find(magtheridon->GetMap()->GetInstanceId()); if (it == dpsWaitTimer.end() || (time(nullptr) - it->second) < dpsWaitSeconds) { - if (!botAI->IsMainTank(bot) && (dynamic_cast(action) || - (!botAI->IsHeal(bot) && dynamic_cast(action)))) + if (dynamic_cast(action) || + (!botAI->IsHeal(bot) && dynamic_cast(action))) return 0.0f; } return 1.0f; } -// No tank assist for offtanks during the channeler phase -// So they don't try to pull channelers from each other or the main tank float MagtheridonDisableOffTankAssistMultiplier::GetValue(Action* action) { Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon"); - Unit* channeler = AI_VALUE2(Unit*, "find target", "hellfire channeler"); if (!magtheridon) return 1.0f; + if (bot->GetVictim() == nullptr) + return 1.0f; + if ((botAI->IsAssistTankOfIndex(bot, 0) || botAI->IsAssistTankOfIndex(bot, 1)) && dynamic_cast(action)) return 0.0f; diff --git a/src/Ai/Raid/Magtheridon/Trigger/RaidMagtheridonTriggers.cpp b/src/Ai/Raid/Magtheridon/Trigger/RaidMagtheridonTriggers.cpp index 35442df6e0..43aa3361f3 100644 --- a/src/Ai/Raid/Magtheridon/Trigger/RaidMagtheridonTriggers.cpp +++ b/src/Ai/Raid/Magtheridon/Trigger/RaidMagtheridonTriggers.cpp @@ -18,7 +18,7 @@ bool MagtheridonNWChannelerEngagedByFirstAssistTankTrigger::IsActive() Creature* channelerDiamond = GetChanneler(bot, NORTHWEST_CHANNELER); return magtheridon && botAI->IsAssistTankOfIndex(bot, 0) && - channelerDiamond && channelerDiamond->IsAlive(); + channelerDiamond; } bool MagtheridonNEChannelerEngagedBySecondAssistTankTrigger::IsActive() @@ -27,7 +27,7 @@ bool MagtheridonNEChannelerEngagedBySecondAssistTankTrigger::IsActive() Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER); return magtheridon && botAI->IsAssistTankOfIndex(bot, 1) && - channelerTriangle && channelerTriangle->IsAlive(); + channelerTriangle; } bool MagtheridonPullingWestAndEastChannelersTrigger::IsActive() @@ -38,8 +38,7 @@ bool MagtheridonPullingWestAndEastChannelersTrigger::IsActive() Creature* channelerCircle = GetChanneler(bot, EAST_CHANNELER); return magtheridon && bot->getClass() == CLASS_HUNTER && - ((channelerStar && channelerStar->IsAlive()) || - (channelerCircle && channelerCircle->IsAlive())); + (channelerStar || channelerCircle); } bool MagtheridonDeterminingKillOrderTrigger::IsActive() @@ -51,12 +50,11 @@ bool MagtheridonDeterminingKillOrderTrigger::IsActive() Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER); if (!magtheridon || botAI->IsHeal(bot) || botAI->IsMainTank(bot) || - (botAI->IsAssistTankOfIndex(bot, 0) && channelerDiamond && channelerDiamond->IsAlive()) || - (botAI->IsAssistTankOfIndex(bot, 1) && channelerTriangle && channelerTriangle->IsAlive())) + (botAI->IsAssistTankOfIndex(bot, 0) && channelerDiamond) || + (botAI->IsAssistTankOfIndex(bot, 1) && channelerTriangle)) return false; - return (channeler && channeler->IsAlive()) || (magtheridon && - !magtheridon->HasAura(SPELL_SHADOW_CAGE)); + return channeler || (magtheridon && !magtheridon->HasAura(SPELL_SHADOW_CAGE)); } bool MagtheridonBurningAbyssalSpawnedTrigger::IsActive() @@ -84,10 +82,8 @@ bool MagtheridonBossEngagedByMainTankTrigger::IsActive() bool MagtheridonBossEngagedByRangedTrigger::IsActive() { Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon"); - Unit* channeler = AI_VALUE2(Unit*, "find target", "hellfire channeler"); - return magtheridon && botAI->IsRanged(bot) && - !(channeler && channeler->IsAlive()); + return magtheridon && !magtheridon->HasAura(SPELL_SHADOW_CAGE) && botAI->IsRanged(bot); } bool MagtheridonIncomingBlastNovaTrigger::IsActive() @@ -122,7 +118,5 @@ bool MagtheridonIncomingBlastNovaTrigger::IsActive() bool MagtheridonNeedToManageTimersAndAssignmentsTrigger::IsActive() { - Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon"); - - return magtheridon; + return AI_VALUE2(Unit*, "find target", "magtheridon"); } diff --git a/src/Ai/Raid/Magtheridon/Util/RaidMagtheridonHelpers.cpp b/src/Ai/Raid/Magtheridon/Util/RaidMagtheridonHelpers.cpp index dc88d2a192..a56d4b85bc 100644 --- a/src/Ai/Raid/Magtheridon/Util/RaidMagtheridonHelpers.cpp +++ b/src/Ai/Raid/Magtheridon/Util/RaidMagtheridonHelpers.cpp @@ -1,22 +1,18 @@ #include "RaidMagtheridonHelpers.h" #include "Creature.h" #include "GameObject.h" -#include "GroupReference.h" #include "Map.h" #include "ObjectGuid.h" #include "Playerbots.h" namespace MagtheridonHelpers { - namespace MagtheridonsLairLocations - { - const Location WaitingForMagtheridonPosition = { 1.359f, 2.048f, -0.406f, 3.135f }; - const Location MagtheridonTankPosition = { 22.827f, 2.105f, -0.406f, 3.135f }; - const Location NWChannelerTankPosition = { -11.764f, 30.818f, -0.411f, 0.0f }; - const Location NEChannelerTankPosition = { -12.490f, -26.211f, -0.411f, 0.0f }; - const Location RangedSpreadPosition = { -14.890f, 1.995f, -0.406f, 0.0f }; - const Location HealerSpreadPosition = { -2.265f, 1.874f, -0.404f, 0.0f }; - } + const Position WAITING_FOR_MAGTHERIDON_POSITION = { 1.359f, 2.048f, -0.406f, 3.135f }; + const Position MAGTHERIDON_TANK_POSITION = { 22.827f, 2.105f, -0.406f, 3.135f }; + const Position NW_CHANNELER_TANK_POSITION = { -11.764f, 30.818f, -0.411f, 0.0f }; + const Position NE_CHANNELER_TANK_POSITION = { -12.490f, -26.211f, -0.411f, 0.0f }; + const Position RANGED_SPREAD_POSITION = { -14.890f, 1.995f, -0.406f, 0.0f }; + const Position HEALER_SPREAD_POSITION = { -2.265f, 1.874f, -0.404f, 0.0f }; // Identify channelers by their database GUIDs Creature* GetChanneler(Player* bot, uint32 dbGuid) @@ -29,63 +25,11 @@ namespace MagtheridonHelpers if (it == map->GetCreatureBySpawnIdStore().end()) return nullptr; - return it->second; - } - - void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId) - { - Group* group = bot->GetGroup(); - if (!target || !group) - return; - - ObjectGuid currentGuid = group->GetTargetIcon(iconId); - if (currentGuid != target->GetGUID()) - group->SetTargetIcon(iconId, bot->GetGUID(), target->GetGUID()); - } - - void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target) - { - if (!target) - return; - - std::string currentRti = botAI->GetAiObjectContext()->GetValue("rti")->Get(); - Unit* currentTarget = botAI->GetAiObjectContext()->GetValue("rti target")->Get(); - - if (currentRti != rtiName || currentTarget != target) - { - botAI->GetAiObjectContext()->GetValue("rti")->Set(rtiName); - botAI->GetAiObjectContext()->GetValue("rti target")->Set(target); - } - } - - void MarkTargetWithSquare(Player* bot, Unit* target) - { - MarkTargetWithIcon(bot, target, RtiTargetValue::squareIndex); - } - - void MarkTargetWithStar(Player* bot, Unit* target) - { - MarkTargetWithIcon(bot, target, RtiTargetValue::starIndex); - } - - void MarkTargetWithCircle(Player* bot, Unit* target) - { - MarkTargetWithIcon(bot, target, RtiTargetValue::circleIndex); - } - - void MarkTargetWithDiamond(Player* bot, Unit* target) - { - MarkTargetWithIcon(bot, target, RtiTargetValue::diamondIndex); - } - - void MarkTargetWithTriangle(Player* bot, Unit* target) - { - MarkTargetWithIcon(bot, target, RtiTargetValue::triangleIndex); - } + Creature* channeler = it->second; + if (!channeler->IsAlive()) + return nullptr; - void MarkTargetWithCross(Player* bot, Unit* target) - { - MarkTargetWithIcon(bot, target, RtiTargetValue::crossIndex); + return channeler; } const std::vector MANTICRON_CUBE_DB_GUIDS = { 43157, 43158, 43159, 43160, 43161 }; @@ -208,19 +152,4 @@ namespace MagtheridonHelpers return true; } - - bool IsInstanceTimerManager(PlayerbotAI* botAI, Player* bot) - { - if (Group* group = bot->GetGroup()) - { - for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) - { - Player* member = ref->GetSource(); - if (member && member->IsAlive() && botAI->IsDps(member) && GET_PLAYERBOT_AI(member)) - return member == bot; - } - } - - return true; - } } diff --git a/src/Ai/Raid/Magtheridon/Util/RaidMagtheridonHelpers.h b/src/Ai/Raid/Magtheridon/Util/RaidMagtheridonHelpers.h index f9f514de3e..1335aff465 100644 --- a/src/Ai/Raid/Magtheridon/Util/RaidMagtheridonHelpers.h +++ b/src/Ai/Raid/Magtheridon/Util/RaidMagtheridonHelpers.h @@ -8,7 +8,6 @@ #include "Group.h" #include "ObjectGuid.h" #include "PlayerbotAI.h" -#include "RtiTargetValue.h" namespace MagtheridonHelpers { @@ -19,10 +18,6 @@ namespace MagtheridonHelpers SPELL_BLAST_NOVA = 30616, SPELL_SHADOW_GRASP = 30410, - // Warlock - SPELL_BANISH = 18647, - SPELL_FEAR = 6215, - // Hunter SPELL_MISDIRECTION = 35079, }; @@ -38,6 +33,7 @@ namespace MagtheridonHelpers GO_BLAZE = 181832, }; + constexpr uint32 MAGTHERIDON_MAP_ID = 544; constexpr uint32 SOUTH_CHANNELER = 90978; constexpr uint32 WEST_CHANNELER = 90979; constexpr uint32 NORTHWEST_CHANNELER = 90980; @@ -45,31 +41,14 @@ namespace MagtheridonHelpers constexpr uint32 NORTHEAST_CHANNELER = 90981; Creature* GetChanneler(Player* bot, uint32 dbGuid); - void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId); - void MarkTargetWithSquare(Player* bot, Unit* target); - void MarkTargetWithStar(Player* bot, Unit* target); - void MarkTargetWithCircle(Player* bot, Unit* target); - void MarkTargetWithDiamond(Player* bot, Unit* target); - void MarkTargetWithTriangle(Player* bot, Unit* target); - void MarkTargetWithCross(Player* bot, Unit* target); - void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target); bool IsSafeFromMagtheridonHazards(PlayerbotAI* botAI, Player* bot, float x, float y, float z); - bool IsInstanceTimerManager(PlayerbotAI* botAI, Player* bot); - struct Location - { - float x, y, z, orientation; - }; - - namespace MagtheridonsLairLocations - { - extern const Location WaitingForMagtheridonPosition; - extern const Location MagtheridonTankPosition; - extern const Location NWChannelerTankPosition; - extern const Location NEChannelerTankPosition; - extern const Location RangedSpreadPosition; - extern const Location HealerSpreadPosition; - } + extern const Position WAITING_FOR_MAGTHERIDON_POSITION; + extern const Position MAGTHERIDON_TANK_POSITION; + extern const Position NW_CHANNELER_TANK_POSITION; + extern const Position NE_CHANNELER_TANK_POSITION; + extern const Position RANGED_SPREAD_POSITION; + extern const Position HEALER_SPREAD_POSITION; struct CubeInfo { diff --git a/src/Ai/Raid/MoltenCore/RaidMcActionContext.h b/src/Ai/Raid/MoltenCore/RaidMcActionContext.h index 79a4a95a85..aaccb80d33 100644 --- a/src/Ai/Raid/MoltenCore/RaidMcActionContext.h +++ b/src/Ai/Raid/MoltenCore/RaidMcActionContext.h @@ -2,6 +2,7 @@ #define _PLAYERBOT_RAIDMCACTIONCONTEXT_H #include "Action.h" +#include "BossAuraActions.h" #include "NamedObjectContext.h" #include "RaidMcActions.h" diff --git a/src/Ai/Raid/MoltenCore/RaidMcTriggerContext.h b/src/Ai/Raid/MoltenCore/RaidMcTriggerContext.h index b74958919c..a62d851dc0 100644 --- a/src/Ai/Raid/MoltenCore/RaidMcTriggerContext.h +++ b/src/Ai/Raid/MoltenCore/RaidMcTriggerContext.h @@ -2,6 +2,7 @@ #define _PLAYERBOT_RAIDMCTRIGGERCONTEXT_H #include "AiObjectContext.h" +#include "BossAuraTriggers.h" #include "NamedObjectContext.h" #include "RaidMcTriggers.h" diff --git a/src/Ai/Raid/MoltenCore/RaidMcHelpers.h b/src/Ai/Raid/MoltenCore/Util/RaidMcHelpers.h similarity index 100% rename from src/Ai/Raid/MoltenCore/RaidMcHelpers.h rename to src/Ai/Raid/MoltenCore/Util/RaidMcHelpers.h diff --git a/src/Ai/Raid/RaidBossHelpers.cpp b/src/Ai/Raid/RaidBossHelpers.cpp new file mode 100644 index 0000000000..bcb48294f8 --- /dev/null +++ b/src/Ai/Raid/RaidBossHelpers.cpp @@ -0,0 +1,142 @@ +#include "RaidBossHelpers.h" +#include "Playerbots.h" +#include "RtiTargetValue.h" + +// Functions to mark targets with raid target icons +// Note that these functions do not allow the player to change the icon during the encounter +void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId) +{ + if (!target) + return; + + if (Group* group = bot->GetGroup()) + { + ObjectGuid currentGuid = group->GetTargetIcon(iconId); + if (currentGuid != target->GetGUID()) + group->SetTargetIcon(iconId, bot->GetGUID(), target->GetGUID()); + } +} + +void MarkTargetWithSkull(Player* bot, Unit* target) +{ + MarkTargetWithIcon(bot, target, RtiTargetValue::skullIndex); +} + +void MarkTargetWithSquare(Player* bot, Unit* target) +{ + MarkTargetWithIcon(bot, target, RtiTargetValue::squareIndex); +} + +void MarkTargetWithStar(Player* bot, Unit* target) +{ + MarkTargetWithIcon(bot, target, RtiTargetValue::starIndex); +} + +void MarkTargetWithCircle(Player* bot, Unit* target) +{ + MarkTargetWithIcon(bot, target, RtiTargetValue::circleIndex); +} + +void MarkTargetWithDiamond(Player* bot, Unit* target) +{ + MarkTargetWithIcon(bot, target, RtiTargetValue::diamondIndex); +} + +void MarkTargetWithTriangle(Player* bot, Unit* target) +{ + MarkTargetWithIcon(bot, target, RtiTargetValue::triangleIndex); +} + +void MarkTargetWithCross(Player* bot, Unit* target) +{ + MarkTargetWithIcon(bot, target, RtiTargetValue::crossIndex); +} + +void MarkTargetWithMoon(Player* bot, Unit* target) +{ + MarkTargetWithIcon(bot, target, RtiTargetValue::moonIndex); +} + +// For bots to set their raid target icon to the specified icon on the specified target +void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target) +{ + if (!target) + return; + + std::string currentRti = botAI->GetAiObjectContext()->GetValue("rti")->Get(); + Unit* currentTarget = botAI->GetAiObjectContext()->GetValue("rti target")->Get(); + + if (currentRti != rtiName || currentTarget != target) + { + botAI->GetAiObjectContext()->GetValue("rti")->Set(rtiName); + botAI->GetAiObjectContext()->GetValue("rti target")->Set(target); + } +} + +// Return the first alive DPS bot in the specified instance map, excluding any specified bot +// Intended for purposes of storing and erasing timers and trackers in associative containers +bool IsMechanicTrackerBot(PlayerbotAI* botAI, Player* bot, uint32 mapId, Player* exclude) +{ + if (Group* group = bot->GetGroup()) + { + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive() || member->GetMapId() != mapId || + !GET_PLAYERBOT_AI(member) || !botAI->IsDps(member)) + continue; + + if (member != exclude) + return member == bot; + } + } + + return false; +} + +// Return the first matching alive unit from a cell search of nearby npcs +// More responsive than "find target," but performance cost is much higher +// Re: using the third parameter (false by default), some units are never considered +// to be in combat (e.g., totems) +Unit* GetFirstAliveUnitByEntry(PlayerbotAI* botAI, uint32 entry, bool requireInCombat) +{ + auto const& npcs = + botAI->GetAiObjectContext()->GetValue("nearest npcs")->Get(); + for (auto const& npcGuid : npcs) + { + Unit* unit = botAI->GetUnit(npcGuid); + if (unit && unit->IsAlive() && unit->GetEntry() == entry) + { + if (!requireInCombat || unit->IsInCombat()) + return unit; + } + } + + return nullptr; +} + +// Return the nearest alive player (human or bot) within the specified radius +Unit* GetNearestPlayerInRadius(Player* bot, float radius) +{ + Unit* nearestPlayer = nullptr; + float nearestDistance = radius; + + if (Group* group = bot->GetGroup()) + { + for (GroupReference* ref = group->GetFirstMember(); ref != nullptr; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive() || member == bot) + continue; + + float distance = bot->GetExactDist2d(member); + if (distance < nearestDistance) + { + nearestDistance = distance; + nearestPlayer = member; + } + } + } + + return nearestPlayer; +} diff --git a/src/Ai/Raid/RaidBossHelpers.h b/src/Ai/Raid/RaidBossHelpers.h new file mode 100644 index 0000000000..15c60353e5 --- /dev/null +++ b/src/Ai/Raid/RaidBossHelpers.h @@ -0,0 +1,21 @@ +#ifndef _PLAYERBOT_RAIDBOSSHELPERS_H_ +#define _PLAYERBOT_RAIDBOSSHELPERS_H_ + +#include "AiObject.h" +#include "Unit.h" + +void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId); +void MarkTargetWithSkull(Player* bot, Unit* target); +void MarkTargetWithSquare(Player* bot, Unit* target); +void MarkTargetWithStar(Player* bot, Unit* target); +void MarkTargetWithCircle(Player* bot, Unit* target); +void MarkTargetWithDiamond(Player* bot, Unit* target); +void MarkTargetWithTriangle(Player* bot, Unit* target); +void MarkTargetWithCross(Player* bot, Unit* target); +void MarkTargetWithMoon(Player* bot, Unit* target); +void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target); +bool IsMechanicTrackerBot(PlayerbotAI* botAI, Player* bot, uint32 mapId, Player* exclude = nullptr); +Unit* GetFirstAliveUnitByEntry(PlayerbotAI* botAI, uint32 entry, bool requireInCombat = false); +Unit* GetNearestPlayerInRadius(Player* bot, float radius); + +#endif diff --git a/src/Ai/Raid/RaidStrategyContext.h b/src/Ai/Raid/RaidStrategyContext.h index 4f7a63c7ad..3c7971fb6f 100644 --- a/src/Ai/Raid/RaidStrategyContext.h +++ b/src/Ai/Raid/RaidStrategyContext.h @@ -29,7 +29,7 @@ class RaidStrategyContext : public NamedObjectContext creators["wotlk-os"] = &RaidStrategyContext::wotlk_os; creators["wotlk-eoe"] = &RaidStrategyContext::wotlk_eoe; creators["voa"] = &RaidStrategyContext::voa; - creators["uld"] = &RaidStrategyContext::uld; + creators["ulduar"] = &RaidStrategyContext::ulduar; creators["onyxia"] = &RaidStrategyContext::onyxia; creators["icc"] = &RaidStrategyContext::icc; } @@ -45,7 +45,7 @@ class RaidStrategyContext : public NamedObjectContext static Strategy* wotlk_eoe(PlayerbotAI* botAI) { return new RaidEoEStrategy(botAI); } static Strategy* voa(PlayerbotAI* botAI) { return new RaidVoAStrategy(botAI); } static Strategy* onyxia(PlayerbotAI* botAI) { return new RaidOnyxiaStrategy(botAI); } - static Strategy* uld(PlayerbotAI* botAI) { return new RaidUlduarStrategy(botAI); } + static Strategy* ulduar(PlayerbotAI* botAI) { return new RaidUlduarStrategy(botAI); } static Strategy* icc(PlayerbotAI* botAI) { return new RaidIccStrategy(botAI); } }; diff --git a/src/Ai/Raid/Ulduar/Action/RaidUlduarActions.cpp b/src/Ai/Raid/Ulduar/Action/RaidUlduarActions.cpp index b20425d224..f7eca38db1 100644 --- a/src/Ai/Raid/Ulduar/Action/RaidUlduarActions.cpp +++ b/src/Ai/Raid/Ulduar/Action/RaidUlduarActions.cpp @@ -11,7 +11,6 @@ #include "GameObject.h" #include "Group.h" #include "LastMovementValue.h" -#include "ObjectDefines.h" #include "ObjectGuid.h" #include "PlayerbotAI.h" #include "PlayerbotAIConfig.h" @@ -19,11 +18,9 @@ #include "Position.h" #include "RaidUlduarBossHelper.h" #include "RaidUlduarScripts.h" -#include "RaidUlduarStrategy.h" #include "RtiValue.h" #include "ScriptedCreature.h" #include "ServerFacade.h" -#include "SharedDefines.h" #include "Unit.h" #include "Vehicle.h" #include diff --git a/src/Ai/Raid/Ulduar/Multiplier/RaidUlduarMultipliers.cpp b/src/Ai/Raid/Ulduar/Multiplier/RaidUlduarMultipliers.cpp deleted file mode 100644 index 0a51ca407b..0000000000 --- a/src/Ai/Raid/Ulduar/Multiplier/RaidUlduarMultipliers.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "RaidUlduarMultipliers.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 "RaidUlduarActions.h" -#include "ReachTargetActions.h" -#include "RogueActions.h" -#include "ScriptedCreature.h" -#include "ShamanActions.h" -#include "UseMeetingStoneAction.h" -#include "WarriorActions.h" - -float FlameLeviathanMultiplier::GetValue(Action* action) -{ - // if (dynamic_cast(action)) - // return 0.0f; - return 1.0f; -} diff --git a/src/Ai/Raid/Ulduar/Multiplier/RaidUlduarMultipliers.h b/src/Ai/Raid/Ulduar/Multiplier/RaidUlduarMultipliers.h deleted file mode 100644 index 6c9a468fcd..0000000000 --- a/src/Ai/Raid/Ulduar/Multiplier/RaidUlduarMultipliers.h +++ /dev/null @@ -1,17 +0,0 @@ - -#ifndef _PLAYERRBOT_RAIDULDUARMULTIPLIERS_H_ -#define _PLAYERRBOT_RAIDULDUARMULTIPLIERS_H_ - -#include "Multiplier.h" -#include "Ai/Raid/Ulduar/RaidUlduarBossHelper.h" - -class FlameLeviathanMultiplier : public Multiplier -{ -public: - FlameLeviathanMultiplier(PlayerbotAI* ai) : Multiplier(ai, "flame leviathan") {} - -public: - virtual float GetValue(Action* action); -}; - -#endif diff --git a/src/Ai/Raid/Ulduar/RaidUlduarBossHelper.h b/src/Ai/Raid/Ulduar/RaidUlduarBossHelper.h deleted file mode 100644 index 592fbc80ea..0000000000 --- a/src/Ai/Raid/Ulduar/RaidUlduarBossHelper.h +++ /dev/null @@ -1,169 +0,0 @@ -#ifndef _PLAYERBOT_RAIDULDUARBOSSHELPER_H -#define _PLAYERBOT_RAIDULDUARBOSSHELPER_H - -#include -#include -#include -#include -#include -#include - -#include "AiObject.h" -#include "AiObjectContext.h" -#include "EventMap.h" -#include "Log.h" -#include "ObjectGuid.h" -#include "Player.h" -#include "PlayerbotAI.h" -#include "Playerbots.h" -#include "ScriptedCreature.h" -#include "SharedDefines.h" - -const uint32 ULDUAR_MAP_ID = 603; - -class RazorscaleBossHelper : public AiObject -{ -public: - // Enums and constants specific to Razorscale - enum RazorscaleUnits : uint32 - { - UNIT_RAZORSCALE = 33186, - UNIT_DARK_RUNE_SENTINEL = 33846, - UNIT_DARK_RUNE_WATCHER = 33453, - UNIT_DARK_RUNE_GUARDIAN = 33388, - UNIT_DEVOURING_FLAME = 34188, - }; - - enum RazorscaleGameObjects : uint32 - { - GO_RAZORSCALE_HARPOON_1 = 194519, - GO_RAZORSCALE_HARPOON_2 = 194541, - GO_RAZORSCALE_HARPOON_3 = 194542, - GO_RAZORSCALE_HARPOON_4 = 194543, - }; - - enum RazorscaleSpells : uint32 - { - SPELL_CHAIN_1 = 49679, - SPELL_CHAIN_2 = 49682, - SPELL_CHAIN_3 = 49683, - SPELL_CHAIN_4 = 49684, - SPELL_SENTINEL_WHIRLWIND = 63806, - SPELL_STUN_AURA = 62794, - SPELL_FUSEARMOR = 64771 - }; - - static constexpr uint32 FUSEARMOR_THRESHOLD = 2; - - // Constants for arena parameters - static constexpr float RAZORSCALE_FLYING_Z_THRESHOLD = 440.0f; - static constexpr float RAZORSCALE_ARENA_CENTER_X = 587.54f; - static constexpr float RAZORSCALE_ARENA_CENTER_Y = -175.04f; - static constexpr float RAZORSCALE_ARENA_RADIUS = 30.0f; - - // Harpoon cooldown (seconds) - static constexpr time_t HARPOON_COOLDOWN_DURATION = 5; - - // Structure for harpoon data - struct HarpoonData - { - uint32 gameObjectEntry; - uint32 chainSpellId; - }; - - explicit RazorscaleBossHelper(PlayerbotAI* botAI) - : AiObject(botAI), _boss(nullptr) {} - - bool UpdateBossAI(); - Unit* GetBoss() const; - - bool IsGroundPhase() const; - bool IsFlyingPhase() const; - - bool IsHarpoonFired(uint32 chainSpellId) const; - static bool IsHarpoonReady(GameObject* harpoonGO); - static void SetHarpoonOnCooldown(GameObject* harpoonGO); - GameObject* FindNearestHarpoon(float x, float y, float z) const; - - static const std::vector& GetHarpoonData(); - - void AssignRolesBasedOnHealth(); - bool AreRolesAssigned() const; - bool CanSwapRoles() const; - -private: - Unit* _boss; - - // A map to track the last role swap *per bot* by their GUID - static std::unordered_map _lastRoleSwapTime; - - // The cooldown that applies to every bot - static const std::time_t _roleSwapCooldown = 10; - - static std::unordered_map _harpoonCooldowns; -}; - -// 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 = _event_map->GetTimer(); -// 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; -// }; - -#endif diff --git a/src/Ai/Raid/Ulduar/Strategy/RaidUlduarStrategy.cpp b/src/Ai/Raid/Ulduar/Strategy/RaidUlduarStrategy.cpp index 3b9a426cc6..0a1b76a40f 100644 --- a/src/Ai/Raid/Ulduar/Strategy/RaidUlduarStrategy.cpp +++ b/src/Ai/Raid/Ulduar/Strategy/RaidUlduarStrategy.cpp @@ -1,7 +1,5 @@ #include "RaidUlduarStrategy.h" -#include "RaidUlduarMultipliers.h" - void RaidUlduarStrategy::InitTriggers(std::vector& triggers) { // @@ -316,8 +314,3 @@ void RaidUlduarStrategy::InitTriggers(std::vector& triggers) "yogg-saron phase 3 positioning trigger", { NextAction("yogg-saron phase 3 positioning action", ACTION_RAID) })); } - -void RaidUlduarStrategy::InitMultipliers(std::vector& multipliers) -{ - multipliers.push_back(new FlameLeviathanMultiplier(botAI)); -} diff --git a/src/Ai/Raid/Ulduar/Strategy/RaidUlduarStrategy.h b/src/Ai/Raid/Ulduar/Strategy/RaidUlduarStrategy.h index 81bb93c3a5..bb2feefe4a 100644 --- a/src/Ai/Raid/Ulduar/Strategy/RaidUlduarStrategy.h +++ b/src/Ai/Raid/Ulduar/Strategy/RaidUlduarStrategy.h @@ -3,16 +3,14 @@ #define _PLAYERBOT_RAIDULDUARSTRATEGY_H #include "AiObjectContext.h" -#include "Multiplier.h" #include "Strategy.h" class RaidUlduarStrategy : public Strategy { public: RaidUlduarStrategy(PlayerbotAI* ai) : Strategy(ai) {} - virtual std::string const getName() override { return "uld"; } + virtual std::string const getName() override { return "ulduar"; } virtual void InitTriggers(std::vector& triggers) override; - virtual void InitMultipliers(std::vector& multipliers) override; }; #endif diff --git a/src/Ai/Raid/Ulduar/Trigger/RaidUlduarTriggers.cpp b/src/Ai/Raid/Ulduar/Trigger/RaidUlduarTriggers.cpp index a4bc2cf9a2..f14a513118 100644 --- a/src/Ai/Raid/Ulduar/Trigger/RaidUlduarTriggers.cpp +++ b/src/Ai/Raid/Ulduar/Trigger/RaidUlduarTriggers.cpp @@ -1634,7 +1634,7 @@ bool VezaxShadowCrashTrigger::IsActive() return false; } - return botAI->HasAura(SPELL_SHADOW_CRASH, bot); + return botAI->HasAura(SPELL_VEZAX_SHADOW_CRASH, bot); } bool VezaxMarkOfTheFacelessTrigger::IsActive() diff --git a/src/Ai/Raid/Ulduar/Trigger/RaidUlduarTriggers.h b/src/Ai/Raid/Ulduar/Trigger/RaidUlduarTriggers.h index 129c3d4dba..7f8cb51a83 100644 --- a/src/Ai/Raid/Ulduar/Trigger/RaidUlduarTriggers.h +++ b/src/Ai/Raid/Ulduar/Trigger/RaidUlduarTriggers.h @@ -3,187 +3,9 @@ #include "EventMap.h" #include "GenericTriggers.h" -#include "PlayerbotAIConfig.h" #include "RaidUlduarBossHelper.h" #include "Trigger.h" -enum UlduarIDs -{ - // Iron Assembly - SPELL_LIGHTNING_TENDRILS_10_MAN = 61887, - SPELL_LIGHTNING_TENDRILS_25_MAN = 63486, - SPELL_OVERLOAD_10_MAN = 61869, - SPELL_OVERLOAD_25_MAN = 63481, - SPELL_OVERLOAD_10_MAN_2 = 63485, - SPELL_OVERLOAD_25_MAN_2 = 61886, - SPELL_RUNE_OF_POWER = 64320, - - // Kologarn - NPC_RIGHT_ARM = 32934, - NPC_RUBBLE = 33768, - SPELL_CRUNCH_ARMOR = 64002, - - SPELL_FOCUSED_EYEBEAM_10_2 = 63346, - SPELL_FOCUSED_EYEBEAM_10 = 63347, - SPELL_FOCUSED_EYEBEAM_25_2 = 63976, - SPELL_FOCUSED_EYEBEAM_25 = 63977, - - // Hodir - NPC_SNOWPACKED_ICICLE = 33174, - NPC_TOASTY_FIRE = 33342, - SPELL_FLASH_FREEZE = 61968, - SPELL_BITING_COLD_PLAYER_AURA = 62039, - - // Freya - NPC_SNAPLASHER = 32916, - NPC_STORM_LASHER = 32919, - NPC_DETONATING_LASHER = 32918, - NPC_ANCIENT_WATER_SPIRIT = 33202, - NPC_ANCIENT_CONSERVATOR = 33203, - NPC_HEALTHY_SPORE = 33215, - NPC_EONARS_GIFT = 33228, - GOBJECT_NATURE_BOMB = 194902, - - // Thorim - NPC_DARK_RUNE_ACOLYTE_I = 32886, - NPC_CAPTURED_MERCENARY_SOLDIER_ALLY = 32885, - NPC_CAPTURED_MERCENARY_SOLDIER_HORDE = 32883, - NPC_CAPTURED_MERCENARY_CAPTAIN_ALLY = 32908, - NPC_CAPTURED_MERCENARY_CAPTAIN_HORDE = 32907, - NPC_JORMUNGAR_BEHEMOT = 32882, - NPC_DARK_RUNE_WARBRINGER = 32877, - NPC_DARK_RUNE_EVOKER = 32878, - NPC_DARK_RUNE_CHAMPION = 32876, - NPC_DARK_RUNE_COMMONER = 32904, - NPC_IRON_RING_GUARD = 32874, - NPC_RUNIC_COLOSSUS = 32872, - NPC_ANCIENT_RUNE_GIANT = 32873, - NPC_DARK_RUNE_ACOLYTE_G = 33110, - NPC_IRON_HONOR_GUARD = 32875, - SPELL_UNBALANCING_STRIKE = 62130, - - // Mimiron - NPC_LEVIATHAN_MKII = 33432, - NPC_VX001 = 33651, - NPC_AERIAL_COMMAND_UNIT = 33670, - NPC_BOMB_BOT = 33836, - NPC_ROCKET_STRIKE_N = 34047, - NPC_ASSAULT_BOT = 34057, - NPC_PROXIMITY_MINE = 34362, - SPELL_P3WX2_LASER_BARRAGE_1 = 63293, - SPELL_P3WX2_LASER_BARRAGE_2 = 63297, - SPELL_SPINNING_UP = 63414, - SPELL_SHOCK_BLAST = 63631, - SPELL_P3WX2_LASER_BARRAGE_3 = 64042, - SPELL_P3WX2_LASER_BARRAGE_AURA_1 = 63274, - SPELL_P3WX2_LASER_BARRAGE_AURA_2 = 63300, - - // General Vezax - SPELL_MARK_OF_THE_FACELESS = 63276, - SPELL_SHADOW_CRASH = 63277, - - // Yogg-Saron - ACTION_ILLUSION_DRAGONS = 1, - ACTION_ILLUSION_ICECROWN = 2, - ACTION_ILLUSION_STORMWIND = 3, - NPC_GUARDIAN_OF_YS = 33136, - NPC_YOGG_SARON = 33288, - NPC_OMINOUS_CLOUD = 33292, - NPC_RUBY_CONSORT = 33716, - NPC_AZURE_CONSORT = 33717, - NPC_BRONZE_CONSORT = 33718, - NPC_EMERALD_CONSORT = 33719, - NPC_OBSIDIAN_CONSORT = 33720, - NPC_ALEXTRASZA = 33536, - NPC_MALYGOS_ILLUSION = 33535, - NPC_NELTHARION = 33523, - NPC_YSERA = 33495, - GO_DRAGON_SOUL = 194462, - NPC_SARA_PHASE_1 = 33134, - NPC_LICH_KING_ILLUSION = 33441, - NPC_IMMOLATED_CHAMPION = 33442, - NPC_SUIT_OF_ARMOR = 33433, - NPC_GARONA = 33436, - NPC_KING_LLANE = 33437, - NPC_DEATHSWORN_ZEALOT = 33567, - NPC_INFLUENCE_TENTACLE = 33943, - NPC_DEATH_ORB = 33882, - NPC_BRAIN = 33890, - NPC_CRUSHER_TENTACLE = 33966, - NPC_CONSTRICTOR_TENTACLE = 33983, - NPC_CORRUPTOR_TENTACLE = 33985, - NPC_IMMORTAL_GUARDIAN = 33988, - NPC_LAUGHING_SKULL = 33990, - NPC_SANITY_WELL = 33991, - NPC_DESCEND_INTO_MADNESS = 34072, - NPC_MARKED_IMMORTAL_GUARDIAN = 36064, - SPELL_SANITY = 63050, - SPELL_BRAIN_LINK = 63802, - SPELL_MALADY_OF_THE_MIND = 63830, - SPELL_SHADOW_BARRIER = 63894, - SPELL_TELEPORT_TO_CHAMBER = 63997, - SPELL_TELEPORT_TO_ICECROWN = 63998, - SPELL_TELEPORT_TO_STORMWIND = 63989, - SPELL_TELEPORT_BACK = 63992, - SPELL_CANCEL_ILLUSION_AURA = 63993, - SPELL_INDUCE_MADNESS = 64059, - SPELL_LUNATIC_GAZE_YS = 64163, - GO_FLEE_TO_THE_SURFACE_PORTAL = 194625, - - // Buffs - SPELL_FROST_TRAP = 13809 -}; - -const float ULDUAR_KOLOGARN_AXIS_Z_PATHING_ISSUE_DETECT = 420.0f; -const float ULDUAR_KOLOGARN_EYEBEAM_RADIUS = 3.0f; -const float ULDUAR_THORIM_AXIS_Z_FLOOR_THRESHOLD = 429.6094f; -const float ULDUAR_THORIM_AXIS_Z_PATHING_ISSUE_DETECT = 410.0f; -const float ULDUAR_AURIAYA_AXIS_Z_PATHING_ISSUE_DETECT = 410.0f; -const float ULDUAR_YOGG_SARON_BOSS_ROOM_AXIS_Z_PATHING_ISSUE_DETECT = 300.0f; -const float ULDUAR_YOGG_SARON_BRAIN_ROOM_AXIS_Z_PATHING_ISSUE_DETECT = 200.0f; -const float ULDUAR_YOGG_SARON_STORMWIND_KEEPER_RADIUS = 150.0f; -const float ULDUAR_YOGG_SARON_ICECROWN_CITADEL_RADIUS = 150.0f; -const float ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_RADIUS = 150.0f; -const float ULDUAR_YOGG_SARON_BRAIN_ROOM_RADIUS = 50.0f; - -const Position ULDUAR_THORIM_NEAR_ARENA_CENTER = Position(2134.9854f, -263.11853f, 419.8465f); -const Position ULDUAR_THORIM_NEAR_ENTRANCE_POSITION = Position(2172.4355f, -258.27957f, 418.47162f); -const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_6_YARDS_1 = Position(2237.6187f, -265.08844f, 412.17548f); -const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_6_YARDS_2 = Position(2237.2498f, -275.81122f, 412.17548f); -const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_5_YARDS_1 = Position(2236.895f, -294.62448f, 412.1348f); -const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_1 = Position(2242.1162f, -310.15308f, 412.1348f); -const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_2 = Position(2242.018f, -318.66003f, 412.1348f); -const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_3 = Position(2242.1904f, -329.0533f, 412.1348f); -const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_6_YARDS_1 = Position(2219.5417f, -264.77167f, 412.17548f); -const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_6_YARDS_2 = Position(2217.446f, -275.85248f, 412.17548f); -const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_5_YARDS_1 = Position(2217.8877f, -295.01193f, 412.13434f); -const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_1 = Position(2212.193f, -307.44992f, 412.1348f); -const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_2 = Position(2212.1353f, -318.20795f, 412.1348f); -const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_3 = Position(2212.1956f, -328.0144f, 412.1348f); -const Position ULDUAR_THORIM_JUMP_END_POINT = Position(2137.8818f, -278.18942f, 419.66653f); -const Position ULDUAR_THORIM_PHASE2_TANK_SPOT = Position(2134.8572f, -287.0291f, 419.4935f); -const Position ULDUAR_THORIM_PHASE2_RANGE1_SPOT = Position(2112.8752f, -267.69305f, 419.52814f); -const Position ULDUAR_THORIM_PHASE2_RANGE2_SPOT = Position(2134.1296f, -257.3316f, 419.8462f); -const Position ULDUAR_THORIM_PHASE2_RANGE3_SPOT = Position(2156.798f, -267.57434f, 419.52722f); -const Position ULDUAR_MIMIRON_PHASE2_SIDE1RANGE_SPOT = Position(2753.708f, 2583.9617f, 364.31357f); -const Position ULDUAR_MIMIRON_PHASE2_SIDE1MELEE_SPOT = Position(2746.9792f, 2573.6716f, 364.31357f); -const Position ULDUAR_MIMIRON_PHASE2_SIDE2RANGE_SPOT = Position(2727.7224f, 2569.527f, 364.31357f); -const Position ULDUAR_MIMIRON_PHASE2_SIDE2MELEE_SPOT = Position(2739.4746f, 2569.4106f, 364.31357f); -const Position ULDUAR_MIMIRON_PHASE2_SIDE3RANGE_SPOT = Position(2754.1294f, 2553.9954f, 364.31357f); -const Position ULDUAR_MIMIRON_PHASE2_SIDE3MELEE_SPOT = Position(2746.8513f, 2565.4263f, 364.31357f); -const Position ULDUAR_MIMIRON_PHASE4_TANK_SPOT = Position(2744.5754f, 2570.8657f, 364.3138f); -const Position ULDUAR_VEZAX_MARK_OF_THE_FACELESS_SPOT = Position(1913.6501f, 122.93989f, 342.38083f); -const Position ULDUAR_YOGG_SARON_MIDDLE = Position(1980.28f, -25.5868f, 329.397f); -const Position ULDUAR_YOGG_SARON_STORMWIND_KEEPER_MIDDLE = Position(1927.1511f, 68.507256f, 242.37657f); -const Position ULDUAR_YOGG_SARON_ICECROWN_CITADEL_MIDDLE = Position(1925.6553f, -121.59296f, 239.98965f); -const Position ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_MIDDLE = Position(2104.5667f, -25.509348f, 242.64679f); -const Position ULDUAR_YOGG_SARON_BRAIN_ROOM_MIDDLE = Position(1980.1971f, -27.854689f, 236.06789f); -const Position ULDUAR_YOGG_SARON_STORMWIND_KEEPER_ENTRANCE = Position(1954.06f, 21.66f, 239.71f); -const Position ULDUAR_YOGG_SARON_ICECROWN_CITADEL_ENTRANCE = Position(1950.11f, -79.284f, 239.98982f); -const Position ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_ENTRANCE = Position(2048.63f, -25.5f, 239.72f); -const Position ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT = Position(1998.5377f, -22.90317f, 324.8895f); -const Position ULDUAR_YOGG_SARON_PHASE_3_RANGED_SPOT = Position(2018.7628f, -18.896868f, 327.07245f); - // // Flame Levi // diff --git a/src/Ai/Raid/Ulduar/RaidUlduarBossHelper.cpp b/src/Ai/Raid/Ulduar/Util/RaidUlduarBossHelper.cpp similarity index 63% rename from src/Ai/Raid/Ulduar/RaidUlduarBossHelper.cpp rename to src/Ai/Raid/Ulduar/Util/RaidUlduarBossHelper.cpp index 72333a079b..fd6711cf0f 100644 --- a/src/Ai/Raid/Ulduar/RaidUlduarBossHelper.cpp +++ b/src/Ai/Raid/Ulduar/Util/RaidUlduarBossHelper.cpp @@ -1,4 +1,3 @@ -#include "ChatHelper.h" #include "RaidUlduarBossHelper.h" #include "ObjectAccessor.h" #include "GameObject.h" @@ -9,6 +8,44 @@ #include "Playerbots.h" #include "World.h" +const Position ULDUAR_THORIM_NEAR_ARENA_CENTER = Position(2134.9854f, -263.11853f, 419.8465f); +const Position ULDUAR_THORIM_NEAR_ENTRANCE_POSITION = Position(2172.4355f, -258.27957f, 418.47162f); +const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_6_YARDS_1 = Position(2237.6187f, -265.08844f, 412.17548f); +const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_6_YARDS_2 = Position(2237.2498f, -275.81122f, 412.17548f); +const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_5_YARDS_1 = Position(2236.895f, -294.62448f, 412.1348f); +const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_1 = Position(2242.1162f, -310.15308f, 412.1348f); +const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_2 = Position(2242.018f, -318.66003f, 412.1348f); +const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_3 = Position(2242.1904f, -329.0533f, 412.1348f); +const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_6_YARDS_1 = Position(2219.5417f, -264.77167f, 412.17548f); +const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_6_YARDS_2 = Position(2217.446f, -275.85248f, 412.17548f); +const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_5_YARDS_1 = Position(2217.8877f, -295.01193f, 412.13434f); +const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_1 = Position(2212.193f, -307.44992f, 412.1348f); +const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_2 = Position(2212.1353f, -318.20795f, 412.1348f); +const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_3 = Position(2212.1956f, -328.0144f, 412.1348f); +const Position ULDUAR_THORIM_JUMP_END_POINT = Position(2137.8818f, -278.18942f, 419.66653f); +const Position ULDUAR_THORIM_PHASE2_TANK_SPOT = Position(2134.8572f, -287.0291f, 419.4935f); +const Position ULDUAR_THORIM_PHASE2_RANGE1_SPOT = Position(2112.8752f, -267.69305f, 419.52814f); +const Position ULDUAR_THORIM_PHASE2_RANGE2_SPOT = Position(2134.1296f, -257.3316f, 419.8462f); +const Position ULDUAR_THORIM_PHASE2_RANGE3_SPOT = Position(2156.798f, -267.57434f, 419.52722f); +const Position ULDUAR_MIMIRON_PHASE2_SIDE1RANGE_SPOT = Position(2753.708f, 2583.9617f, 364.31357f); +const Position ULDUAR_MIMIRON_PHASE2_SIDE1MELEE_SPOT = Position(2746.9792f, 2573.6716f, 364.31357f); +const Position ULDUAR_MIMIRON_PHASE2_SIDE2RANGE_SPOT = Position(2727.7224f, 2569.527f, 364.31357f); +const Position ULDUAR_MIMIRON_PHASE2_SIDE2MELEE_SPOT = Position(2739.4746f, 2569.4106f, 364.31357f); +const Position ULDUAR_MIMIRON_PHASE2_SIDE3RANGE_SPOT = Position(2754.1294f, 2553.9954f, 364.31357f); +const Position ULDUAR_MIMIRON_PHASE2_SIDE3MELEE_SPOT = Position(2746.8513f, 2565.4263f, 364.31357f); +const Position ULDUAR_MIMIRON_PHASE4_TANK_SPOT = Position(2744.5754f, 2570.8657f, 364.3138f); +const Position ULDUAR_VEZAX_MARK_OF_THE_FACELESS_SPOT = Position(1913.6501f, 122.93989f, 342.38083f); +const Position ULDUAR_YOGG_SARON_MIDDLE = Position(1980.28f, -25.5868f, 329.397f); +const Position ULDUAR_YOGG_SARON_STORMWIND_KEEPER_MIDDLE = Position(1927.1511f, 68.507256f, 242.37657f); +const Position ULDUAR_YOGG_SARON_ICECROWN_CITADEL_MIDDLE = Position(1925.6553f, -121.59296f, 239.98965f); +const Position ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_MIDDLE = Position(2104.5667f, -25.509348f, 242.64679f); +const Position ULDUAR_YOGG_SARON_BRAIN_ROOM_MIDDLE = Position(1980.1971f, -27.854689f, 236.06789f); +const Position ULDUAR_YOGG_SARON_STORMWIND_KEEPER_ENTRANCE = Position(1954.06f, 21.66f, 239.71f); +const Position ULDUAR_YOGG_SARON_ICECROWN_CITADEL_ENTRANCE = Position(1950.11f, -79.284f, 239.98982f); +const Position ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_ENTRANCE = Position(2048.63f, -25.5f, 239.72f); +const Position ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT = Position(1998.5377f, -22.90317f, 324.8895f); +const Position ULDUAR_YOGG_SARON_PHASE_3_RANGED_SPOT = Position(2018.7628f, -18.896868f, 327.07245f); + // Prevent harpoon spam std::unordered_map RazorscaleBossHelper::_harpoonCooldowns; // Prevent role assignment spam diff --git a/src/Ai/Raid/Ulduar/Util/RaidUlduarBossHelper.h b/src/Ai/Raid/Ulduar/Util/RaidUlduarBossHelper.h new file mode 100644 index 0000000000..b3d49ff58c --- /dev/null +++ b/src/Ai/Raid/Ulduar/Util/RaidUlduarBossHelper.h @@ -0,0 +1,341 @@ +#ifndef _PLAYERBOT_RAIDULDUARBOSSHELPER_H +#define _PLAYERBOT_RAIDULDUARBOSSHELPER_H + +#include +#include +#include + +#include "AiObject.h" +#include "AiObjectContext.h" +#include "EventMap.h" +#include "ObjectGuid.h" +#include "Player.h" +#include "PlayerbotAI.h" +#include "Playerbots.h" +#include "ScriptedCreature.h" + +constexpr uint32 ULDUAR_MAP_ID = 603; + +enum UlduarIDs +{ + // Iron Assembly + SPELL_LIGHTNING_TENDRILS_10_MAN = 61887, + SPELL_LIGHTNING_TENDRILS_25_MAN = 63486, + SPELL_OVERLOAD_10_MAN = 61869, + SPELL_OVERLOAD_25_MAN = 63481, + SPELL_OVERLOAD_10_MAN_2 = 63485, + SPELL_OVERLOAD_25_MAN_2 = 61886, + SPELL_RUNE_OF_POWER = 64320, + + // Kologarn + NPC_RIGHT_ARM = 32934, + NPC_RUBBLE = 33768, + SPELL_CRUNCH_ARMOR = 64002, + + SPELL_FOCUSED_EYEBEAM_10_2 = 63346, + SPELL_FOCUSED_EYEBEAM_10 = 63347, + SPELL_FOCUSED_EYEBEAM_25_2 = 63976, + SPELL_FOCUSED_EYEBEAM_25 = 63977, + + // Hodir + NPC_SNOWPACKED_ICICLE = 33174, + NPC_TOASTY_FIRE = 33342, + SPELL_FLASH_FREEZE = 61968, + SPELL_BITING_COLD_PLAYER_AURA = 62039, + + // Freya + NPC_SNAPLASHER = 32916, + NPC_STORM_LASHER = 32919, + NPC_DETONATING_LASHER = 32918, + NPC_ANCIENT_WATER_SPIRIT = 33202, + NPC_ANCIENT_CONSERVATOR = 33203, + NPC_HEALTHY_SPORE = 33215, + NPC_EONARS_GIFT = 33228, + GOBJECT_NATURE_BOMB = 194902, + + // Thorim + NPC_DARK_RUNE_ACOLYTE_I = 32886, + NPC_CAPTURED_MERCENARY_SOLDIER_ALLY = 32885, + NPC_CAPTURED_MERCENARY_SOLDIER_HORDE = 32883, + NPC_CAPTURED_MERCENARY_CAPTAIN_ALLY = 32908, + NPC_CAPTURED_MERCENARY_CAPTAIN_HORDE = 32907, + NPC_JORMUNGAR_BEHEMOT = 32882, + NPC_DARK_RUNE_WARBRINGER = 32877, + NPC_DARK_RUNE_EVOKER = 32878, + NPC_DARK_RUNE_CHAMPION = 32876, + NPC_DARK_RUNE_COMMONER = 32904, + NPC_IRON_RING_GUARD = 32874, + NPC_RUNIC_COLOSSUS = 32872, + NPC_ANCIENT_RUNE_GIANT = 32873, + NPC_DARK_RUNE_ACOLYTE_G = 33110, + NPC_IRON_HONOR_GUARD = 32875, + SPELL_UNBALANCING_STRIKE = 62130, + + // Mimiron + NPC_LEVIATHAN_MKII = 33432, + NPC_VX001 = 33651, + NPC_AERIAL_COMMAND_UNIT = 33670, + NPC_BOMB_BOT = 33836, + NPC_ROCKET_STRIKE_N = 34047, + NPC_ASSAULT_BOT = 34057, + NPC_PROXIMITY_MINE = 34362, + SPELL_P3WX2_LASER_BARRAGE_1 = 63293, + SPELL_P3WX2_LASER_BARRAGE_2 = 63297, + SPELL_SPINNING_UP = 63414, + SPELL_SHOCK_BLAST = 63631, + SPELL_P3WX2_LASER_BARRAGE_3 = 64042, + SPELL_P3WX2_LASER_BARRAGE_AURA_1 = 63274, + SPELL_P3WX2_LASER_BARRAGE_AURA_2 = 63300, + + // General Vezax + SPELL_MARK_OF_THE_FACELESS = 63276, + SPELL_VEZAX_SHADOW_CRASH = 63277, + + // Yogg-Saron + ACTION_ILLUSION_DRAGONS = 1, + ACTION_ILLUSION_ICECROWN = 2, + ACTION_ILLUSION_STORMWIND = 3, + NPC_GUARDIAN_OF_YS = 33136, + NPC_YOGG_SARON = 33288, + NPC_OMINOUS_CLOUD = 33292, + NPC_RUBY_CONSORT = 33716, + NPC_AZURE_CONSORT = 33717, + NPC_BRONZE_CONSORT = 33718, + NPC_EMERALD_CONSORT = 33719, + NPC_OBSIDIAN_CONSORT = 33720, + NPC_ALEXTRASZA = 33536, + NPC_MALYGOS_ILLUSION = 33535, + NPC_NELTHARION = 33523, + NPC_YSERA = 33495, + GO_DRAGON_SOUL = 194462, + NPC_SARA_PHASE_1 = 33134, + NPC_LICH_KING_ILLUSION = 33441, + NPC_IMMOLATED_CHAMPION = 33442, + NPC_SUIT_OF_ARMOR = 33433, + NPC_GARONA = 33436, + NPC_KING_LLANE = 33437, + NPC_DEATHSWORN_ZEALOT = 33567, + NPC_INFLUENCE_TENTACLE = 33943, + NPC_DEATH_ORB = 33882, + NPC_BRAIN = 33890, + NPC_CRUSHER_TENTACLE = 33966, + NPC_CONSTRICTOR_TENTACLE = 33983, + NPC_CORRUPTOR_TENTACLE = 33985, + NPC_IMMORTAL_GUARDIAN = 33988, + NPC_LAUGHING_SKULL = 33990, + NPC_SANITY_WELL = 33991, + NPC_DESCEND_INTO_MADNESS = 34072, + NPC_MARKED_IMMORTAL_GUARDIAN = 36064, + SPELL_SANITY = 63050, + SPELL_BRAIN_LINK = 63802, + SPELL_MALADY_OF_THE_MIND = 63830, + SPELL_SHADOW_BARRIER = 63894, + SPELL_TELEPORT_TO_CHAMBER = 63997, + SPELL_TELEPORT_TO_ICECROWN = 63998, + SPELL_TELEPORT_TO_STORMWIND = 63989, + SPELL_TELEPORT_BACK = 63992, + SPELL_CANCEL_ILLUSION_AURA = 63993, + SPELL_INDUCE_MADNESS = 64059, + SPELL_LUNATIC_GAZE_YS = 64163, + GO_FLEE_TO_THE_SURFACE_PORTAL = 194625, + + // Buffs + SPELL_FROST_TRAP = 13809 +}; + +constexpr float ULDUAR_KOLOGARN_AXIS_Z_PATHING_ISSUE_DETECT = 420.0f; +constexpr float ULDUAR_KOLOGARN_EYEBEAM_RADIUS = 3.0f; +constexpr float ULDUAR_THORIM_AXIS_Z_FLOOR_THRESHOLD = 429.6094f; +constexpr float ULDUAR_THORIM_AXIS_Z_PATHING_ISSUE_DETECT = 410.0f; +constexpr float ULDUAR_AURIAYA_AXIS_Z_PATHING_ISSUE_DETECT = 410.0f; +constexpr float ULDUAR_YOGG_SARON_BOSS_ROOM_AXIS_Z_PATHING_ISSUE_DETECT = 300.0f; +constexpr float ULDUAR_YOGG_SARON_BRAIN_ROOM_AXIS_Z_PATHING_ISSUE_DETECT = 200.0f; +constexpr float ULDUAR_YOGG_SARON_STORMWIND_KEEPER_RADIUS = 150.0f; +constexpr float ULDUAR_YOGG_SARON_ICECROWN_CITADEL_RADIUS = 150.0f; +constexpr float ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_RADIUS = 150.0f; +constexpr float ULDUAR_YOGG_SARON_BRAIN_ROOM_RADIUS = 50.0f; + +extern const Position ULDUAR_THORIM_NEAR_ARENA_CENTER; +extern const Position ULDUAR_THORIM_NEAR_ENTRANCE_POSITION; +extern const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_6_YARDS_1; +extern const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_6_YARDS_2; +extern const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_5_YARDS_1; +extern const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_1; +extern const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_2; +extern const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_3; +extern const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_6_YARDS_1; +extern const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_6_YARDS_2; +extern const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_5_YARDS_1; +extern const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_1; +extern const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_2; +extern const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_3; +extern const Position ULDUAR_THORIM_JUMP_END_POINT; +extern const Position ULDUAR_THORIM_PHASE2_TANK_SPOT; +extern const Position ULDUAR_THORIM_PHASE2_RANGE1_SPOT; +extern const Position ULDUAR_THORIM_PHASE2_RANGE2_SPOT; +extern const Position ULDUAR_THORIM_PHASE2_RANGE3_SPOT; +extern const Position ULDUAR_MIMIRON_PHASE2_SIDE1RANGE_SPOT; +extern const Position ULDUAR_MIMIRON_PHASE2_SIDE1MELEE_SPOT; +extern const Position ULDUAR_MIMIRON_PHASE2_SIDE2RANGE_SPOT; +extern const Position ULDUAR_MIMIRON_PHASE2_SIDE2MELEE_SPOT; +extern const Position ULDUAR_MIMIRON_PHASE2_SIDE3RANGE_SPOT; +extern const Position ULDUAR_MIMIRON_PHASE2_SIDE3MELEE_SPOT; +extern const Position ULDUAR_MIMIRON_PHASE4_TANK_SPOT; +extern const Position ULDUAR_VEZAX_MARK_OF_THE_FACELESS_SPOT; +extern const Position ULDUAR_YOGG_SARON_MIDDLE; +extern const Position ULDUAR_YOGG_SARON_STORMWIND_KEEPER_MIDDLE; +extern const Position ULDUAR_YOGG_SARON_ICECROWN_CITADEL_MIDDLE; +extern const Position ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_MIDDLE; +extern const Position ULDUAR_YOGG_SARON_BRAIN_ROOM_MIDDLE; +extern const Position ULDUAR_YOGG_SARON_STORMWIND_KEEPER_ENTRANCE; +extern const Position ULDUAR_YOGG_SARON_ICECROWN_CITADEL_ENTRANCE; +extern const Position ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_ENTRANCE; +extern const Position ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT; +extern const Position ULDUAR_YOGG_SARON_PHASE_3_RANGED_SPOT; + +class RazorscaleBossHelper : public AiObject +{ +public: + // Enums and constants specific to Razorscale + enum RazorscaleUnits : uint32 + { + UNIT_RAZORSCALE = 33186, + UNIT_DARK_RUNE_SENTINEL = 33846, + UNIT_DARK_RUNE_WATCHER = 33453, + UNIT_DARK_RUNE_GUARDIAN = 33388, + UNIT_DEVOURING_FLAME = 34188, + }; + + enum RazorscaleGameObjects : uint32 + { + GO_RAZORSCALE_HARPOON_1 = 194519, + GO_RAZORSCALE_HARPOON_2 = 194541, + GO_RAZORSCALE_HARPOON_3 = 194542, + GO_RAZORSCALE_HARPOON_4 = 194543, + }; + + enum RazorscaleSpells : uint32 + { + SPELL_CHAIN_1 = 49679, + SPELL_CHAIN_2 = 49682, + SPELL_CHAIN_3 = 49683, + SPELL_CHAIN_4 = 49684, + SPELL_SENTINEL_WHIRLWIND = 63806, + SPELL_STUN_AURA = 62794, + SPELL_FUSEARMOR = 64771 + }; + + static constexpr uint32 FUSEARMOR_THRESHOLD = 2; + + // Constants for arena parameters + static constexpr float RAZORSCALE_FLYING_Z_THRESHOLD = 440.0f; + static constexpr float RAZORSCALE_ARENA_CENTER_X = 587.54f; + static constexpr float RAZORSCALE_ARENA_CENTER_Y = -175.04f; + static constexpr float RAZORSCALE_ARENA_RADIUS = 30.0f; + + // Harpoon cooldown (seconds) + static constexpr time_t HARPOON_COOLDOWN_DURATION = 5; + + // Structure for harpoon data + struct HarpoonData + { + uint32 gameObjectEntry; + uint32 chainSpellId; + }; + + explicit RazorscaleBossHelper(PlayerbotAI* botAI) + : AiObject(botAI), _boss(nullptr) {} + + bool UpdateBossAI(); + Unit* GetBoss() const; + + bool IsGroundPhase() const; + bool IsFlyingPhase() const; + + bool IsHarpoonFired(uint32 chainSpellId) const; + static bool IsHarpoonReady(GameObject* harpoonGO); + static void SetHarpoonOnCooldown(GameObject* harpoonGO); + GameObject* FindNearestHarpoon(float x, float y, float z) const; + + static const std::vector& GetHarpoonData(); + + void AssignRolesBasedOnHealth(); + bool AreRolesAssigned() const; + bool CanSwapRoles() const; + +private: + Unit* _boss; + + // A map to track the last role swap *per bot* by their GUID + static std::unordered_map _lastRoleSwapTime; + + // The cooldown that applies to every bot + static const std::time_t _roleSwapCooldown = 10; + + static std::unordered_map _harpoonCooldowns; +}; + +// 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 = _event_map->GetTimer(); +// 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; +// }; + +#endif diff --git a/src/Ai/Raid/Ulduar/RaidUlduarScripts.h b/src/Ai/Raid/Ulduar/Util/RaidUlduarScripts.h similarity index 100% rename from src/Ai/Raid/Ulduar/RaidUlduarScripts.h rename to src/Ai/Raid/Ulduar/Util/RaidUlduarScripts.h diff --git a/src/Ai/Raid/VaultOfArchavon/RaidVoAActionContext.h b/src/Ai/Raid/VaultOfArchavon/RaidVoAActionContext.h index b304826b8f..263d03d9b7 100644 --- a/src/Ai/Raid/VaultOfArchavon/RaidVoAActionContext.h +++ b/src/Ai/Raid/VaultOfArchavon/RaidVoAActionContext.h @@ -7,6 +7,7 @@ #define _PLAYERBOT_RAIDVOAACTIONCONTEXT_H #include "Action.h" +#include "BossAuraActions.h" #include "NamedObjectContext.h" #include "RaidVoAActions.h" #include "PlayerbotAI.h" diff --git a/src/Ai/Raid/VaultOfArchavon/RaidVoATriggerContext.h b/src/Ai/Raid/VaultOfArchavon/RaidVoATriggerContext.h index 9fe078f809..6566793fde 100644 --- a/src/Ai/Raid/VaultOfArchavon/RaidVoATriggerContext.h +++ b/src/Ai/Raid/VaultOfArchavon/RaidVoATriggerContext.h @@ -7,6 +7,7 @@ #define _PLAYERBOT_RAIDVOATRIGGERCONTEXT_H #include "AiObjectContext.h" +#include "BossAuraTriggers.h" #include "NamedObjectContext.h" #include "RaidVoATriggers.h" diff --git a/src/Bot/Engine/AiObjectContext.cpp b/src/Bot/Engine/AiObjectContext.cpp index b6d5f7de0d..250a392969 100644 --- a/src/Bot/Engine/AiObjectContext.cpp +++ b/src/Bot/Engine/AiObjectContext.cpp @@ -15,8 +15,6 @@ #include "PaladinAiObjectContext.h" #include "Playerbots.h" #include "PriestAiObjectContext.h" -#include "RaidUlduarActionContext.h" -#include "RaidUlduarTriggerContext.h" #include "RogueAiObjectContext.h" #include "ShamanAiObjectContext.h" #include "SharedValueContext.h" @@ -49,6 +47,8 @@ #include "Ai/Raid/VaultOfArchavon/RaidVoATriggerContext.h" #include "Ai/Raid/ObsidianSanctum/RaidOsActionContext.h" #include "Ai/Raid/ObsidianSanctum/RaidOsTriggerContext.h" +#include "Ai/Raid/Ulduar/RaidUlduarActionContext.h" +#include "Ai/Raid/Ulduar/RaidUlduarTriggerContext.h" #include "Ai/Raid/Onyxia/RaidOnyxiaActionContext.h" #include "Ai/Raid/Onyxia/RaidOnyxiaTriggerContext.h" #include "Ai/Raid/Icecrown/RaidIccActionContext.h" diff --git a/src/Bot/PlayerbotAI.cpp b/src/Bot/PlayerbotAI.cpp index 437e62fc69..56c2cd39b0 100644 --- a/src/Bot/PlayerbotAI.cpp +++ b/src/Bot/PlayerbotAI.cpp @@ -1585,7 +1585,7 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster) strategyName = "wotlk-hol"; // Halls of Lightning break; case 603: - strategyName = "uld"; // Ulduar + strategyName = "ulduar"; // Ulduar break; case 604: strategyName = "wotlk-gd"; // Gundrak From 026df0dabead6e200462f7dbb7594a6d0fee9d37 Mon Sep 17 00:00:00 2001 From: kadeshar Date: Fri, 6 Feb 2026 20:57:48 +0100 Subject: [PATCH 06/25] Chilton wand fix (#2115) # Pull Request Added Chilton wand to excluded to equipment items for bots and unified 2 exclusion lists to single one. Resolves: https://github.com/mod-playerbots/mod-playerbots/issues/2093 --- ## How to Test the Changes Couldnt reproduce Chilton wand bug then testing sound impossible. Someone can try getting this items on shaman. ## Complexity & Impact Does this change add new decision branches? - - [x] No - - [ ] Yes Does this change increase per-bot or per-tick processing? - - [x] No - - [ ] Yes Could this logic scale poorly under load? - - [x] No - - [ ] Yes --- ## Defaults & Configuration Does this change modify default bot behavior? - - [x] No - - [ ] Yes 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? - - [x] No - - [ ] Yes --- ## 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/Bot/Factory/PlayerbotFactory.cpp | 4 ---- src/Mgr/Item/RandomItemMgr.cpp | 9 ++++++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/Bot/Factory/PlayerbotFactory.cpp b/src/Bot/Factory/PlayerbotFactory.cpp index dfefd323c2..ca1f4d1800 100644 --- a/src/Bot/Factory/PlayerbotFactory.cpp +++ b/src/Bot/Factory/PlayerbotFactory.cpp @@ -1799,10 +1799,6 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) { for (uint32 itemId : sRandomItemMgr.GetCachedEquipments(requiredLevel, inventoryType)) { - if (itemId == 46978) // shaman earth ring totem - { - continue; - } uint32 skipProb = 25; if (urand(1, 100) <= skipProb) continue; diff --git a/src/Mgr/Item/RandomItemMgr.cpp b/src/Mgr/Item/RandomItemMgr.cpp index 5c0e8c94ab..1053e542e6 100644 --- a/src/Mgr/Item/RandomItemMgr.cpp +++ b/src/Mgr/Item/RandomItemMgr.cpp @@ -2256,10 +2256,13 @@ void RandomItemMgr::BuildEquipCacheNew() { continue; } - if (itemId == 22784) - { // Sunwell Orb + + // Unobtainable or unusable items + if (itemId == 12468 || // Chilton Wand + itemId == 22784 || // Sunwell Orb + itemId == 46978) // Totem of the Earthen Ring continue; - } + equipCacheNew[proto->RequiredLevel][proto->InventoryType].push_back(itemId); } } From 76b6df9ea363912f47355a9984c5c18ec4be05e0 Mon Sep 17 00:00:00 2001 From: Alex Dcnh <140754794+Wishmaster117@users.noreply.github.com> Date: Fri, 6 Feb 2026 23:20:31 +0100 Subject: [PATCH 07/25] Extend SummonWhenGroup to auto-added bots (#2034) ### Summary Extend AiPlayerbot.SummonWhenGroup to apply when bots are auto-added to a group (e.g., addclass bots or raidus style auto invites). ### Motivation Bots added automatically to a group never accept a normal invite, so they do not trigger the summon-on-accept path. When SummonWhenGroup is enabled, these bots should also be teleported next to the master to match expected behavior. ### Implementation details Hook the summon behavior right after automatic group addition. --- src/Ai/Base/Actions/UseMeetingStoneAction.h | 2 +- src/Script/WorldThr/PlayerbotOperations.h | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Ai/Base/Actions/UseMeetingStoneAction.h b/src/Ai/Base/Actions/UseMeetingStoneAction.h index ff1282284e..fd2a2c5f2d 100644 --- a/src/Ai/Base/Actions/UseMeetingStoneAction.h +++ b/src/Ai/Base/Actions/UseMeetingStoneAction.h @@ -17,9 +17,9 @@ class SummonAction : public MovementAction SummonAction(PlayerbotAI* botAI, std::string const name = "summon") : MovementAction(botAI, name) {} bool Execute(Event event) override; + bool Teleport(Player* summoner, Player* player, bool preserveAuras); protected: - bool Teleport(Player* summoner, Player* player, bool preserveAuras); bool SummonUsingGos(Player* summoner, Player* player, bool preserveAuras); bool SummonUsingNpcs(Player* summoner, Player* player, bool preserveAuras); }; diff --git a/src/Script/WorldThr/PlayerbotOperations.h b/src/Script/WorldThr/PlayerbotOperations.h index a80321d535..ee6443cd8a 100644 --- a/src/Script/WorldThr/PlayerbotOperations.h +++ b/src/Script/WorldThr/PlayerbotOperations.h @@ -14,9 +14,11 @@ #include "PlayerbotOperation.h" #include "Player.h" #include "PlayerbotAI.h" +#include "PlayerbotAIConfig.h" #include "PlayerbotMgr.h" #include "PlayerbotRepository.h" #include "RandomPlayerbotMgr.h" +#include "UseMeetingStoneAction.h" #include "WorldSession.h" #include "WorldSessionMgr.h" @@ -74,6 +76,15 @@ class GroupInviteOperation : public PlayerbotOperation if (group->AddMember(target)) { LOG_DEBUG("playerbots", "GroupInviteOperation: Successfully added {} to group", target->GetName()); + if (sPlayerbotAIConfig.summonWhenGroup && target->GetDistance(bot) > sPlayerbotAIConfig.sightDistance) + { + PlayerbotAI* targetAI = sPlayerbotsMgr.GetPlayerbotAI(target); + if (targetAI) + { + SummonAction summonAction(targetAI, "group summon"); + summonAction.Teleport(bot, target, true); + } + } return true; } else From 6ed3f24ecb13158a5a856e2b5112266d5479799b Mon Sep 17 00:00:00 2001 From: kadeshar Date: Sat, 7 Feb 2026 16:34:15 +0100 Subject: [PATCH 08/25] Enforce test fix (#2122) CI/CD PR --------- Co-authored-by: Crow --- .github/workflows/check_pr_source.yml | 4 +++- .github/workflows/code_style.yml | 4 ++-- .github/workflows/core_build.yml | 4 ++-- .github/workflows/macos_build.yml | 4 ++-- .github/workflows/windows_build.yml | 4 ++-- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/.github/workflows/check_pr_source.yml b/.github/workflows/check_pr_source.yml index 877ab85c73..5b354f6f74 100644 --- a/.github/workflows/check_pr_source.yml +++ b/.github/workflows/check_pr_source.yml @@ -1,13 +1,15 @@ -name: Enforce test-staging → main +name: Enforce test-staging → master on: pull_request: branches: - master + - test-staging jobs: require-test-staging: runs-on: ubuntu-22.04 + if: github.event.pull_request.base.ref == 'master' steps: - name: Ensure PR source is test-staging run: | diff --git a/.github/workflows/code_style.yml b/.github/workflows/code_style.yml index 7b1688fb6d..83b78c3927 100644 --- a/.github/workflows/code_style.yml +++ b/.github/workflows/code_style.yml @@ -2,9 +2,9 @@ name: Codestyle on: push: - branches: [ "master" ] + branches: [ "master", "test-staging" ] pull_request: - branches: [ "master" ] + branches: [ "master", "test-staging" ] concurrency: group: "codestyle-${{ github.event.pull_request.number }}" diff --git a/.github/workflows/core_build.yml b/.github/workflows/core_build.yml index 2927716f12..ac6ee60d84 100644 --- a/.github/workflows/core_build.yml +++ b/.github/workflows/core_build.yml @@ -2,9 +2,9 @@ name: ubuntu-build on: push: - branches: [ "master" ] + branches: [ "master", "test-staging" ] pull_request: - branches: [ "master" ] + branches: [ "master", "test-staging" ] concurrency: group: "core-build-${{ github.event.pull_request.number }}" diff --git a/.github/workflows/macos_build.yml b/.github/workflows/macos_build.yml index 30a558c639..ab4d83daa6 100644 --- a/.github/workflows/macos_build.yml +++ b/.github/workflows/macos_build.yml @@ -1,9 +1,9 @@ name: macos-build on: push: - branches: [ "master" ] + branches: [ "master", "test-staging" ] pull_request: - branches: [ "master" ] + branches: [ "master", "test-staging" ] concurrency: group: "macos-build-${{ github.event.pull_request.number }}" diff --git a/.github/workflows/windows_build.yml b/.github/workflows/windows_build.yml index 49678c97aa..121d97751f 100644 --- a/.github/workflows/windows_build.yml +++ b/.github/workflows/windows_build.yml @@ -1,9 +1,9 @@ name: windows-build on: push: - branches: [ "master" ] + branches: [ "master", "test-staging" ] pull_request: - branches: [ "master" ] + branches: [ "master", "test-staging" ] concurrency: group: "windows-build-${{ github.event.pull_request.number }}" From 79fb3a5bbc9e27752a3133a83d56a9c72dc44ed6 Mon Sep 17 00:00:00 2001 From: kadeshar Date: Sat, 7 Feb 2026 17:53:55 +0100 Subject: [PATCH 09/25] - Fixed Oculus drake mounting --- .../Dungeon/Oculus/Action/OculusActions.cpp | 2 +- src/Bot/PlayerbotAI.cpp | 34 ++++++++++++++++--- src/Bot/PlayerbotAI.h | 3 +- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/Ai/Dungeon/Oculus/Action/OculusActions.cpp b/src/Ai/Dungeon/Oculus/Action/OculusActions.cpp index 414acc4bc9..bbb94d110a 100644 --- a/src/Ai/Dungeon/Oculus/Action/OculusActions.cpp +++ b/src/Ai/Dungeon/Oculus/Action/OculusActions.cpp @@ -62,7 +62,7 @@ bool MountDrakeAction::Execute(Event event) break; } - std::vector players = botAI->GetPlayersInGroup(); + std::vector players = botAI->GetAllPlayersInGroup(); for (Player* player : players) { if (!player || !player->IsInWorld() || player->IsDuringRemoveFromWorld()) diff --git a/src/Bot/PlayerbotAI.cpp b/src/Bot/PlayerbotAI.cpp index 437e62fc69..8d95981e40 100644 --- a/src/Bot/PlayerbotAI.cpp +++ b/src/Bot/PlayerbotAI.cpp @@ -2580,7 +2580,7 @@ std::string PlayerbotAI::GetLocalizedGameObjectName(uint32 entry) return name; } -std::vector PlayerbotAI::GetPlayersInGroup() +std::vector PlayerbotAI::GetRealPlayersInGroup() { std::vector members; @@ -2607,6 +2607,30 @@ std::vector PlayerbotAI::GetPlayersInGroup() return members; } +std::vector PlayerbotAI::GetAllPlayersInGroup() +{ + std::vector members; + + Group* group = bot->GetGroup(); + + if (!group) + return members; + + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + + if (!member) + { + continue; + } + + members.push_back(ref->GetSource()); + } + + return members; +} + bool PlayerbotAI::SayToGuild(const std::string& msg) { if (msg.empty()) @@ -2715,9 +2739,9 @@ bool PlayerbotAI::SayToParty(const std::string& msg) ChatHandler::BuildChatPacket(data, CHAT_MSG_PARTY, msg.c_str(), LANG_UNIVERSAL, CHAT_TAG_NONE, bot->GetGUID(), bot->GetName()); - for (auto reciever : GetPlayersInGroup()) + for (auto receiver : GetRealPlayersInGroup()) { - ServerFacade::instance().SendPacket(reciever, &data); + ServerFacade::instance().SendPacket(receiver, &data); } return true; @@ -2732,9 +2756,9 @@ bool PlayerbotAI::SayToRaid(const std::string& msg) ChatHandler::BuildChatPacket(data, CHAT_MSG_RAID, msg.c_str(), LANG_UNIVERSAL, CHAT_TAG_NONE, bot->GetGUID(), bot->GetName()); - for (auto reciever : GetPlayersInGroup()) + for (auto receiver : GetRealPlayersInGroup()) { - ServerFacade::instance().SendPacket(reciever, &data); + ServerFacade::instance().SendPacket(receiver, &data); } return true; diff --git a/src/Bot/PlayerbotAI.h b/src/Bot/PlayerbotAI.h index c2d4aeb75b..3364b31ac5 100644 --- a/src/Bot/PlayerbotAI.h +++ b/src/Bot/PlayerbotAI.h @@ -446,7 +446,8 @@ class PlayerbotAI : public PlayerbotAIBase GameObject* GetGameObject(ObjectGuid guid); // static GameObject* GetGameObject(GameObjectData const* gameObjectData); WorldObject* GetWorldObject(ObjectGuid guid); - std::vector GetPlayersInGroup(); + std::vector GetAllPlayersInGroup(); + std::vector GetRealPlayersInGroup(); const AreaTableEntry* GetCurrentArea(); const AreaTableEntry* GetCurrentZone(); static std::string GetLocalizedAreaName(const AreaTableEntry* entry); From 8585f10f48e470c495338fc5b237ca5cadcdcfaa Mon Sep 17 00:00:00 2001 From: Crow Date: Sun, 8 Feb 2026 05:31:23 -0600 Subject: [PATCH 10/25] Implement Serpentshrine Cavern Strategies (#1888) Edited: Below description of methods were brought up to date as of the PR coming off of draft. ### General I've starting leveraging, to the extent possible, an out-of-combat method to erase map keys. This is mostly useful for timers that need to start upon the pull because I dislike having to rely on a check for a boss to be at 100% HP (or 99.9% or whatever) because it can be unreliable sometimes. ### Trash Underbog Colossi: Some Colossi leave behind a lake of toxin when they die that quickly kills any player that is standing in it. The pool is a dynamic-object-generated AoE, and bots will not avoid it on their own (I think because the AoE is out of combat, plus the radius is much larger than the default avoidance radius in the config). The method does not require bots to be in combat, and simply gets bots to run out of the toxin. You will probably still get a couple of idiots who drink in the middle of it, but in my experience, the vast majority of the raid gets out, and healers that escape can easily keep up a couple of fools until they've drank to full. Greyheart Tidecallers: Bots will mark and destroy Water Elemental Totems immediately. ### Hydross the Unstable The strategy uses 2 tanks, with the main tank assigned to the frost phase and the 1st assistant tank assigned to the nature phase. - The main tank will tank the frost phase, and the first assistant tank will tank the nature phase. They each have designated spots and will wait at their spots twiddling their thumbs while Hydross is in the other phase. - Hunters will misdirect to the applicable tank upon the pull and after each phase change. - The phase change process begins 1 second after Hydross reaches 100% Marks. The current tank will begin moving to the next phase tank's spot for the next tank to take over as soon as Hydross transitions. - DPS is ordered to stop after Hydross reaches 100% Marks until 5 seconds after he transitions. - Bots will prioritize the elementals adds after every phase change, unless Hydross is under 10% HP, in which case they should ignore the adds and burn the boss. - Ranged bots should spread during the frost phase to mitigate the impact of Water Tombs. ### The Lurker Below - There is a designated spot for the main tank. - Ranged DPS will fan out over a 120-degree arc that is centered directly across from the tank spot (to try to spread to reduce Geyser damage while also keeping them behind Lurker). - When Spout begins, all bots will run around behind Lurker. The intent is to keep a distance with a radius of 20 or 21 yards and within 45 degrees (either side) of directly behind him. Movement is specifically tangential along an arc so bots don't run in front of Lurker. - Spout's duration is tracked by a timer. The mechanics of the spell itself are rather unique and don't involve a continuous cast or aura to track easily so I settled for the timer. - If you have 3 (or more) tanks, each of the first 3 tanks will be assigned to one of the 3 Coilfang Guardians during the submerge phase. ### Leotheras the Blind The fight is designed for a Warlock tank. You can choose the Warlock tank by giving a Warlock the Assistant flag. If you don't do that, your highest HP Warlock will be picked. Do NOT switch the Warlock tank to a co +tank strategy--the designated Warlock is hardcoded to spam Searing Pain on Demon Leo and otherwise will engage in normal DPS strategies. If you don't have a Warlock at all, the strategy has some methods built in to try to make things work as best as possible with a melee tank. - The Spellbinders get marked with skulls and killed in order. - There is no designated spot or designated tank for the human phase. Your tanks will fight for aggro. Ranged bots will attempt to keep some distance, and when Whirlwind starts, everybody will run away from Leotheras. - During the demon phase, your melee tanks should take a backseat to your Warlock tank, who will receive help in the form of Misdirection. Bots will get the hell away from the Warlock tank so the Warlock tank should be taking every Chaos Blast alone. - During the final phase, your regular tanks will tank Leotheras, and the Warlock tank will tank his Shadow. The melee tanks will attempt to separate Leotheras from his Shadow so bots can focus down Leotheras without getting hit with Chaos Blasts. - Bots will wait 5 seconds to DPS after every transition into human phase, 12 seconds to DPS after every transition into demon phase, and 8 seconds to DPS after the transition into the final phase. There is no waiting on DPS after Whirlwinds, even though it would be ideal. It's not a big deal to live without, and for various reasons, it would have been a pain in the ass to deal with. - Bots will save Bloodlust/Heroism until after Spellbinders are down. - To deal with the Inner Demons, I disabled DPS assist for bots who are targeted and force them to focus only on their Inner Demons. This is sufficient in my experience for all DPS bots and Protection Warriors and Paladins to kill their Inner Demons, even at 50% damage. Feral Tank Druids and Healers still need help, so the strategy hardcodes their actions while fighting Inner Demons. For example, Resto Druids are coded to shift out of Tree Form, cast Barkskin on themselves, and just spam Wrath until the Inner Demon is dead. There are no bot strategy changes used for this method. ### Fathom-Lord Karathress You will need 4 tanks. Your main tank will tank Karathress, and an assistant tank will tank each Fathom Guard. If you have fewer than 4 tanks, then the priority order for tank assignment will be Karathress, Caribdis, Sharkkis, and then Tidalvess. - Roughly, the tank spots are (1) for Karathress, near where he starts but closer to the ledge for LoS reasons, (2) for Sharkkis, North from his starting location on the other side of the ramp, (3) for Tidalvess, Northwest from his starting location near the pillar, and (4) for Caribdis, far to the West of her starting position, near the corner. - Note that the tanks will probably clip through the terrain a bit when going to their positions. This is due to me implementing a forced MoveTo to the tank position coordinates. There is something weird about the maps in Karathress's room, and the tanks will take some really screwed up paths without making them go directly to the exact coordinates. So this looks stupid but is necessary. - One healer will be assigned to heal the Caribdis tank. Because AC Playerbots does not yet have a focus heal strategy, this just means that such healer has a designated location near the Caribdis tank's location. This healer can be selected with the Assistant flag. - Hunters will misdirect the Fathom Guards onto their applicable tanks. If you don't have three Hunters, the priority is Caribdis, Tidalvess, then Sharkkis. - DPS will wait 12 seconds to begin attacking. After that, they will prioritize targets as follows: - (1): Melee will always prioritize Spitfire Totems as soon as they spawn. This will continue through the duration of the fight. - (2): All bots will kill Tidalvess first. - (3): Melee bots will move to Sharkkis, and ranged bots will move to Caribdis. I understand this is not the standard kill order for players, which would have the entire raid kill Sharkkis next. The reasons I have done this differently are because melee DPS is much stronger with 3.3.5 talents vs. in retail TBC, and because bots get really thrown off by Cyclones and therefore they struggle to kill Caribdis quickly. You do not want Karathress below 75% HP before all Fathom-Guards are dead or he gets a huge damage buff. - (4) If Caribdis dies first, ranged bots will help with Sharkkis. - (5) Everybody kills Sharkkis's pet. - (6) Everybody kills Karathress. ### Morogrim Tidewalker - The main tank will pull the boss to the Northeast pillar, with the tank's back against the pillar. - A hunter will misdirect the boss onto the main tank upon the pull. - When the boss gets to 26% HP, the main tank will begin moving the boss to the Northeast corner of the room in preparation for Phase 2 (which begins at 25%). The tank will move in two steps to get around the pillar. - When the boss gets to 25% HP, ranged will follow the main tank to the corner and stack up right behind the boss. They will also move in two steps. - There is no method for melee since they will just naturally follow the boss anyway. ### Lady Vashj **Phase 1**: - The main tank will tank Vashj in the center of the arena. - If a Shaman is in the main tank's group, that Shaman will attempt to keep a Grounding Totem down in range of the main tank to absorb Shock Blast. This should continue in Phase 3. - Ranged bots will spread out in a semicircle around the center of the arena. - If any bot other than the main tank gets Static Charge, it will run away from other bots. If the main tank gets Static Charge, other bots will run away from the main tank. This method should continue in Phase 3. - If any bot is Entangled and has Static Charge, the bot will attempt to use Cloak of Shadows if it is a Rogue, and Paladins will attempt to use Hand of Freedom. This method should continue in Phase 3 (with some modifications). - Bots will not use Bloodlust or Heroism (saved for Phase 3). Bots will not use any other major cooldowns, either, such as Metamorphosis (saved for Phase 2 and 3). **Phase 2**: There are two central mechanics to this phase, both of which were challenging to get bots to execute properly. First is the system of prioritizing adds. The large playing field and multiple types of adds coming from random directions make this phase not doable with realistic DPS under the standard Playerbots target selection system. Therefore, I took inspiration from liyunfan's Naxx strategy for Phase 1 of Kel'Thuzad to disable dps assist and create a custom target selection system. First, a cheat with respect to the Coilfang Striders: - Tanks will permanently have the Fear Ward aura applied to them if you have raid cheats enabled. This allows them to tank the Coilfang Striders. The standard strategy was to have an Elemental Shaman kite the Strider around the perimeter of the arena, with ranged players (including healers) spamming DoTs on the Strider. If you can make bots do this, then great, but it's far beyond my capabilities. Therefore, with the cheat, the first assistant tank is responsible for tanking Striders and keeping them away from Core passers (described below) and Vashj. Evidently it was (and is, in TBC Classic) possible to tank (and melee DPS) Striders by wearing a Dire Maul Ogre Suit, which would give you enough reach to stay out of the Strider's fear. I actually tried that, and it does not work, either because AC's radiuses are not the same or just because bots do not maintain the same level of precise positioning. But anyway, the point is that technically the Striders are tankable by real players, so maybe that will make you feel better about using this cheat (it's fine enough rationalizing for me). I found this fight to be unmanageable without this cheat (i.e., using a method that would only have bots try to run away from Striders) because each Strider was guaranteed to wipe out a couple of bots, and you really cannot afford to lose anyone. YMMV though. - If cheats are enabled for Striders, Hunters will attempt to Misdirect the Striders to the first assist tank. - If cheats are not enabled, bots will attempt to use slows/roots to stop the Striders. I have some logic for them to use Netherweave Nets, but I suspect it does not actually work so I may remove it instead of trying to get it to function properly. Target priority is as follows: - Hunters and Mages: Enchanted Elementals, Coilfang Striders, Coilfang Elites. - Other Ranged Bots: Elites, Striders, Elementals. - Melee DPS: Elementals, Elites. - Tanks: Elites, Elementals (except if cheats are enabled, the first assistant tank will instead prioritize Striders and then Elementals) - Everybody else (basically means healers): Elementals, Elites, Striders - If there is more than one of the same target, bots will prioritize the one that is closer to Vashj. - In all cases, the valid attack ranged is limited so that bots should not leave the central platform. - If somehow a bot ends up too far from the center of the room and is not actively attacking anything, there is logic to make them run back. Handling Tainted Elementals and the Tainted Core: I will make another post about this later. It is easily the most complicated strategy I've ever worked on (far beyond anything on Kael'thas even) so will necessitate a long explanation. The tl;dr is that there is a chain of two-to-four bots that receive/pass the Tainted Core before using it on a Shield Generator, and if you are playing by yourself, you probably need to turn raid cheats on, in which case there will also be a bot that teleports to, kills, and loots the Tainted Elementals (i.e., the bots will then handle the entire sequence of shutting down Shield Generators). **Phase 3**: - The main tank will pick up Vashj immediately and try to keep her away from Enchanted Elementals. - DPS will burn down residual adds from Phase 2 in the order of (1) elementals, (2) strider for ranged only (if you have more than one up, you're dead), and (3) elites (hopefully you have only one up, but two with one almost dead is possible). - Hunters will kill Toxic Sporebats. This works quite well, but they (and anybody else if ordered to target Sporebats) have a tendency to levitate up into the pipes at the top of the room when killing the Sporebats. To counteract this, a method forcibly teleports bots to the ground if they get more than 2 yards above the ground. - The Phase 1 Cloak of Shadows/Hand of Freedom method is now expanded to include bots Entangled in the Sporebat poison pools (with Hand of Freedom usage prioritized on the main tank). - There is a specific method to avoid the Sporebat poison pools. The Vashj tank will move backwards when avoiding poison. --------- Co-authored-by: kadeshar --- conf/playerbots.conf.dist | 2 +- src/Ai/Raid/RaidStrategyContext.h | 3 + .../Action/RaidSSCActions.cpp | 3128 +++++++++++++++++ .../Action/RaidSSCActions.h | 457 +++ .../Multiplier/RaidSSCMultipliers.cpp | 799 +++++ .../Multiplier/RaidSSCMultipliers.h | 236 ++ .../RaidSSCActionContext.h | 337 ++ .../RaidSSCTriggerContext.h | 325 ++ .../Strategy/RaidSSCStrategy.cpp | 206 ++ .../Strategy/RaidSSCStrategy.h | 18 + .../Trigger/RaidSSCTriggers.cpp | 670 ++++ .../Trigger/RaidSSCTriggers.h | 414 +++ .../Util/RaidSSCHelpers.cpp | 583 +++ .../SerpentshrineCavern/Util/RaidSSCHelpers.h | 189 + src/Bot/Engine/AiObjectContext.cpp | 4 + src/Bot/PlayerbotAI.cpp | 3 + 16 files changed, 7373 insertions(+), 1 deletion(-) create mode 100644 src/Ai/Raid/SerpentshrineCavern/Action/RaidSSCActions.cpp create mode 100644 src/Ai/Raid/SerpentshrineCavern/Action/RaidSSCActions.h create mode 100644 src/Ai/Raid/SerpentshrineCavern/Multiplier/RaidSSCMultipliers.cpp create mode 100644 src/Ai/Raid/SerpentshrineCavern/Multiplier/RaidSSCMultipliers.h create mode 100644 src/Ai/Raid/SerpentshrineCavern/RaidSSCActionContext.h create mode 100644 src/Ai/Raid/SerpentshrineCavern/RaidSSCTriggerContext.h create mode 100644 src/Ai/Raid/SerpentshrineCavern/Strategy/RaidSSCStrategy.cpp create mode 100644 src/Ai/Raid/SerpentshrineCavern/Strategy/RaidSSCStrategy.h create mode 100644 src/Ai/Raid/SerpentshrineCavern/Trigger/RaidSSCTriggers.cpp create mode 100644 src/Ai/Raid/SerpentshrineCavern/Trigger/RaidSSCTriggers.h create mode 100644 src/Ai/Raid/SerpentshrineCavern/Util/RaidSSCHelpers.cpp create mode 100644 src/Ai/Raid/SerpentshrineCavern/Util/RaidSSCHelpers.h diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 9073773167..18a6addf6d 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -558,7 +558,7 @@ AiPlayerbot.AutoGearScoreLimit = 0 # "mana" (bots have infinite mana) # "power" (bots have infinite energy, rage, and runic power) # "taxi" (bots may use all flight paths, though they will not actually learn them) -# "raid" (bots use cheats implemented into raid strategies (currently only for Ulduar)) +# "raid" (bots use cheats implemented into raid strategies (currently only for SSC and Ulduar)) # To use multiple cheats, separate them by commas below (e.g., to enable all, use "gold,health,mana,power,raid,taxi") # Default: food, taxi, and raid are enabled AiPlayerbot.BotCheats = "food,taxi,raid" diff --git a/src/Ai/Raid/RaidStrategyContext.h b/src/Ai/Raid/RaidStrategyContext.h index 3c7971fb6f..4a040985eb 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 "RaidSSCStrategy.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["ssc"] = &RaidStrategyContext::ssc; 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* ssc(PlayerbotAI* botAI) { return new RaidSSCStrategy(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/Ai/Raid/SerpentshrineCavern/Action/RaidSSCActions.cpp b/src/Ai/Raid/SerpentshrineCavern/Action/RaidSSCActions.cpp new file mode 100644 index 0000000000..7aa3eda35b --- /dev/null +++ b/src/Ai/Raid/SerpentshrineCavern/Action/RaidSSCActions.cpp @@ -0,0 +1,3128 @@ +#include "RaidSSCActions.h" +#include "RaidSSCHelpers.h" +#include "AiFactory.h" +#include "Corpse.h" +#include "LootAction.h" +#include "LootObjectStack.h" +#include "ObjectAccessor.h" +#include "Playerbots.h" +#include "RaidBossHelpers.h" +#include "RtiTargetValue.h" + +using namespace SerpentShrineCavernHelpers; + +// General + +bool SerpentShrineCavernEraseTimersAndTrackersAction::Execute(Event /*event*/) +{ + const uint32 instanceId = bot->GetMap()->GetInstanceId(); + const ObjectGuid guid = bot->GetGUID(); + + bool erased = false; + if (!AI_VALUE2(Unit*, "find target", "hydross the unstable")) + { + if (hydrossChangeToNaturePhaseTimer.erase(instanceId) > 0) + erased = true; + if (hydrossChangeToFrostPhaseTimer.erase(instanceId) > 0) + erased = true; + if (hydrossNatureDpsWaitTimer.erase(instanceId) > 0) + erased = true; + if (hydrossFrostDpsWaitTimer.erase(instanceId) > 0) + erased = true; + } + if (!AI_VALUE2(Unit*, "find target", "the lurker below")) + { + if (lurkerRangedPositions.erase(guid) > 0) + erased = true; + if (lurkerSpoutTimer.erase(instanceId) > 0) + erased = true; + } + if (!AI_VALUE2(Unit*, "find target", "fathom-lord karathress")) + { + if (karathressDpsWaitTimer.erase(instanceId) > 0) + erased = true; + } + if (!AI_VALUE2(Unit*, "find target", "morogrim tidewalker")) + { + if (tidewalkerTankStep.erase(guid) > 0) + erased = true; + if (tidewalkerRangedStep.erase(guid) > 0) + erased = true; + } + if (!AI_VALUE2(Unit*, "find target", "lady vashj")) + { + if (vashjRangedPositions.erase(guid) > 0) + erased = true; + if (hasReachedVashjRangedPosition.erase(guid) > 0) + erased = true; + } + + return erased; +} + +// Trash Mobs + +// Move out of toxic pool left behind by some colossi upon death +bool UnderbogColossusEscapeToxicPoolAction::Execute(Event /*event*/) +{ + Aura* aura = bot->GetAura(SPELL_TOXIC_POOL); + if (!aura) + return false; + + DynamicObject* dynObj = aura->GetDynobjOwner(); + if (!dynObj) + return false; + + float radius = dynObj->GetRadius(); + if (radius <= 0.0f) + { + const SpellInfo* sInfo = sSpellMgr->GetSpellInfo(dynObj->GetSpellId()); + if (sInfo) + { + for (int e = 0; e < MAX_SPELL_EFFECTS; ++e) + { + auto const& eff = sInfo->Effects[e]; + if (eff.Effect == SPELL_EFFECT_SCHOOL_DAMAGE || + (eff.Effect == SPELL_EFFECT_APPLY_AURA && + eff.ApplyAuraName == SPELL_AURA_PERIODIC_DAMAGE)) + { + radius = eff.CalcRadius(); + break; + } + } + } + } + + if (radius <= 0.0f) + return false; + + constexpr float bufferDist = 3.0f; + constexpr float centerThreshold = 1.0f; + + float dx = bot->GetPositionX() - dynObj->GetPositionX(); + float dy = bot->GetPositionY() - dynObj->GetPositionY(); + + float distToObj = bot->GetExactDist2d(dynObj->GetPositionX(), dynObj->GetPositionY()); + const float insideThresh = radius + centerThreshold; + + if (distToObj > insideThresh) + return false; + + float safeDist = radius + bufferDist; + float moveX, moveY; + + if (distToObj == 0.0f) + { + float angle = frand(0.0f, static_cast(M_PI * 2.0)); + moveX = dynObj->GetPositionX() + std::cos(angle) * safeDist; + moveY = dynObj->GetPositionY() + std::sin(angle) * safeDist; + } + else + { + float invDist = 1.0f / distToObj; + moveX = dynObj->GetPositionX() + (dx * invDist) * safeDist; + moveY = dynObj->GetPositionY() + (dy * invDist) * safeDist; + } + + botAI->Reset(); + return MoveTo(SSC_MAP_ID, moveX, moveY, bot->GetPositionZ(), false, false, false, + true, MovementPriority::MOVEMENT_FORCED, true, false); +} + +bool GreyheartTidecallerMarkWaterElementalTotemAction::Execute(Event /*event*/) +{ + if (Unit* totem = GetFirstAliveUnitByEntry(botAI, NPC_WATER_ELEMENTAL_TOTEM)) + MarkTargetWithSkull(bot, totem); + + return false; +} + +// Hydross the Unstable + +// (1) When tanking, move to designated tanking spot on frost side +// (2) 1 second after 100% Mark of Hydross, move to nature tank's spot to hand off boss +// (3) When Hydross is in nature form, move back to frost tank spot and wait for transition +bool HydrossTheUnstablePositionFrostTankAction::Execute(Event /*event*/) +{ + Unit* hydross = AI_VALUE2(Unit*, "find target", "hydross the unstable"); + if (!hydross) + return false; + + if (!hydross->HasAura(SPELL_CORRUPTION) && !HasMarkOfHydrossAt100Percent(bot)) + { + MarkTargetWithSquare(bot, hydross); + SetRtiTarget(botAI, "square", hydross); + + if (bot->GetTarget() != hydross->GetGUID()) + return Attack(hydross); + + if (hydross->GetVictim() == bot && bot->IsWithinMeleeRange(hydross)) + { + const Position& position = HYDROSS_FROST_TANK_POSITION; + float distToPosition = + bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()); + + if (distToPosition > 2.0f) + { + float dX = position.GetPositionX() - bot->GetPositionX(); + float dY = position.GetPositionY() - bot->GetPositionY(); + float moveDist = std::min(5.0f, distToPosition); + float moveX = bot->GetPositionX() + (dX / distToPosition) * moveDist; + float moveY = bot->GetPositionY() + (dY / distToPosition) * moveDist; + + return MoveTo(SSC_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, + false, true, MovementPriority::MOVEMENT_COMBAT, true, true); + } + } + } + + if (!hydross->HasAura(SPELL_CORRUPTION) && HasMarkOfHydrossAt100Percent(bot) && + hydross->GetVictim() == bot && bot->IsWithinMeleeRange(hydross)) + { + const time_t now = std::time(nullptr); + auto it = hydrossChangeToNaturePhaseTimer.find(hydross->GetMap()->GetInstanceId()); + + if (it != hydrossChangeToNaturePhaseTimer.end() && (now - it->second) >= 1) + { + const Position& position = HYDROSS_NATURE_TANK_POSITION; + float distToPosition = + bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()); + + if (distToPosition > 2.0f) + { + float dX = position.GetPositionX() - bot->GetPositionX(); + float dY = position.GetPositionY() - bot->GetPositionY(); + float moveDist = std::min(5.0f, distToPosition); + float moveX = bot->GetPositionX() + (dX / distToPosition) * moveDist; + float moveY = bot->GetPositionY() + (dY / distToPosition) * moveDist; + + return MoveTo(SSC_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, + false, true, MovementPriority::MOVEMENT_COMBAT, true, true); + } + else + { + botAI->Reset(); + return true; + } + } + } + + if (hydross->HasAura(SPELL_CORRUPTION)) + { + const Position& position = HYDROSS_FROST_TANK_POSITION; + if (bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()) > 2.0f) + { + return MoveTo(SSC_MAP_ID, position.GetPositionX(), position.GetPositionY(), + position.GetPositionZ(), false, false, false, true, + MovementPriority::MOVEMENT_COMBAT, true, false); + } + } + + return false; +} + +// (1) When tanking, move to designated tanking spot on nature side +// (2) 1 second after 100% Mark of Corruption, move to frost tank's spot to hand off boss +// (3) When Hydross is in frost form, move back to nature tank spot and wait for transition +bool HydrossTheUnstablePositionNatureTankAction::Execute(Event /*event*/) +{ + Unit* hydross = AI_VALUE2(Unit*, "find target", "hydross the unstable"); + if (!hydross) + return false; + + if (hydross->HasAura(SPELL_CORRUPTION) && !HasMarkOfCorruptionAt100Percent(bot)) + { + MarkTargetWithTriangle(bot, hydross); + SetRtiTarget(botAI, "triangle", hydross); + + if (bot->GetTarget() != hydross->GetGUID()) + return Attack(hydross); + + if (hydross->GetVictim() == bot && bot->IsWithinMeleeRange(hydross)) + { + const Position& position = HYDROSS_NATURE_TANK_POSITION; + float distToPosition = + bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()); + + if (distToPosition > 2.0f) + { + float dX = position.GetPositionX() - bot->GetPositionX(); + float dY = position.GetPositionY() - bot->GetPositionY(); + float moveDist = std::min(5.0f, distToPosition); + float moveX = bot->GetPositionX() + (dX / distToPosition) * moveDist; + float moveY = bot->GetPositionY() + (dY / distToPosition) * moveDist; + + return MoveTo(SSC_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, + false, true, MovementPriority::MOVEMENT_COMBAT, true, true); + } + } + } + + if (hydross->HasAura(SPELL_CORRUPTION) && HasMarkOfCorruptionAt100Percent(bot) && + hydross->GetVictim() == bot && bot->IsWithinMeleeRange(hydross)) + { + const time_t now = std::time(nullptr); + auto it = hydrossChangeToFrostPhaseTimer.find(hydross->GetMap()->GetInstanceId()); + + if (it != hydrossChangeToFrostPhaseTimer.end() && (now - it->second) >= 1) + { + const Position& position = HYDROSS_FROST_TANK_POSITION; + float distToPosition = + bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()); + + if (distToPosition > 2.0f) + { + float dX = position.GetPositionX() - bot->GetPositionX(); + float dY = position.GetPositionY() - bot->GetPositionY(); + float moveDist = std::min(5.0f, distToPosition); + float moveX = bot->GetPositionX() + (dX / distToPosition) * moveDist; + float moveY = bot->GetPositionY() + (dY / distToPosition) * moveDist; + + return MoveTo(SSC_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, + false, true, MovementPriority::MOVEMENT_COMBAT, true, true); + } + else + { + botAI->Reset(); + return true; + } + } + } + + if (!hydross->HasAura(SPELL_CORRUPTION)) + { + const Position& position = HYDROSS_NATURE_TANK_POSITION; + if (bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()) > 2.0f) + { + return MoveTo(SSC_MAP_ID, position.GetPositionX(), position.GetPositionY(), + position.GetPositionZ(), false, false, false, true, + MovementPriority::MOVEMENT_COMBAT, true, false); + } + } + + return false; +} + +bool HydrossTheUnstablePrioritizeElementalAddsAction::Execute(Event /*event*/) +{ + Unit* waterElemental = GetFirstAliveUnitByEntry(botAI, NPC_PURE_SPAWN_OF_HYDROSS); + if (waterElemental) + { + if (IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID, nullptr)) + MarkTargetWithSkull(bot, waterElemental); + + SetRtiTarget(botAI, "skull", waterElemental); + + if (bot->GetTarget() != waterElemental->GetGUID()) + return Attack(waterElemental); + } + else if (Unit* natureElemental = GetFirstAliveUnitByEntry(botAI, NPC_TAINTED_SPAWN_OF_HYDROSS)) + { + if (IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID, nullptr)) + MarkTargetWithSkull(bot, natureElemental); + + SetRtiTarget(botAI, "skull", natureElemental); + + if (bot->GetTarget() != natureElemental->GetGUID()) + return Attack(natureElemental); + } + + return false; +} + +// To mitigate the effect of Water Tomb +bool HydrossTheUnstableFrostPhaseSpreadOutAction::Execute(Event /*event*/) +{ + if (!AI_VALUE2(Unit*, "find target", "hydross the unstable")) + return false; + + if (Group* group = bot->GetGroup()) + { + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || member == bot || !member->IsAlive()) + continue; + + constexpr float safeDistance = 6.0f; + constexpr uint32 minInterval = 1000; + if (bot->GetExactDist2d(member) < safeDistance) + return FleePosition(member->GetPosition(), safeDistance, minInterval); + } + } + + return false; +} + +bool HydrossTheUnstableMisdirectBossToTankAction::Execute(Event /*event*/) +{ + Unit* hydross = AI_VALUE2(Unit*, "find target", "hydross the unstable"); + if (!hydross) + return false; + + if (Group* group = bot->GetGroup()) + { + if (TryMisdirectToFrostTank(hydross, group)) + return true; + + if (TryMisdirectToNatureTank(hydross, group)) + return true; + } + + return false; +} + +bool HydrossTheUnstableMisdirectBossToTankAction::TryMisdirectToFrostTank( + Unit* hydross, Group* group) +{ + Player* frostTank = nullptr; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (member && member->IsAlive() && botAI->IsMainTank(member)) + { + frostTank = member; + break; + } + } + + if (HasNoMarkOfHydross(bot) && !hydross->HasAura(SPELL_CORRUPTION) && frostTank) + { + if (botAI->CanCastSpell("misdirection", frostTank)) + return botAI->CastSpell("misdirection", frostTank); + + if (bot->HasAura(SPELL_MISDIRECTION) && botAI->CanCastSpell("steady shot", hydross)) + return botAI->CastSpell("steady shot", hydross); + } + + return false; +} + +bool HydrossTheUnstableMisdirectBossToTankAction::TryMisdirectToNatureTank( + Unit* hydross, Group* group) +{ + Player* natureTank = nullptr; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (member && botAI->IsAssistTankOfIndex(member, 0, true)) + { + natureTank = member; + break; + } + } + + if (HasNoMarkOfCorruption(bot) && hydross->HasAura(SPELL_CORRUPTION) && natureTank) + { + if (botAI->CanCastSpell("misdirection", natureTank)) + return botAI->CastSpell("misdirection", natureTank); + + if (bot->HasAura(SPELL_MISDIRECTION) && botAI->CanCastSpell("steady shot", hydross)) + return botAI->CastSpell("steady shot", hydross); + } + + return false; +} + +bool HydrossTheUnstableStopDpsUponPhaseChangeAction::Execute(Event /*event*/) +{ + Unit* hydross = AI_VALUE2(Unit*, "find target", "hydross the unstable"); + if (!hydross) + return false; + + const uint32 instanceId = hydross->GetMap()->GetInstanceId(); + const time_t now = std::time(nullptr); + constexpr uint8 phaseStartStopSeconds = 5; + constexpr uint8 phaseEndStopSeconds = 1; + + bool shouldStopDps = false; + + // 1 second after 100% Mark of Hydross, stop DPS + auto itNature = hydrossChangeToNaturePhaseTimer.find(instanceId); + if (itNature != hydrossChangeToNaturePhaseTimer.end() && + (now - itNature->second) >= phaseEndStopSeconds) + shouldStopDps = true; + + // Keep DPS stopped for 5 seconds after transition into nature phase + auto itNatureDps = hydrossNatureDpsWaitTimer.find(instanceId); + if (itNatureDps != hydrossNatureDpsWaitTimer.end() && + (now - itNatureDps->second) < phaseStartStopSeconds) + shouldStopDps = true; + + // 1 second after 100% Mark of Corruption, stop DPS + auto itFrost = hydrossChangeToFrostPhaseTimer.find(instanceId); + if (itFrost != hydrossChangeToFrostPhaseTimer.end() && + (now - itFrost->second) >= phaseEndStopSeconds) + shouldStopDps = true; + + // Keep DPS stopped for 5 seconds after transition into frost phase + auto itFrostDps = hydrossFrostDpsWaitTimer.find(instanceId); + if (itFrostDps != hydrossFrostDpsWaitTimer.end() && + (now - itFrostDps->second) < phaseStartStopSeconds) + shouldStopDps = true; + + if (shouldStopDps) + { + botAI->Reset(); + return true; + } + + return false; +} + +bool HydrossTheUnstableManageTimersAction::Execute(Event /*event*/) +{ + Unit* hydross = AI_VALUE2(Unit*, "find target", "hydross the unstable"); + if (!hydross) + return false; + + const uint32 instanceId = hydross->GetMap()->GetInstanceId(); + const time_t now = std::time(nullptr); + + bool changed = false; + if (!hydross->HasAura(SPELL_CORRUPTION)) + { + if (hydrossFrostDpsWaitTimer.try_emplace(instanceId, now).second) + changed = true; + if (hydrossNatureDpsWaitTimer.erase(instanceId) > 0) + changed = true; + if (hydrossChangeToFrostPhaseTimer.erase(instanceId) > 0) + changed = true; + if (HasMarkOfHydrossAt100Percent(bot)) + { + if (hydrossChangeToNaturePhaseTimer.try_emplace(instanceId, now).second) + changed = true; + } + } + else + { + if (hydrossNatureDpsWaitTimer.try_emplace(instanceId, now).second) + changed = true; + if (hydrossFrostDpsWaitTimer.erase(instanceId) > 0) + changed = true; + if (hydrossChangeToNaturePhaseTimer.erase(instanceId) > 0) + changed = true; + if (HasMarkOfCorruptionAt100Percent(bot)) + { + if (hydrossChangeToFrostPhaseTimer.try_emplace(instanceId, now).second) + changed = true; + } + } + + return changed; +} + +// The Lurker Below + +// Run around behind Lurker during Spout +bool TheLurkerBelowRunAroundBehindBossAction::Execute(Event /*event*/) +{ + Unit* lurker = AI_VALUE2(Unit*, "find target", "the lurker below"); + if (!lurker) + return false; + + float radius = frand(20.0f, 21.0f); + float botAngle = std::atan2( + bot->GetPositionY() - lurker->GetPositionY(), bot->GetPositionX() - lurker->GetPositionX()); + float relativeAngle = Position::NormalizeOrientation(botAngle - lurker->GetOrientation()); + constexpr float safeArc = M_PI / 2.0f; + + if (std::fabs(Position::NormalizeOrientation(relativeAngle - M_PI)) > safeArc / 2.0f) + { + float tangentAngle = botAngle + (relativeAngle > M_PI ? -0.1f : 0.1f); + float moveX = lurker->GetPositionX() + radius * std::cos(tangentAngle); + float moveY = lurker->GetPositionY() + radius * std::sin(tangentAngle); + botAI->Reset(); + return MoveTo(SSC_MAP_ID, moveX, moveY, lurker->GetPositionZ(), false, false, + false, false, MovementPriority::MOVEMENT_FORCED, true, false); + } + else + { + float behindAngle = lurker->GetOrientation() + M_PI + frand(-0.5f, 0.5f) * safeArc; + float targetX = lurker->GetPositionX() + radius * std::cos(behindAngle); + float targetY = lurker->GetPositionY() + radius * std::sin(behindAngle); + if (bot->GetExactDist2d(targetX, targetY) > 2.0f) + { + botAI->Reset(); + return MoveTo(SSC_MAP_ID, targetX, targetY, lurker->GetPositionZ(), false, false, + false, false, MovementPriority::MOVEMENT_FORCED, true, false); + } + } + + return false; +} + +bool TheLurkerBelowPositionMainTankAction::Execute(Event /*event*/) +{ + Unit* lurker = AI_VALUE2(Unit*, "find target", "the lurker below"); + if (!lurker) + return false; + + if (bot->GetTarget() != lurker->GetGUID()) + return Attack(lurker); + + const Position& position = LURKER_MAIN_TANK_POSITION; + if (bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()) > 0.2f) + { + return MoveTo(SSC_MAP_ID, position.GetPositionX(), position.GetPositionY(), + position.GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_FORCED, true, false); + } + + return false; +} + +// Assign ranged positions within a 120-degree arc behind Lurker +bool TheLurkerBelowSpreadRangedInArcAction::Execute(Event /*event*/) +{ + Unit* lurker = AI_VALUE2(Unit*, "find target", "the lurker below"); + if (!lurker) + return false; + + std::vector rangedMembers; + if (Group* group = bot->GetGroup()) + { + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive() || !botAI->IsRanged(member)) + continue; + + rangedMembers.push_back(member); + } + } + + if (rangedMembers.empty()) + return false; + + const ObjectGuid guid = bot->GetGUID(); + + auto it = lurkerRangedPositions.find(guid); + if (it == lurkerRangedPositions.end()) + { + size_t count = rangedMembers.size(); + auto findIt = std::find(rangedMembers.begin(), rangedMembers.end(), bot); + size_t botIndex = (findIt != rangedMembers.end()) ? + std::distance(rangedMembers.begin(), findIt) : 0; + + constexpr float arcSpan = 2.0f * M_PI / 3.0f; + constexpr float arcCenter = 2.262f; + constexpr float arcStart = arcCenter - arcSpan / 2.0f; + + float angle = (count == 1) ? arcCenter : + (arcStart + arcSpan * static_cast(botIndex) / static_cast(count - 1)); + float radius = 28.0f; + + float targetX = lurker->GetPositionX() + radius * std::sin(angle); + float targetY = lurker->GetPositionY() + radius * std::cos(angle); + + lurkerRangedPositions.try_emplace(guid, Position(targetX, targetY, lurker->GetPositionZ())); + it = lurkerRangedPositions.find(guid); + } + + if (it == lurkerRangedPositions.end()) + return false; + + const Position& position = it->second; + if (bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()) > 2.0f) + { + return MoveTo(SSC_MAP_ID, position.GetPositionX(), position.GetPositionY(), + position.GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_COMBAT, true, false); + } + + return false; +} + +// During the submerge phase, if there are >= 3 tanks in the raid, +// the first 3 will each pick up 1 Guardian +bool TheLurkerBelowTanksPickUpAddsAction::Execute(Event /*event*/) +{ + Player* mainTank = nullptr; + Player* firstAssistTank = nullptr; + Player* secondAssistTank = nullptr; + + if (Group* group = bot->GetGroup()) + { + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive()) + continue; + + if (!mainTank && botAI->IsMainTank(member)) + mainTank = member; + else if (!firstAssistTank && botAI->IsAssistTankOfIndex(member, 0, true)) + firstAssistTank = member; + else if (!secondAssistTank && botAI->IsAssistTankOfIndex(member, 1, true)) + secondAssistTank = member; + } + } + + if (!mainTank || !firstAssistTank || !secondAssistTank) + return false; + + std::vector guardians; + auto const& npcs = + botAI->GetAiObjectContext()->GetValue("nearest hostile npcs")->Get(); + for (auto guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (unit && unit->IsAlive() && unit->GetEntry() == NPC_COILFANG_GUARDIAN) + guardians.push_back(unit); + } + + if (guardians.size() < 3) + return false; + + std::vector tanks = { mainTank, firstAssistTank, secondAssistTank }; + std::vector rtiIndices = + { + RtiTargetValue::starIndex, + RtiTargetValue::circleIndex, + RtiTargetValue::diamondIndex + }; + std::vector rtiNames = { "star", "circle", "diamond" }; + + for (size_t i = 0; i < 3; ++i) + { + Player* tank = tanks[i]; + Unit* guardian = guardians[i]; + if (bot == tank) + { + MarkTargetWithIcon(bot, guardian, rtiIndices[i]); + SetRtiTarget(botAI, rtiNames[i], guardian); + if (bot->GetTarget() != guardian->GetGUID()) + return Attack(guardian); + } + } + + return false; +} + +bool TheLurkerBelowManageSpoutTimerAction::Execute(Event /*event*/) +{ + Unit* lurker = AI_VALUE2(Unit*, "find target", "the lurker below"); + if (!lurker) + return false; + + const uint32 instanceId = lurker->GetMap()->GetInstanceId(); + const time_t now = std::time(nullptr); + + auto it = lurkerSpoutTimer.find(instanceId); + if (it != lurkerSpoutTimer.end() && it->second <= now) + { + lurkerSpoutTimer.erase(it); + it = lurkerSpoutTimer.end(); + } + + const time_t spoutCastTime = 20; + if (IsLurkerCastingSpout(lurker) && it == lurkerSpoutTimer.end()) + lurkerSpoutTimer.try_emplace(instanceId, now + spoutCastTime); + + return false; +} + +// Leotheras the Blind + +bool LeotherasTheBlindTargetSpellbindersAction::Execute(Event /*event*/) +{ + if (Unit* spellbinder = GetFirstAliveUnitByEntry(botAI, NPC_GREYHEART_SPELLBINDER)) + MarkTargetWithSkull(bot, spellbinder); + + return false; +} + +// Warlock tank action--see GetLeotherasDemonFormTank in RaidSSCHelpers.cpp +// Use tank strategy for Demon Form and DPS strategy for Human Form +bool LeotherasTheBlindDemonFormTankAttackBossAction::Execute(Event /*event*/) +{ + Unit* innerDemon = nullptr; + auto const& npcs = + botAI->GetAiObjectContext()->GetValue("nearest hostile npcs")->Get(); + for (auto const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + Creature* creature = unit ? unit->ToCreature() : nullptr; + if (creature && creature->GetEntry() == NPC_INNER_DEMON + && creature->GetSummonerGUID() == bot->GetGUID()) + { + innerDemon = creature; + break; + } + } + + if (innerDemon) + return false; + + if (Unit* leotherasDemon = GetActiveLeotherasDemon(botAI)) + { + MarkTargetWithSquare(bot, leotherasDemon); + SetRtiTarget(botAI, "square", leotherasDemon); + + if (botAI->CanCastSpell("searing pain", leotherasDemon)) + return botAI->CastSpell("searing pain", leotherasDemon); + } + + return false; +} + +// Stop melee tanks from attacking upon transformation so they don't take aggro +// Applies only if there is a Warlock tank present +bool LeotherasTheBlindMeleeTanksDontAttackDemonFormAction::Execute(Event /*event*/) +{ + bot->AttackStop(); + botAI->Reset(); + return true; +} + +// Intent is to keep enough distance from Leotheras and spread to prepare for Whirlwind +// And stay away from the Warlock tank to avoid Chaos Blasts +bool LeotherasTheBlindPositionRangedAction::Execute(Event /*event*/) +{ + constexpr float safeDistFromBoss = 15.0f; + Unit* leotherasHuman = GetLeotherasHuman(botAI); + if (leotherasHuman && bot->GetExactDist2d(leotherasHuman) < safeDistFromBoss && + leotherasHuman->GetVictim() != bot) + { + constexpr uint32 minInterval = 500; + return FleePosition(leotherasHuman->GetPosition(), safeDistFromBoss, minInterval); + } + + Group* group = bot->GetGroup(); + if (!group) + return false; + + if (GetActiveLeotherasDemon(botAI)) + { + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || member == bot || !member->IsAlive()) + continue; + + constexpr uint32 minInterval = 0; + if (GetLeotherasDemonFormTank(bot) == member) + { + constexpr float safeDistFromTank = 10.0f; + if (bot->GetExactDist2d(member) < safeDistFromTank) + return FleePosition(member->GetPosition(), safeDistFromTank, minInterval); + } + else + { + constexpr float safeDistFromMember = 6.0f; + if (bot->GetExactDist2d(member) < safeDistFromMember) + return FleePosition(member->GetPosition(), safeDistFromMember, minInterval); + } + } + } + + return false; +} + +bool LeotherasTheBlindRunAwayFromWhirlwindAction::Execute(Event /*event*/) +{ + if (Unit* leotherasHuman = GetLeotherasHuman(botAI)) + { + float currentDistance = bot->GetExactDist2d(leotherasHuman); + constexpr float safeDistance = 25.0f; + if (currentDistance < safeDistance) + { + botAI->Reset(); + return MoveAway(leotherasHuman, safeDistance - currentDistance); + } + } + + return false; +} + +// This method is likely unnecessary unless the player does not use a Warlock tank +// If a melee tank is used, other melee needs to run away after too many Chaos Blast stacks +bool LeotherasTheBlindMeleeDpsRunAwayFromBossAction::Execute(Event /*event*/) +{ + if (botAI->CanCastSpell("cloak of shadows", bot)) + return botAI->CastSpell("cloak of shadows", bot); + + Unit* leotheras = GetPhase2LeotherasDemon(botAI); + if (!leotheras) + return false; + + Unit* demonVictim = leotheras->GetVictim(); + if (!demonVictim) + return false; + + float currentDistance = bot->GetExactDist2d(demonVictim); + constexpr float safeDistance = 10.0f; + if (currentDistance < safeDistance) + { + botAI->Reset(); + if (demonVictim != bot) + return MoveAway(demonVictim, safeDistance - currentDistance); + } + + return false; +} + +// Hardcoded actions for healers and bear tanks to kill Inner Demons +bool LeotherasTheBlindDestroyInnerDemonAction::Execute(Event /*event*/) +{ + Unit* innerDemon = nullptr; + auto const& npcs = + botAI->GetAiObjectContext()->GetValue("nearest hostile npcs")->Get(); + for (auto const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + Creature* creature = unit ? unit->ToCreature() : nullptr; + if (creature && creature->GetEntry() == NPC_INNER_DEMON + && creature->GetSummonerGUID() == bot->GetGUID()) + { + innerDemon = creature; + break; + } + } + + if (innerDemon) + { + if (botAI->IsTank(bot) && bot->getClass() == CLASS_DRUID) + return HandleFeralTankStrategy(innerDemon); + + if (botAI->IsHeal(bot)) + return HandleHealerStrategy(innerDemon); + + // Roles without a strategy need to affirmatively attack their Inner Demons + // Because DPS assist is disabled via multipliers + if (bot->GetTarget() != innerDemon->GetGUID()) + return Attack(innerDemon); + } + + return false; +} + +// At 50% nerfed damage, bears have trouble killing their Inner Demons without a specific strategy +// Warrior and Paladin tanks have no trouble in my experience (Prot Warriors have high DPS, and +// Prot Paladins have an advantage in that Inner Demons are weak to Holy) +bool LeotherasTheBlindDestroyInnerDemonAction::HandleFeralTankStrategy(Unit* innerDemon) +{ + if (bot->HasAura(SPELL_DIRE_BEAR_FORM)) + bot->RemoveAura(SPELL_DIRE_BEAR_FORM); + + if (bot->HasAura(SPELL_BEAR_FORM)) + bot->RemoveAura(SPELL_BEAR_FORM); + + bool casted = false; + if (!bot->HasAura(SPELL_CAT_FORM) && botAI->CanCastSpell("cat form", bot)) + { + if (botAI->CastSpell("cat form", bot)) + casted = true; + } + if (botAI->CanCastSpell("berserk", bot)) + { + if (botAI->CastSpell("berserk", bot)) + casted = true; + } + if (bot->GetPower(POWER_ENERGY) < 30 && botAI->CanCastSpell("tiger's fury", bot)) + { + if (botAI->CastSpell("tiger's fury", bot)) + casted = true; + } + if (bot->GetComboPoints() >= 4 && botAI->CanCastSpell("ferocious bite", innerDemon)) + { + if (botAI->CastSpell("ferocious bite", innerDemon)) + casted = true; + } + if (bot->GetComboPoints() == 0 && innerDemon->GetHealthPct() > 25.0f && + botAI->CanCastSpell("rake", innerDemon)) + { + if (botAI->CastSpell("rake", innerDemon)) + casted = true; + } + if (botAI->CanCastSpell("mangle (cat)", innerDemon)) + { + if (botAI->CastSpell("mangle (cat)", innerDemon)) + casted = true; + } + + return casted; +} + +bool LeotherasTheBlindDestroyInnerDemonAction::HandleHealerStrategy(Unit* innerDemon) +{ + if (bot->getClass() == CLASS_DRUID) + { + if (bot->HasAura(SPELL_TREE_OF_LIFE)) + bot->RemoveAura(SPELL_TREE_OF_LIFE); + + bool casted = false; + if (botAI->CanCastSpell("barkskin", bot)) + { + if (botAI->CastSpell("barkskin", bot)) + casted = true; + } + if (botAI->CanCastSpell("wrath", innerDemon)) + { + if (botAI->CastSpell("wrath", innerDemon)) + casted = true; + } + + return casted; + } + else if (bot->getClass() == CLASS_PALADIN) + { + bool casted = false; + if (botAI->CanCastSpell("avenging wrath", bot)) + { + if (botAI->CastSpell("avenging wrath", bot)) + casted = true; + } + if (botAI->CanCastSpell("consecration", bot)) + { + if (botAI->CastSpell("consecration", bot)) + casted = true; + } + if (botAI->CanCastSpell("exorcism", innerDemon)) + { + if (botAI->CastSpell("exorcism", innerDemon)) + casted = true; + } + if (botAI->CanCastSpell("hammer of wrath", innerDemon)) + { + if (botAI->CastSpell("hammer of wrath", innerDemon)) + casted = true; + } + if (botAI->CanCastSpell("holy shock", innerDemon)) + { + if (botAI->CastSpell("holy shock", innerDemon)) + casted = true; + } + if (botAI->CanCastSpell("judgment of light", innerDemon)) + { + if (botAI->CastSpell("judgment of light", innerDemon)) + casted = true; + } + + return casted; + } + else if (bot->getClass() == CLASS_PRIEST) + { + if (botAI->CanCastSpell("smite", innerDemon)) + return botAI->CastSpell("smite", innerDemon); + } + else if (bot->getClass() == CLASS_SHAMAN) + { + bool casted = false; + if (botAI->CanCastSpell("earth shock", innerDemon)) + { + if (botAI->CastSpell("earth shock", innerDemon)) + casted = true; + } + if (botAI->CanCastSpell("chain lightning", innerDemon)) + { + if (botAI->CastSpell("chain lightning", innerDemon)) + casted = true; + } + if (botAI->CanCastSpell("lightning bolt", innerDemon)) + { + if (botAI->CastSpell("lightning bolt", innerDemon)) + casted = true; + } + + return casted; + } + + return false; +} + +// Everybody except the Warlock tank should focus on Leotheras in Phase 3 +bool LeotherasTheBlindFinalPhaseAssignDpsPriorityAction::Execute(Event /*event*/) +{ + Unit* leotherasHuman = GetLeotherasHuman(botAI); + if (!leotherasHuman) + return false; + + MarkTargetWithStar(bot, leotherasHuman); + SetRtiTarget(botAI, "star", leotherasHuman); + + if (bot->GetTarget() != leotherasHuman->GetGUID()) + return Attack(leotherasHuman); + + Unit* leotherasDemon = GetPhase3LeotherasDemon(botAI); + if (leotherasDemon) + { + if (leotherasHuman->GetVictim() == bot) + { + Unit* demonTarget = leotherasDemon->GetVictim(); + if (demonTarget && leotherasHuman->GetExactDist2d(demonTarget) < 20.0f) + { + float angle = atan2(bot->GetPositionY() - demonTarget->GetPositionY(), + bot->GetPositionX() - demonTarget->GetPositionX()); + float targetX = bot->GetPositionX() + 25.0f * std::cos(angle); + float targetY = bot->GetPositionY() + 25.0f * std::sin(angle); + + return MoveTo(SSC_MAP_ID, targetX, targetY, bot->GetPositionZ(), false, false, + false, false, MovementPriority::MOVEMENT_FORCED, true, false); + } + } + } + + return false; +} + +// Misdirect to Warlock tank or to main tank if there is no Warlock tank +bool LeotherasTheBlindMisdirectBossToDemonFormTankAction::Execute(Event /*event*/) +{ + Unit* leotherasDemon = GetActiveLeotherasDemon(botAI); + if (!leotherasDemon) + return false; + + Player* demonFormTank = GetLeotherasDemonFormTank(bot); + Player* targetTank = demonFormTank; + + if (!targetTank) + { + if (Group* group = bot->GetGroup()) + { + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (member && member->IsAlive() && botAI->IsMainTank(member)) + { + targetTank = member; + break; + } + } + } + } + + if (!targetTank) + return false; + + if (botAI->CanCastSpell("misdirection", targetTank)) + return botAI->CastSpell("misdirection", targetTank); + + if (bot->HasAura(SPELL_MISDIRECTION) && botAI->CanCastSpell("steady shot", leotherasDemon)) + return botAI->CastSpell("steady shot", leotherasDemon); + + return false; +} + +// This does not pause DPS after a Whirlwind, which is also an aggro wipe +bool LeotherasTheBlindManageDpsWaitTimersAction::Execute(Event /*event*/) +{ + Unit* leotheras = AI_VALUE2(Unit*, "find target", "leotheras the blind"); + if (!leotheras) + return false; + + const uint32 instanceId = leotheras->GetMap()->GetInstanceId(); + const time_t now = std::time(nullptr); + + bool changed = false; + // Encounter start/reset: clear all timers + if (leotheras->HasAura(SPELL_LEOTHERAS_BANISHED)) + { + if (leotherasHumanFormDpsWaitTimer.erase(instanceId) > 0) + changed = true; + if (leotherasDemonFormDpsWaitTimer.erase(instanceId) > 0) + changed = true; + if (leotherasFinalPhaseDpsWaitTimer.erase(instanceId) > 0) + changed = true; + } + + // Human Phase + Unit* leotherasHuman = GetLeotherasHuman(botAI); + Unit* leotherasPhase3Demon = GetPhase3LeotherasDemon(botAI); + if (leotherasHuman && !leotherasPhase3Demon) + { + if (leotherasHumanFormDpsWaitTimer.try_emplace(instanceId, now).second) + changed = true; + if (leotherasDemonFormDpsWaitTimer.erase(instanceId) > 0) + changed = true; + } + // Demon Phase + else if (Unit* leotherasPhase2Demon = GetPhase2LeotherasDemon(botAI)) + { + if (leotherasDemonFormDpsWaitTimer.try_emplace(instanceId, now).second) + changed = true; + if (leotherasHumanFormDpsWaitTimer.erase(instanceId) > 0) + changed = true; + } + // Final Phase (<15% HP) + else if (leotherasHuman && leotherasPhase3Demon) + { + if (leotherasFinalPhaseDpsWaitTimer.try_emplace(instanceId, now).second) + changed = true; + if (leotherasHumanFormDpsWaitTimer.erase(instanceId) > 0) + changed = true; + if (leotherasDemonFormDpsWaitTimer.erase(instanceId) > 0) + changed = true; + } + + return changed; +} + +// Fathom-Lord Karathress +// Note: 4 tanks are required for the full strategy, and having at least 2 +// is crucial to separate Caribdis from the others + +// Karathress is tanked near his starting position +bool FathomLordKarathressMainTankPositionBossAction::Execute(Event /*event*/) +{ + Unit* karathress = AI_VALUE2(Unit*, "find target", "fathom-lord karathress"); + if (!karathress) + return false; + + MarkTargetWithTriangle(bot, karathress); + SetRtiTarget(botAI, "triangle", karathress); + + if (bot->GetTarget() != karathress->GetGUID()) + return Attack(karathress); + + if (karathress->GetVictim() == bot && bot->IsWithinMeleeRange(karathress)) + { + const Position& position = KARATHRESS_TANK_POSITION; + float distToPosition = + bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()); + + if (distToPosition > 3.0f) + { + float dX = position.GetPositionX() - bot->GetPositionX(); + float dY = position.GetPositionY() - bot->GetPositionY(); + float moveDist = std::min(5.0f, distToPosition); + float moveX = bot->GetPositionX() + (dX / distToPosition) * moveDist; + float moveY = bot->GetPositionY() + (dY / distToPosition) * moveDist; + + return MoveTo(SSC_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, + false, true, MovementPriority::MOVEMENT_COMBAT, true, true); + } + } + + return false; +} + +// Caribdis is pulled far to the West in the corner +// Best to use a Warrior or Druid tank for interrupts +bool FathomLordKarathressFirstAssistTankPositionCaribdisAction::Execute(Event /*event*/) +{ + Unit* caribdis = AI_VALUE2(Unit*, "find target", "fathom-guard caribdis"); + if (!caribdis) + return false; + + MarkTargetWithDiamond(bot, caribdis); + SetRtiTarget(botAI, "diamond", caribdis); + + if (bot->GetTarget() != caribdis->GetGUID()) + return Attack(caribdis); + + if (caribdis->GetVictim() == bot) + { + const Position& position = CARIBDIS_TANK_POSITION; + float distToPosition = + bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()); + + if (distToPosition > 3.0f) + { + float dX = position.GetPositionX() - bot->GetPositionX(); + float dY = position.GetPositionY() - bot->GetPositionY(); + float moveDist = std::min(10.0f, distToPosition); + float moveX = bot->GetPositionX() + (dX / distToPosition) * moveDist; + float moveY = bot->GetPositionY() + (dY / distToPosition) * moveDist; + + return MoveTo(SSC_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, + false, true, MovementPriority::MOVEMENT_COMBAT, true, false); + } + } + + return false; +} + +// Sharkkis is pulled North to the other side of the ramp +bool FathomLordKarathressSecondAssistTankPositionSharkkisAction::Execute(Event /*event*/) +{ + Unit* sharkkis = AI_VALUE2(Unit*, "find target", "fathom-guard sharkkis"); + if (!sharkkis) + return false; + + MarkTargetWithStar(bot, sharkkis); + SetRtiTarget(botAI, "star", sharkkis); + + if (bot->GetTarget() != sharkkis->GetGUID()) + return Attack(sharkkis); + + if (sharkkis->GetVictim() == bot && bot->IsWithinMeleeRange(sharkkis)) + { + const Position& position = SHARKKIS_TANK_POSITION; + float distToPosition = + bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()); + + if (distToPosition > 3.0f) + { + float dX = position.GetPositionX() - bot->GetPositionX(); + float dY = position.GetPositionY() - bot->GetPositionY(); + float moveDist = std::min(10.0f, distToPosition); + float moveX = bot->GetPositionX() + (dX / distToPosition) * moveDist; + float moveY = bot->GetPositionY() + (dY / distToPosition) * moveDist; + + return MoveTo(SSC_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, + false, true, MovementPriority::MOVEMENT_COMBAT, true, true); + } + } + + return false; +} + +// Tidalvess is pulled Northwest near the pillar +bool FathomLordKarathressThirdAssistTankPositionTidalvessAction::Execute(Event /*event*/) +{ + Unit* tidalvess = AI_VALUE2(Unit*, "find target", "fathom-guard tidalvess"); + if (!tidalvess) + return false; + + MarkTargetWithCircle(bot, tidalvess); + SetRtiTarget(botAI, "circle", tidalvess); + + if (bot->GetTarget() != tidalvess->GetGUID()) + return Attack(tidalvess); + + if (tidalvess->GetVictim() == bot && bot->IsWithinMeleeRange(tidalvess)) + { + const Position& position = TIDALVESS_TANK_POSITION; + float distToPosition = + bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()); + + if (distToPosition > 3.0f) + { + float dX = position.GetPositionX() - bot->GetPositionX(); + float dY = position.GetPositionY() - bot->GetPositionY(); + float moveDist = std::min(10.0f, distToPosition); + float moveX = bot->GetPositionX() + (dX / distToPosition) * moveDist; + float moveY = bot->GetPositionY() + (dY / distToPosition) * moveDist; + + return MoveTo(SSC_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, + false, true, MovementPriority::MOVEMENT_COMBAT, true, true); + } + } + + return false; +} + +// Caribdis's tank spot is far away so a dedicated healer is needed +// Use the assistant flag to select the healer +bool FathomLordKarathressPositionCaribdisTankHealerAction::Execute(Event /*event*/) +{ + Unit* caribdis = AI_VALUE2(Unit*, "find target", "fathom-guard caribdis"); + if (!caribdis) + return false; + + const Position& position = CARIBDIS_HEALER_POSITION; + float distToPosition = + bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()); + + if (distToPosition > 3.0f) + { + float dX = position.GetPositionX() - bot->GetPositionX(); + float dY = position.GetPositionY() - bot->GetPositionY(); + float moveDist = std::min(10.0f, distToPosition); + float moveX = bot->GetPositionX() + (dX / distToPosition) * moveDist; + float moveY = bot->GetPositionY() + (dY / distToPosition) * moveDist; + + return MoveTo(SSC_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, + false, true, MovementPriority::MOVEMENT_COMBAT, true, false); + } + + return false; +} + +// Misdirect priority: (1) Caribdis tank, (2) Tidalvess tank, (3) Sharkkis tank +bool FathomLordKarathressMisdirectBossesToTanksAction::Execute(Event /*event*/) +{ + Group* group = bot->GetGroup(); + if (!group) + return false; + + std::vector hunters; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (member && member->IsAlive() && member->getClass() == CLASS_HUNTER && + GET_PLAYERBOT_AI(member)) + hunters.push_back(member); + + if (hunters.size() >= 3) + break; + } + + int hunterIndex = -1; + for (size_t i = 0; i < hunters.size(); ++i) + { + if (hunters[i] == bot) + { + hunterIndex = static_cast(i); + break; + } + } + if (hunterIndex == -1) + return false; + + Unit* bossTarget = nullptr; + Player* tankTarget = nullptr; + if (hunterIndex == 0) + { + bossTarget = AI_VALUE2(Unit*, "find target", "fathom-guard caribdis"); + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (member && member->IsAlive() && botAI->IsAssistTankOfIndex(member, 0, false)) + { + tankTarget = member; + break; + } + } + } + else if (hunterIndex == 1) + { + bossTarget = AI_VALUE2(Unit*, "find target", "fathom-guard tidalvess"); + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (member && member->IsAlive() && botAI->IsAssistTankOfIndex(member, 2, false)) + { + tankTarget = member; + break; + } + } + } + else if (hunterIndex == 2) + { + bossTarget = AI_VALUE2(Unit*, "find target", "fathom-guard sharkkis"); + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (member && member->IsAlive() && botAI->IsAssistTankOfIndex(member, 1, false)) + { + tankTarget = member; + break; + } + } + } + + if (!bossTarget || !tankTarget) + return false; + + if (botAI->CanCastSpell("misdirection", tankTarget)) + return botAI->CastSpell("misdirection", tankTarget); + + if (bot->HasAura(SPELL_MISDIRECTION) && botAI->CanCastSpell("steady shot", bossTarget)) + return botAI->CastSpell("steady shot", bossTarget); + + return false; +} + +// Kill order is non-standard because bots handle Cyclones poorly and need more time +// to get her down than real players (standard is ranged DPS help with Sharkkis first) +bool FathomLordKarathressAssignDpsPriorityAction::Execute(Event /*event*/) +{ + // Target priority 1: Spitfire Totems for melee dps + Unit* totem = GetFirstAliveUnitByEntry(botAI, NPC_SPITFIRE_TOTEM); + if (totem && botAI->IsMelee(bot) && botAI->IsDps(bot)) + { + MarkTargetWithSkull(bot, totem); + SetRtiTarget(botAI, "skull", totem); + + if (bot->GetTarget() != totem->GetGUID()) + return Attack(totem); + + // Direct movement order due to path between Sharkkis and totem sometimes being screwy + if (!bot->IsWithinMeleeRange(totem)) + { + return MoveTo(SSC_MAP_ID, totem->GetPositionX(), totem->GetPositionY(), + totem->GetPositionZ(), false, false, false, true, + MovementPriority::MOVEMENT_COMBAT, true, false); + } + + return false; + } + + // Target priority 2: Tidalvess for all dps + Unit* tidalvess = AI_VALUE2(Unit*, "find target", "fathom-guard tidalvess"); + if (tidalvess) + { + MarkTargetWithCircle(bot, tidalvess); + SetRtiTarget(botAI, "circle", tidalvess); + + if (bot->GetTarget() != tidalvess->GetGUID()) + return Attack(tidalvess); + + return false; + } + + // Target priority 3: Caribdis for ranged dps + Unit* caribdis = AI_VALUE2(Unit*, "find target", "fathom-guard caribdis"); + if (botAI->IsRangedDps(bot) && caribdis) + { + MarkTargetWithDiamond(bot, caribdis); + SetRtiTarget(botAI, "diamond", caribdis); + + const Position& position = CARIBDIS_RANGED_DPS_POSITION; + if (bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()) > 2.0f) + { + return MoveInside(SSC_MAP_ID, position.GetPositionX(), position.GetPositionY(), + position.GetPositionZ(), 8.0f, MovementPriority::MOVEMENT_COMBAT); + } + + if (bot->GetTarget() != caribdis->GetGUID()) + return Attack(caribdis); + + return false; + } + + // Target priority 4: Sharkkis for melee dps and, after Caribdis is down, ranged dps also + Unit* sharkkis = AI_VALUE2(Unit*, "find target", "fathom-guard sharkkis"); + if (sharkkis) + { + MarkTargetWithStar(bot, sharkkis); + SetRtiTarget(botAI, "star", sharkkis); + + if (bot->GetTarget() != sharkkis->GetGUID()) + return Attack(sharkkis); + + return false; + } + + // Target priority 5: Sharkkis pets for all dps + Unit* fathomSporebat = AI_VALUE2(Unit*, "find target", "fathom sporebat"); + if (fathomSporebat && botAI->IsMelee(bot)) + { + MarkTargetWithCross(bot, fathomSporebat); + SetRtiTarget(botAI, "cross", fathomSporebat); + + if (bot->GetTarget() != fathomSporebat->GetGUID()) + return Attack(fathomSporebat); + + return false; + } + + Unit* fathomLurker = AI_VALUE2(Unit*, "find target", "fathom lurker"); + if (fathomLurker && botAI->IsMelee(bot)) + { + MarkTargetWithSquare(bot, fathomLurker); + SetRtiTarget(botAI, "square", fathomLurker); + + if (bot->GetTarget() != fathomLurker->GetGUID()) + return Attack(fathomLurker); + + return false; + } + + // Target priority 6: Karathress for all dps + Unit* karathress = AI_VALUE2(Unit*, "find target", "fathom-lord karathress"); + if (karathress) + { + MarkTargetWithTriangle(bot, karathress); + SetRtiTarget(botAI, "triangle", karathress); + + if (bot->GetTarget() != karathress->GetGUID()) + return Attack(karathress); + } + + return false; +} + +bool FathomLordKarathressManageDpsTimerAction::Execute(Event /*event*/) +{ + Unit* karathress = AI_VALUE2(Unit*, "find target", "fathom-lord karathress"); + if (!karathress) + return false; + + karathressDpsWaitTimer.try_emplace( + karathress->GetMap()->GetInstanceId(), std::time(nullptr)); + + return false; +} + +// Morogrim Tidewalker + +bool MorogrimTidewalkerMisdirectBossToMainTankAction::Execute(Event /*event*/) +{ + Unit* tidewalker = AI_VALUE2(Unit*, "find target", "morogrim tidewalker"); + if (!tidewalker) + return false; + + Player* mainTank = nullptr; + if (Group* group = bot->GetGroup()) + { + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (member && member->IsAlive() && botAI->IsMainTank(member)) + { + mainTank = member; + break; + } + } + } + + if (!mainTank) + return false; + + if (botAI->CanCastSpell("misdirection", mainTank)) + return botAI->CastSpell("misdirection", mainTank); + + if (bot->HasAura(SPELL_MISDIRECTION) && botAI->CanCastSpell("steady shot", tidewalker)) + return botAI->CastSpell("steady shot", tidewalker); + + return false; +} + +// Separate tanking positions are used for phase 1 and phase 2 to address the +// Water Globule mechanic in phase 2 +bool MorogrimTidewalkerMoveBossToTankPositionAction::Execute(Event /*event*/) +{ + Unit* tidewalker = AI_VALUE2(Unit*, "find target", "morogrim tidewalker"); + if (!tidewalker) + return false; + + if (bot->GetTarget() != tidewalker->GetGUID()) + return Attack(tidewalker); + + if (tidewalker->GetVictim() == bot && bot->IsWithinMeleeRange(tidewalker)) + { + if (tidewalker->GetHealthPct() > 26.0f) + return MoveToPhase1TankPosition(tidewalker); + else + return MoveToPhase2TankPosition(tidewalker); + } + + return false; +} + +// Phase 1: tank position is up against the Northeast pillar +bool MorogrimTidewalkerMoveBossToTankPositionAction::MoveToPhase1TankPosition(Unit* tidewalker) +{ + const Position& phase1 = TIDEWALKER_PHASE_1_TANK_POSITION; + float distToPhase1 = bot->GetExactDist2d(phase1.GetPositionX(), phase1.GetPositionY()); + if (distToPhase1 > 1.0f) + { + float dX = phase1.GetPositionX() - bot->GetPositionX(); + float dY = phase1.GetPositionY() - bot->GetPositionY(); + float moveDist = std::min(5.0f, distToPhase1); + float moveX = bot->GetPositionX() + (dX / distToPhase1) * moveDist; + float moveY = bot->GetPositionY() + (dY / distToPhase1) * moveDist; + + return MoveTo(SSC_MAP_ID, moveX, moveY, phase1.GetPositionZ(), false, false, + false, false, MovementPriority::MOVEMENT_COMBAT, true, true); + } + + return false; +} + +// Phase 2: move in two steps to get around the pillar and back up into the Northeast corner +bool MorogrimTidewalkerMoveBossToTankPositionAction::MoveToPhase2TankPosition(Unit* tidewalker) +{ + const Position& phase2 = TIDEWALKER_PHASE_2_TANK_POSITION; + const Position& transition = TIDEWALKER_PHASE_TRANSITION_WAYPOINT; + + auto itStep = tidewalkerTankStep.find(bot->GetGUID()); + uint8 step = (itStep != tidewalkerTankStep.end()) ? itStep->second : 0; + + if (step == 0) + { + float distToTransition = + bot->GetExactDist2d(transition.GetPositionX(), transition.GetPositionY()); + + if (distToTransition > 2.0f) + { + float dX = transition.GetPositionX() - bot->GetPositionX(); + float dY = transition.GetPositionY() - bot->GetPositionY(); + float moveDist = std::min(5.0f, distToTransition); + float moveX = bot->GetPositionX() + (dX / distToTransition) * moveDist; + float moveY = bot->GetPositionY() + (dY / distToTransition) * moveDist; + + return MoveTo(SSC_MAP_ID, moveX, moveY, transition.GetPositionZ(), false, false, + false, false, MovementPriority::MOVEMENT_COMBAT, true, true); + } + else + tidewalkerTankStep.try_emplace(bot->GetGUID(), 1); + } + + if (step == 1) + { + float distToPhase2 = + bot->GetExactDist2d(phase2.GetPositionX(), phase2.GetPositionY()); + + if (distToPhase2 > 1.0f) + { + float dX = phase2.GetPositionX() - bot->GetPositionX(); + float dY = phase2.GetPositionY() - bot->GetPositionY(); + float moveDist = std::min(5.0f, distToPhase2); + float moveX = bot->GetPositionX() + (dX / distToPhase2) * moveDist; + float moveY = bot->GetPositionY() + (dY / distToPhase2) * moveDist; + + return MoveTo(SSC_MAP_ID, moveX, moveY, phase2.GetPositionZ(), false, false, + false, false, MovementPriority::MOVEMENT_COMBAT, true, true); + } + } + + return false; +} + +// Ranged stack behind the boss in the Northeast corner in phase 2 +// No corresponding method for melee since they will do so anyway +bool MorogrimTidewalkerPhase2RepositionRangedAction::Execute(Event /*event*/) +{ + Unit* tidewalker = AI_VALUE2(Unit*, "find target", "morogrim tidewalker"); + if (!tidewalker) + return false; + + const Position& phase2 = TIDEWALKER_PHASE_2_RANGED_POSITION; + const Position& transition = TIDEWALKER_PHASE_TRANSITION_WAYPOINT; + + auto itStep = tidewalkerRangedStep.find(bot->GetGUID()); + uint8 step = (itStep != tidewalkerRangedStep.end()) ? itStep->second : 0; + + if (step == 0) + { + float distToTransition = + bot->GetExactDist2d(transition.GetPositionX(), transition.GetPositionY()); + + if (distToTransition > 2.0f) + { + float dX = transition.GetPositionX() - bot->GetPositionX(); + float dY = transition.GetPositionY() - bot->GetPositionY(); + float moveDist = std::min(10.0f, distToTransition); + float moveX = bot->GetPositionX() + (dX / distToTransition) * moveDist; + float moveY = bot->GetPositionY() + (dY / distToTransition) * moveDist; + + return MoveTo(SSC_MAP_ID, moveX, moveY, transition.GetPositionZ(), false, false, + false, false, MovementPriority::MOVEMENT_COMBAT, true, false); + } + else + { + tidewalkerRangedStep.try_emplace(bot->GetGUID(), 1); + step = 1; + } + } + + if (step == 1) + { + float distToPhase2 = + bot->GetExactDist2d(phase2.GetPositionX(), phase2.GetPositionY()); + + if (distToPhase2 > 1.0f) + { + float dX = phase2.GetPositionX() - bot->GetPositionX(); + float dY = phase2.GetPositionY() - bot->GetPositionY(); + float moveDist = std::min(10.0f, distToPhase2); + float moveX = bot->GetPositionX() + (dX / distToPhase2) * moveDist; + float moveY = bot->GetPositionY() + (dY / distToPhase2) * moveDist; + + return MoveTo(SSC_MAP_ID, moveX, moveY, phase2.GetPositionZ(), false, false, + false, false, MovementPriority::MOVEMENT_COMBAT, true, false); + } + } + + return false; +} + +// Lady Vashj + +bool LadyVashjMainTankPositionBossAction::Execute(Event /*event*/) +{ + Unit* vashj = AI_VALUE2(Unit*, "find target", "lady vashj"); + if (!vashj) + return false; + + if (bot->GetTarget() != vashj->GetGUID()) + return Attack(vashj); + + if (vashj->GetVictim() == bot && bot->IsWithinMeleeRange(vashj)) + { + // Phase 1: Position Vashj in the center of the platform + if (IsLadyVashjInPhase1(botAI)) + { + const Position& position = VASHJ_PLATFORM_CENTER_POSITION; + float distToPosition = + bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()); + + if (distToPosition > 2.0f) + { + float dX = position.GetPositionX() - bot->GetPositionX(); + float dY = position.GetPositionY() - bot->GetPositionY(); + float moveDist = std::min(5.0f, distToPosition); + float moveX = bot->GetPositionX() + (dX / distToPosition) * moveDist; + float moveY = bot->GetPositionY() + (dY / distToPosition) * moveDist; + + return MoveTo(SSC_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, + false, false, MovementPriority::MOVEMENT_COMBAT, true, true); + } + } + // Phase 3: No fixed position, but move Vashj away from Enchanted Elementals + else if (IsLadyVashjInPhase3(botAI)) + { + Unit* enchanted = AI_VALUE2(Unit*, "find target", "enchanted elemental"); + if (enchanted) + { + float currentDistance = bot->GetExactDist2d(enchanted); + constexpr float safeDistance = 10.0f; + if (currentDistance < safeDistance) + return MoveAway(enchanted, safeDistance - currentDistance); + } + } + } + + return false; +} + +// Semicircle around center of the room (to allow escape paths by Static Charged bots) +bool LadyVashjPhase1SpreadRangedInArcAction::Execute(Event /*event*/) +{ + std::vector spreadMembers; + if (Group* group = bot->GetGroup()) + { + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (member && member->IsAlive() && GET_PLAYERBOT_AI(member)) + { + if (botAI->IsRanged(member)) + spreadMembers.push_back(member); + } + } + } + + const ObjectGuid guid = bot->GetGUID(); + + auto itPos = vashjRangedPositions.find(guid); + auto itReached = hasReachedVashjRangedPosition.find(guid); + if (itPos == vashjRangedPositions.end()) + { + auto it = std::find(spreadMembers.begin(), spreadMembers.end(), bot); + size_t botIndex = (it != spreadMembers.end()) ? + std::distance(spreadMembers.begin(), it) : 0; + size_t count = spreadMembers.size(); + if (count == 0) + return false; + + const Position& center = VASHJ_PLATFORM_CENTER_POSITION; + constexpr float minRadius = 20.0f; + constexpr float maxRadius = 30.0f; + + constexpr float arcCenter = M_PI / 2.0f; // North + constexpr float arcSpan = M_PI; // 180° + constexpr float arcStart = arcCenter - arcSpan / 2.0f; + + float angle; + if (count == 1) + angle = arcCenter; + else + angle = arcStart + (static_cast(botIndex) / (count - 1)) * arcSpan; + + float radius = frand(minRadius, maxRadius); + float targetX = center.GetPositionX() + radius * std::cos(angle); + float targetY = center.GetPositionY() + radius * std::sin(angle); + + auto res = vashjRangedPositions.try_emplace(guid, Position(targetX, targetY, center.GetPositionZ())); + itPos = res.first; + hasReachedVashjRangedPosition.try_emplace(guid, false); + itReached = hasReachedVashjRangedPosition.find(guid); + } + + if (itPos == vashjRangedPositions.end()) + return false; + + Position position = itPos->second; + if (itReached == hasReachedVashjRangedPosition.end() || !(itReached->second)) + { + if (bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()) > 2.0f) + { + return MoveTo(SSC_MAP_ID, position.GetPositionX(), position.GetPositionY(), + position.GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_COMBAT, true, false); + } + if (itReached != hasReachedVashjRangedPosition.end()) + itReached->second = true; + } + + return false; +} + +// For absorbing Shock Burst +bool LadyVashjSetGroundingTotemInMainTankGroupAction::Execute(Event /*event*/) +{ + Player* mainTank = nullptr; + if (Group* group = bot->GetGroup()) + { + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (member && member->IsAlive() && botAI->IsMainTank(member)) + { + mainTank = member; + break; + } + } + } + + if (!mainTank) + return false; + + if (bot->GetExactDist2d(mainTank) > 25.0f) + { + return MoveInside(SSC_MAP_ID, mainTank->GetPositionX(), mainTank->GetPositionY(), + mainTank->GetPositionZ(), 20.0f, MovementPriority::MOVEMENT_COMBAT); + } + + if (!botAI->HasStrategy("grounding", BotState::BOT_STATE_COMBAT)) + botAI->ChangeStrategy("+grounding", BotState::BOT_STATE_COMBAT); + + if (!bot->HasAura(SPELL_GROUNDING_TOTEM_EFFECT) && + botAI->CanCastSpell("grounding totem", bot)) + return botAI->CastSpell("grounding totem", bot); + + return false; +} + +bool LadyVashjMisdirectBossToMainTankAction::Execute(Event /*event*/) +{ + Unit* vashj = AI_VALUE2(Unit*, "find target", "lady vashj"); + if (!vashj) + return false; + + Player* mainTank = nullptr; + if (Group* group = bot->GetGroup()) + { + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (member && member->IsAlive() && botAI->IsMainTank(member)) + { + mainTank = member; + break; + } + } + } + + if (!mainTank) + return false; + + if (botAI->CanCastSpell("misdirection", mainTank)) + return botAI->CastSpell("misdirection", mainTank); + + if (bot->HasAura(SPELL_MISDIRECTION) && botAI->CanCastSpell("steady shot", vashj)) + return botAI->CastSpell("steady shot", vashj); + + return false; +} + +bool LadyVashjStaticChargeMoveAwayFromGroupAction::Execute(Event /*event*/) +{ + Group* group = bot->GetGroup(); + if (!group) + return false; + + // If the main tank has Static Charge, other group members should move away + Player* mainTank = nullptr; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (member && member->IsAlive() && botAI->IsMainTank(member) && + member->HasAura(SPELL_STATIC_CHARGE)) + { + mainTank = member; + break; + } + } + + if (mainTank && bot != mainTank) + { + float currentDistance = bot->GetExactDist2d(mainTank); + constexpr float safeDistance = 11.0f; + if (currentDistance < safeDistance) + return MoveAway(mainTank, safeDistance - currentDistance); + } + + // If any other bot has Static Charge, it should move away from other group members + if (!botAI->IsMainTank(bot) && bot->HasAura(SPELL_STATIC_CHARGE)) + { + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive() || member == bot) + continue; + + float currentDistance = bot->GetExactDist2d(member); + constexpr float safeDistance = 11.0f; + if (currentDistance < safeDistance) + return MoveFromGroup(safeDistance); + } + } + + return false; +} + +bool LadyVashjAssignPhase2AndPhase3DpsPriorityAction::Execute(Event /*event*/) +{ + Unit* vashj = AI_VALUE2(Unit*, "find target", "lady vashj"); + if (!vashj) + return false; + + const Position& center = VASHJ_PLATFORM_CENTER_POSITION; + float platformZ = center.GetPositionZ(); + if (bot->GetPositionZ() - platformZ > 2.0f) + { + // This block is needed to prevent bots from floating into the air to attack sporebats + bot->AttackStop(); + bot->InterruptNonMeleeSpells(true); + bot->StopMoving(); + bot->GetMotionMaster()->Clear(); + bot->TeleportTo(SSC_MAP_ID, bot->GetPositionX(), bot->GetPositionY(), + platformZ, bot->GetOrientation()); + return true; + } + + auto const& attackers = + botAI->GetAiObjectContext()->GetValue("nearest hostile npcs")->Get(); + Unit* target = nullptr; + Unit* enchanted = nullptr; + Unit* elite = nullptr; + Unit* strider = nullptr; + Unit* sporebat = nullptr; + + // Search and attack radius are intended to keep bots from going down the stairs + const float maxSearchRange = + botAI->IsRanged(bot) ? 60.0f : 55.0f; + const float maxPursueRange = maxSearchRange - 5.0f; + + for (auto guid : attackers) + { + Unit* unit = botAI->GetUnit(guid); + if (!IsValidLadyVashjCombatNpc(unit, botAI)) + continue; + + float distFromCenter = unit->GetExactDist2d(center.GetPositionX(), center.GetPositionY()); + if (IsLadyVashjInPhase2(botAI) && distFromCenter > maxSearchRange) + continue; + + switch (unit->GetEntry()) + { + case NPC_ENCHANTED_ELEMENTAL: + if (!enchanted || vashj->GetExactDist2d(unit) < vashj->GetExactDist2d(enchanted)) + enchanted = unit; + break; + + case NPC_COILFANG_ELITE: + if (!elite || unit->GetHealthPct() < elite->GetHealthPct()) + elite = unit; + break; + + case NPC_COILFANG_STRIDER: + if (!strider || unit->GetHealthPct() < strider->GetHealthPct()) + strider = unit; + break; + + case NPC_TOXIC_SPOREBAT: + if (!sporebat || bot->GetExactDist2d(unit) < bot->GetExactDist2d(sporebat)) + sporebat = unit; + break; + + case NPC_LADY_VASHJ: + vashj = unit; + break; + + default: + break; + } + } + + std::vector targets; + if (IsLadyVashjInPhase2(botAI)) + { + if (botAI->IsRanged(bot)) + { + // Hunters and Mages prioritize Enchanted Elementals, + // while other ranged DPS prioritize Striders + if (bot->getClass() == CLASS_HUNTER || bot->getClass() == CLASS_MAGE) + targets = { enchanted, strider, elite }; + else + targets = { strider, elite, enchanted }; + } + else if (botAI->IsMelee(bot) && botAI->IsDps(bot)) + targets = { enchanted, elite }; + else if (botAI->IsTank(bot)) + { + if (botAI->HasCheat(BotCheatMask::raid) && botAI->IsAssistTankOfIndex(bot, 0, true)) + targets = { strider, elite, enchanted }; + else + targets = { elite, strider, enchanted }; + } + else + targets = { enchanted, elite, strider }; + } + + if (IsLadyVashjInPhase3(botAI)) + { + if (botAI->IsTank(bot)) + { + if (botAI->IsMainTank(bot)) + { + MarkTargetWithDiamond(bot, vashj); + SetRtiTarget(botAI, "diamond", vashj); + targets = { vashj }; + } + else if (botAI->IsAssistTankOfIndex(bot, 0, true)) + { + if (botAI->HasCheat(BotCheatMask::raid)) + targets = { strider, elite, enchanted, vashj }; + } + else + targets = { elite, strider, enchanted, vashj }; + } + else if (botAI->IsRanged(bot)) + { + // Hunters are assigned to kill Sporebats in Phase 3 + if (bot->getClass() == CLASS_HUNTER) + targets = { sporebat, enchanted, strider, elite, vashj }; + else + targets = { enchanted, strider, elite, vashj }; + } + else if (botAI->IsMelee(bot) && botAI->IsDps(bot)) + targets = { enchanted, elite, vashj }; + else + targets = { enchanted, elite, strider, vashj }; + } + + for (Unit* candidate : targets) + { + if (candidate && bot->GetExactDist2d(candidate) <= maxPursueRange) + { + target = candidate; + break; + } + } + + Unit* currentTarget = context->GetValue("current target")->Get(); + + if (currentTarget && !IsValidLadyVashjCombatNpc(currentTarget, botAI)) + { + bot->AttackStop(); + bot->InterruptNonMeleeSpells(true); + context->GetValue("current target")->Set(nullptr); + bot->SetTarget(ObjectGuid::Empty); + bot->SetSelection(ObjectGuid()); + currentTarget = nullptr; + } + + if (target && currentTarget != target && bot->GetTarget() != target->GetGUID()) + return Attack(target); + + // If bots have wandered too far from the center, move them back + if (bot->GetExactDist2d(center.GetPositionX(), center.GetPositionY()) > 55.0f) + { + Player* designatedLooter = GetDesignatedCoreLooter(bot->GetGroup(), botAI); + Player* firstCorePasser = GetFirstTaintedCorePasser(bot->GetGroup(), botAI); + // A bot will not move back to the middle if (1) there is a Tainted Elemental, and + // (2) the bot is either the designated looter or the first core passer + if (Unit* tainted = AI_VALUE2(Unit*, "find target", "tainted elemental")) + { + if ((designatedLooter && designatedLooter == bot) || + (firstCorePasser && firstCorePasser == bot)) + return false; + } + + return MoveInside(SSC_MAP_ID, center.GetPositionX(), center.GetPositionY(), + center.GetPositionZ(), 40.0f, MovementPriority::MOVEMENT_COMBAT); + } + + return false; +} + +bool LadyVashjMisdirectStriderToFirstAssistTankAction::Execute(Event /*event*/) +{ + // Striders are not tankable without a cheat to block Fear so there is + // no point in misdirecting if raid cheats are not enabled + if (!botAI->HasCheat(BotCheatMask::raid)) + return false; + + if (bot->getClass() != CLASS_HUNTER) + return false; + + Unit* strider = GetFirstAliveUnitByEntry(botAI, NPC_COILFANG_STRIDER); + if (!strider) + return false; + + Player* firstAssistTank = nullptr; + if (Group* group = bot->GetGroup()) + { + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (member && botAI->IsAssistTankOfIndex(member, 0, true)) + { + firstAssistTank = member; + break; + } + } + } + + if (!firstAssistTank || strider->GetVictim() == firstAssistTank) + return false; + + if (botAI->CanCastSpell("misdirection", firstAssistTank)) + return botAI->CastSpell("misdirection", firstAssistTank); + + if (bot->HasAura(SPELL_MISDIRECTION) && botAI->CanCastSpell("steady shot", strider)) + return botAI->CastSpell("steady shot", strider); + + return false; +} + +bool LadyVashjTankAttackAndMoveAwayStriderAction::Execute(Event /*event*/) +{ + Unit* vashj = AI_VALUE2(Unit*, "find target", "lady vashj"); + if (!vashj) + return false; + + Unit* strider = GetFirstAliveUnitByEntry(botAI, NPC_COILFANG_STRIDER); + if (!strider) + return false; + + // Raid cheat automatically applies Fear Ward to tanks to make Strider tankable + // This simulates the real-life strategy where the Strider can be meleed by + // players wearing an Ogre Suit (due to the extended combat reach) + if (botAI->HasCheat(BotCheatMask::raid) && botAI->IsTank(bot)) + { + if (!bot->HasAura(SPELL_FEAR_WARD)) + bot->AddAura(SPELL_FEAR_WARD, bot); + + if (botAI->IsAssistTankOfIndex(bot, 0, true) && + bot->GetTarget() != strider->GetGUID()) + return Attack(strider); + + if (strider->GetVictim() == bot) + { + float currentDistance = bot->GetExactDist2d(vashj); + constexpr float safeDistance = 28.0f; + + if (currentDistance < safeDistance) + return MoveAway(vashj, safeDistance - currentDistance); + } + + return false; + } + + // Don't move away if raid cheats are enabled, or in any case if the bot is a tank + if (!botAI->HasCheat(BotCheatMask::raid) || !botAI->IsTank(bot)) + { + float currentDistance = bot->GetExactDist2d(strider); + constexpr float safeDistance = 20.0f; + if (currentDistance < safeDistance) + return MoveAway(strider, safeDistance - currentDistance); + } + + // Try to root/slow the Strider if it is not tankable (poor man's kiting strategy) + if (!botAI->HasCheat(BotCheatMask::raid)) + { + if (!strider->HasAura(SPELL_HEAVY_NETHERWEAVE_NET)) + { + Item* net = bot->GetItemByEntry(ITEM_HEAVY_NETHERWEAVE_NET); + if (net && botAI->HasItemInInventory(ITEM_HEAVY_NETHERWEAVE_NET) && + botAI->CanCastSpell("heavy netherweave net", strider)) + return botAI->CastSpell("heavy netherweave net", strider); + } + + if (!botAI->HasAura("frost shock", strider) && bot->getClass() == CLASS_SHAMAN && + botAI->CanCastSpell("frost shock", strider)) + return botAI->CastSpell("frost shock", strider); + + if (!strider->HasAura(SPELL_CURSE_OF_EXHAUSTION) && bot->getClass() == CLASS_WARLOCK && + botAI->CanCastSpell("curse of exhaustion", strider)) + return botAI->CastSpell("curse of exhaustion", strider); + + if (!strider->HasAura(SPELL_SLOW) && bot->getClass() == CLASS_MAGE && + botAI->CanCastSpell("slow", strider)) + return botAI->CastSpell("slow", strider); + } + + return false; +} + +// If cheats are enabled, the first returned melee DPS bot will teleport to Tainted Elementals +// Such bot will recover HP and remove the Poison Bolt debuff while attacking the elemental +bool LadyVashjTeleportToTaintedElementalAction::Execute(Event /*event*/) +{ + Unit* tainted = AI_VALUE2(Unit*, "find target", "tainted elemental"); + if (!tainted) + return false; + + if (bot->GetExactDist2d(tainted) >= 10.0f) + { + bot->AttackStop(); + bot->InterruptNonMeleeSpells(true); + bot->TeleportTo(SSC_MAP_ID, tainted->GetPositionX(), tainted->GetPositionY(), + tainted->GetPositionZ(), tainted->GetOrientation()); + } + + if (bot->GetTarget() != tainted->GetGUID()) + { + MarkTargetWithStar(bot, tainted); + SetRtiTarget(botAI, "star", tainted); + return Attack(tainted); + } + + if (bot->GetExactDist2d(tainted) < 5.0f) + { + bot->SetFullHealth(); + bot->RemoveAura(SPELL_POISON_BOLT); + } + + return false; +} + +bool LadyVashjLootTaintedCoreAction::Execute(Event) +{ + Unit* vashj = AI_VALUE2(Unit*, "find target", "lady vashj"); + if (!vashj) + return false; + + auto const& corpses = context->GetValue("nearest corpses")->Get(); + const float maxLootRange = sPlayerbotAIConfig.lootDistance; + + for (auto const& guid : corpses) + { + LootObject loot(bot, guid); + if (!loot.IsLootPossible(bot)) + continue; + + WorldObject* object = loot.GetWorldObject(bot); + if (!object) + continue; + + Creature* creature = object->ToCreature(); + if (!creature || creature->GetEntry() != NPC_TAINTED_ELEMENTAL || creature->IsAlive()) + continue; + + context->GetValue("loot target")->Set(loot); + + float dist = bot->GetDistance(object); + if (dist > maxLootRange) + return MoveTo(object, 2.0f, MovementPriority::MOVEMENT_FORCED); + + OpenLootAction open(botAI); + if (!open.Execute(Event())) + return false; + + if (Group* group = bot->GetGroup()) + { + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (member && member->HasItemCount(ITEM_TAINTED_CORE, 1, false)) + return true; + } + } + + const ObjectGuid botGuid = bot->GetGUID(); + const ObjectGuid corpseGuid = guid; + constexpr uint8 coreIndex = 0; + + botAI->AddTimedEvent([botGuid, corpseGuid, coreIndex, vashj]() + { + Player* receiver = botGuid.IsEmpty() ? nullptr : ObjectAccessor::FindPlayer(botGuid); + if (!receiver) + return; + + if (Group* group = receiver->GetGroup()) + { + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (member && member->HasItemCount(ITEM_TAINTED_CORE, 1, false)) + return; + } + } + + receiver->SetLootGUID(corpseGuid); + + WorldPacket* packet = new WorldPacket(CMSG_AUTOSTORE_LOOT_ITEM, 1); + *packet << coreIndex; + receiver->GetSession()->QueuePacket(packet); + + const uint32 instanceId = vashj->GetMap()->GetInstanceId(); + const time_t now = std::time(nullptr); + lastCoreInInventoryTime.insert_or_assign(instanceId, now); + }, 600); + + return true; + } + + return false; +} + +bool LadyVashjPassTheTaintedCoreAction::Execute(Event /*event*/) +{ + Unit* vashj = AI_VALUE2(Unit*, "find target", "lady vashj"); + if (!vashj) + return false; + + Group* group = bot->GetGroup(); + if (!group) + return false; + + Player* designatedLooter = GetDesignatedCoreLooter(group, botAI); + if (!designatedLooter) + return false; + + Player* firstCorePasser = GetFirstTaintedCorePasser(group, botAI); + Player* secondCorePasser = GetSecondTaintedCorePasser(group, botAI); + Player* thirdCorePasser = GetThirdTaintedCorePasser(group, botAI); + Player* fourthCorePasser = GetFourthTaintedCorePasser(group, botAI); + const uint32 instanceId = vashj->GetMap()->GetInstanceId(); + + Unit* closestTrigger = nullptr; + if (Unit* tainted = AI_VALUE2(Unit*, "find target", "tainted elemental")) + { + closestTrigger = GetNearestActiveShieldGeneratorTriggerByEntry(tainted); + if (closestTrigger) + nearestTriggerGuid.insert_or_assign(instanceId, closestTrigger->GetGUID()); + } + + auto itSnap = nearestTriggerGuid.find(instanceId); + if (itSnap != nearestTriggerGuid.end() && !itSnap->second.IsEmpty()) + { + Unit* snapUnit = botAI->GetUnit(itSnap->second); + if (snapUnit) + closestTrigger = snapUnit; + else + nearestTriggerGuid.erase(instanceId); + } + + if (!firstCorePasser || !secondCorePasser || !thirdCorePasser || + !fourthCorePasser || !closestTrigger) + return false; + + // Not gated behind CheatMask because the auto application of Fear Ward is necessary + // to address an issue with bot movement, which is that bots cannot be rooted and + // therefore will move when feared while holding the Tainted Core + if (!bot->HasAura(SPELL_FEAR_WARD)) + bot->AddAura(SPELL_FEAR_WARD, bot); + + Item* item = bot->GetItemByEntry(ITEM_TAINTED_CORE); + if (!item || !botAI->HasItemInInventory(ITEM_TAINTED_CORE)) + { + // Passer order: HealAssistantOfIndex 0, 1, 2, then RangedDpsAssistantOfIndex 0 + if (bot == firstCorePasser) + { + if (LineUpFirstCorePasser(designatedLooter, closestTrigger)) + return true; + } + else if (bot == secondCorePasser) + { + if (LineUpSecondCorePasser(firstCorePasser, closestTrigger)) + return true; + } + else if (bot == thirdCorePasser) + { + if (LineUpThirdCorePasser(designatedLooter, firstCorePasser, + secondCorePasser, closestTrigger)) + return true; + } + else if (bot == fourthCorePasser) + { + if (LineUpFourthCorePasser(firstCorePasser, secondCorePasser, + thirdCorePasser, closestTrigger)) + return true; + } + } + else if (item && botAI->HasItemInInventory(ITEM_TAINTED_CORE)) + { + // Designated core looter logic + // Applicable only if cheat mode is on and thus looter is a bot + if (bot == designatedLooter) + { + if (IsFirstCorePasserInIntendedPosition( + designatedLooter, firstCorePasser, closestTrigger)) + { + const time_t now = std::time(nullptr); + auto it = lastImbueAttempt.find(instanceId); + if (it == lastImbueAttempt.end() || (now - it->second) >= 2) + { + lastImbueAttempt.insert_or_assign(instanceId, now); + lastCoreInInventoryTime.insert_or_assign(instanceId, now); + botAI->ImbueItem(item, firstCorePasser); + intendedLineup.erase(bot->GetGUID()); + ScheduleTransferCoreAfterImbue(botAI, bot, firstCorePasser); + return true; + } + } + } + // First core passer: receive core from looter at the top of the stairs, + // pass to second core passer + else if (bot == firstCorePasser) + { + const time_t now = std::time(nullptr); + auto it = lastImbueAttempt.find(instanceId); + if (it == lastImbueAttempt.end() || (now - it->second) >= 2) + { + lastImbueAttempt.insert_or_assign(instanceId, now); + lastCoreInInventoryTime.insert_or_assign(instanceId, now); + botAI->ImbueItem(item, secondCorePasser); + intendedLineup.erase(bot->GetGUID()); + ScheduleTransferCoreAfterImbue(botAI, bot, secondCorePasser); + return true; + } + } + // Second core passer: if closest usable generator is within passing distance + // of the first passer, move to the generator; otherwise, move as close as + // possible to the generator while staying in passing range + else if (bot == secondCorePasser) + { + if (!UseCoreOnNearestGenerator(instanceId)) + { + if (IsThirdCorePasserInIntendedPosition( + secondCorePasser, thirdCorePasser, closestTrigger)) + { + const time_t now = std::time(nullptr); + auto it = lastImbueAttempt.find(instanceId); + if (it == lastImbueAttempt.end() || (now - it->second) >= 2) + { + lastImbueAttempt.insert_or_assign(instanceId, now); + lastCoreInInventoryTime.insert_or_assign(instanceId, now); + botAI->ImbueItem(item, thirdCorePasser); + intendedLineup.erase(bot->GetGUID()); + ScheduleTransferCoreAfterImbue(botAI, bot, thirdCorePasser); + return true; + } + } + } + } + // Third core passer: if closest usable generator is within passing distance + // of the second passer, move to the generator; otherwise, move as close as + // possible to the generator while staying in passing range + else if (bot == thirdCorePasser) + { + if (!UseCoreOnNearestGenerator(instanceId)) + { + if (IsFourthCorePasserInIntendedPosition( + thirdCorePasser, fourthCorePasser, closestTrigger)) + { + const time_t now = std::time(nullptr); + auto it = lastImbueAttempt.find(instanceId); + if (it == lastImbueAttempt.end() || (now - it->second) >= 2) + { + lastImbueAttempt.insert_or_assign(instanceId, now); + lastCoreInInventoryTime.insert_or_assign(instanceId, now); + botAI->ImbueItem(item, fourthCorePasser); + intendedLineup.erase(bot->GetGUID()); + ScheduleTransferCoreAfterImbue(botAI, bot, fourthCorePasser); + return true; + } + } + } + } + // Fourth core passer: the fourth passer is rarely needed and no more than + // four ever should be, so it should use the Core on the nearest generator + else if (bot == fourthCorePasser) + UseCoreOnNearestGenerator(instanceId); + } + + return false; +} + +bool LadyVashjPassTheTaintedCoreAction::LineUpFirstCorePasser( + Player* designatedLooter, Unit* closestTrigger) +{ + const float centerX = VASHJ_PLATFORM_CENTER_POSITION.GetPositionX(); + const float centerY = VASHJ_PLATFORM_CENTER_POSITION.GetPositionY(); + constexpr float radius = 57.5f; + + float mx = designatedLooter->GetPositionX(); + float my = designatedLooter->GetPositionY(); + float angle = atan2(my - centerY, mx - centerX); + + float targetX = centerX + radius * std::cos(angle); + float targetY = centerY + radius * std::sin(angle); + constexpr float targetZ = 41.097f; + + intendedLineup.insert_or_assign(bot->GetGUID(), Position(targetX, targetY, targetZ)); + + bot->AttackStop(); + bot->InterruptNonMeleeSpells(true); + return MoveTo(SSC_MAP_ID, targetX, targetY, targetZ, false, false, false, true, + MovementPriority::MOVEMENT_FORCED, true, false); +} + +bool LadyVashjPassTheTaintedCoreAction::LineUpSecondCorePasser( + Player* firstCorePasser, Unit* closestTrigger) +{ + float fx = firstCorePasser->GetPositionX(); + float fy = firstCorePasser->GetPositionY(); + + float dx = closestTrigger->GetPositionX() - fx; + float dy = closestTrigger->GetPositionY() - fy; + float distToTrigger = firstCorePasser->GetExactDist2d(closestTrigger); + + if (distToTrigger == 0.0f) + return false; + + dx /= distToTrigger; dy /= distToTrigger; + + // Target is on a line between firstCorePasser and closestTrigger + float targetX, targetY, targetZ; + // If firstCorePasser is within thresholdDist of closestTrigger, + // go to nearTriggerDist short of closestTrigger + constexpr float thresholdDist = 40.0f; + constexpr float nearTriggerDist = 1.5f; + // If firstCorePasser is not thresholdDist yards from closestTrigger, + // go to farDistance from firstCorePasser + constexpr float farDistance = 38.0f; + + if (distToTrigger <= thresholdDist) + { + float moveDist = std::max(distToTrigger - nearTriggerDist, 0.0f); + targetX = fx + dx * moveDist; + targetY = fy + dy * moveDist; + } + else + { + targetX = fx + dx * farDistance; + targetY = fy + dy * farDistance; + } + + intendedLineup.insert_or_assign(bot->GetGUID(), Position(targetX, targetY, VASHJ_PLATFORM_Z)); + + bot->AttackStop(); + bot->InterruptNonMeleeSpells(true); + return MoveTo(SSC_MAP_ID, targetX, targetY, VASHJ_PLATFORM_Z, false, false, false, true, + MovementPriority::MOVEMENT_FORCED, true, false); +} + +bool LadyVashjPassTheTaintedCoreAction::LineUpThirdCorePasser( + Player* designatedLooter, Player* firstCorePasser, Player* secondCorePasser, Unit* closestTrigger) +{ + // Wait to move until it is clear that a third passer is needed + bool needThird = + (IsFirstCorePasserInIntendedPosition(designatedLooter, firstCorePasser, closestTrigger) && + firstCorePasser->GetExactDist2d(closestTrigger) > 42.0f) || + (IsSecondCorePasserInIntendedPosition(firstCorePasser, secondCorePasser, closestTrigger) && + secondCorePasser->GetExactDist2d(closestTrigger) > 4.0f); + + if (!needThird) + return false; + + float sx = secondCorePasser->GetPositionX(); + float sy = secondCorePasser->GetPositionY(); + + float dx = closestTrigger->GetPositionX() - sx; + float dy = closestTrigger->GetPositionY() - sy; + float distToTrigger = secondCorePasser->GetExactDist2d(closestTrigger); + + if (distToTrigger == 0.0f) + return false; + + dx /= distToTrigger; dy /= distToTrigger; + + float targetX, targetY, targetZ; + constexpr float thresholdDist = 40.0f; + constexpr float nearTriggerDist = 1.5f; + constexpr float farDistance = 38.0f; + + if (distToTrigger <= thresholdDist) + { + float moveDist = std::max(distToTrigger - nearTriggerDist, 0.0f); + targetX = sx + dx * moveDist; + targetY = sy + dy * moveDist; + } + else + { + targetX = sx + dx * farDistance; + targetY = sy + dy * farDistance; + } + + intendedLineup.insert_or_assign(bot->GetGUID(), Position(targetX, targetY, VASHJ_PLATFORM_Z)); + + bot->AttackStop(); + bot->InterruptNonMeleeSpells(true); + return MoveTo(SSC_MAP_ID, targetX, targetY, VASHJ_PLATFORM_Z, false, false, false, true, + MovementPriority::MOVEMENT_FORCED, true, false); + + return false; +} + +bool LadyVashjPassTheTaintedCoreAction::LineUpFourthCorePasser( + Player* firstCorePasser, Player* secondCorePasser, Player* thirdCorePasser, Unit* closestTrigger) +{ + // Wait to move until it is clear that a fourth passer is needed + bool needFourth = + (IsSecondCorePasserInIntendedPosition(firstCorePasser, secondCorePasser, closestTrigger) && + secondCorePasser->GetExactDist2d(closestTrigger) > 42.0f) || + (IsThirdCorePasserInIntendedPosition(secondCorePasser, thirdCorePasser, closestTrigger) && + thirdCorePasser->GetExactDist2d(closestTrigger) > 4.0f); + + if (!needFourth) + return false; + + float sx = thirdCorePasser->GetPositionX(); + float sy = thirdCorePasser->GetPositionY(); + + float tx = closestTrigger->GetPositionX(); + float ty = closestTrigger->GetPositionY(); + + float dx = tx - sx; + float dy = ty - sy; + float distToTrigger = thirdCorePasser->GetExactDist2d(closestTrigger); + + if (distToTrigger == 0.0f) + return false; + + dx /= distToTrigger; dy /= distToTrigger; + + constexpr float nearTriggerDist = 1.5f; + float targetX = tx - dx * nearTriggerDist; + float targetY = ty - dy * nearTriggerDist; + + intendedLineup.insert_or_assign(bot->GetGUID(), Position(targetX, targetY, VASHJ_PLATFORM_Z)); + + bot->AttackStop(); + bot->InterruptNonMeleeSpells(true); + return MoveTo(SSC_MAP_ID, targetX, targetY, VASHJ_PLATFORM_Z, false, false, false, true, + MovementPriority::MOVEMENT_FORCED, true, false); +} + +// The next four functions check if the respective passer is <= 2 yards of their intended +// position and are used to determine when the prior bot in the chain can pass the core +bool LadyVashjPassTheTaintedCoreAction::IsFirstCorePasserInIntendedPosition( + Player* designatedLooter, Player* firstCorePasser, Unit* closestTrigger) +{ + auto itSnap = intendedLineup.find(firstCorePasser->GetGUID()); + if (itSnap != intendedLineup.end()) + { + float dist2d = firstCorePasser->GetExactDist2d(itSnap->second.GetPositionX(), + itSnap->second.GetPositionY()); + return dist2d <= 2.0f; + } + + return false; +} + +bool LadyVashjPassTheTaintedCoreAction::IsSecondCorePasserInIntendedPosition( + Player* firstCorePasser, Player* secondCorePasser, Unit* closestTrigger) +{ + auto itSnap = intendedLineup.find(secondCorePasser->GetGUID()); + if (itSnap != intendedLineup.end()) + { + float dist2d = secondCorePasser->GetExactDist2d(itSnap->second.GetPositionX(), + itSnap->second.GetPositionY()); + return dist2d <= 2.0f; + } + + return false; +} + +bool LadyVashjPassTheTaintedCoreAction::IsThirdCorePasserInIntendedPosition( + Player* secondCorePasser, Player* thirdCorePasser, Unit* closestTrigger) +{ + auto itSnap = intendedLineup.find(thirdCorePasser->GetGUID()); + if (itSnap != intendedLineup.end()) + { + float dist2d = thirdCorePasser->GetExactDist2d(itSnap->second.GetPositionX(), + itSnap->second.GetPositionY()); + return dist2d <= 2.0f; + } + + return false; +} + +bool LadyVashjPassTheTaintedCoreAction::IsFourthCorePasserInIntendedPosition( + Player* thirdCorePasser, Player* fourthCorePasser, Unit* closestTrigger) +{ + auto itSnap = intendedLineup.find(fourthCorePasser->GetGUID()); + if (itSnap != intendedLineup.end()) + { + float dist2d = fourthCorePasser->GetExactDist2d(itSnap->second.GetPositionX(), + itSnap->second.GetPositionY()); + return dist2d <= 2.0f; + } + + return false; +} + +// ImbueItem() is inconsistent in causing the receiver bot to receive the core and the giver +// bot to remove the core, so ScheduleTransferCoreAfterImbue() creates the core on the receiver +// and removes it from the giver, with ImbueItem() called primarily for the throwing animation +void LadyVashjPassTheTaintedCoreAction::ScheduleTransferCoreAfterImbue( + PlayerbotAI* botAI, Player* giver, Player* receiver) +{ + if (!receiver || !giver) + return; + + constexpr uint32 delayMs = 1500; + const ObjectGuid receiverGuid = receiver->GetGUID(); + const ObjectGuid giverGuid = giver->GetGUID(); + + botAI->AddTimedEvent([receiverGuid, giverGuid]() + { + Player* receiverPlayer = + receiverGuid.IsEmpty() ? nullptr : ObjectAccessor::FindPlayer(receiverGuid); + Player* giverPlayer = + giverGuid.IsEmpty() ? nullptr : ObjectAccessor::FindPlayer(giverGuid); + + if (!receiverPlayer) + return; + + if (!receiverPlayer->HasItemCount(ITEM_TAINTED_CORE, 1, false)) + { + ItemPosCountVec dest; + uint32 count = 1; + int canStore = + receiverPlayer->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, ITEM_TAINTED_CORE, count); + + if (canStore == EQUIP_ERR_OK) + { + receiverPlayer->StoreNewItem(dest, ITEM_TAINTED_CORE, true, + Item::GenerateItemRandomPropertyId(ITEM_TAINTED_CORE)); + } + } + + if (giverPlayer) + { + Item* item = giverPlayer->GetItemByEntry(ITEM_TAINTED_CORE); + if (item && giverPlayer->HasItemCount(ITEM_TAINTED_CORE, 1, false)) + giverPlayer->DestroyItem(item->GetBagSlot(), item->GetSlot(), true); + } + }, delayMs); +} + +bool LadyVashjPassTheTaintedCoreAction::UseCoreOnNearestGenerator(const uint32 instanceId) +{ + auto const& generators = + GetAllGeneratorInfosByDbGuids(bot->GetMap(), SHIELD_GENERATOR_DB_GUIDS); + const GeneratorInfo* nearestGen = GetNearestGeneratorToBot(bot, generators); + if (!nearestGen) + return false; + + GameObject* generator = botAI->GetGameObject(nearestGen->guid); + if (!generator) + return false; + + if (bot->GetExactDist2d(generator) > 4.5f) + return false; + + Item* core = bot->GetItemByEntry(ITEM_TAINTED_CORE); + if (!core) + return false; + + if (bot->CanUseItem(core) != EQUIP_ERR_OK) + return false; + + if (bot->IsNonMeleeSpellCast(false)) + return false; + + const uint8 bagIndex = core->GetBagSlot(); + const uint8 slot = core->GetSlot(); + constexpr uint8 cast_count = 0; + uint32 spellId = 0; + + for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) + { + if (core->GetTemplate()->Spells[i].SpellId > 0) + { + spellId = core->GetTemplate()->Spells[i].SpellId; + break; + } + } + + const ObjectGuid item_guid = core->GetGUID(); + constexpr uint32 glyphIndex = 0; + constexpr uint8 castFlags = 0; + + WorldPacket packet(CMSG_USE_ITEM); + packet << bagIndex; + packet << slot; + packet << cast_count; + packet << spellId; + packet << item_guid; + packet << glyphIndex; + packet << castFlags; + packet << (uint32)TARGET_FLAG_GAMEOBJECT; + packet << generator->GetGUID().WriteAsPacked(); + + bot->GetSession()->HandleUseItemOpcode(packet); + nearestTriggerGuid.erase(instanceId); + lastImbueAttempt.erase(instanceId); + lastCoreInInventoryTime.erase(instanceId); + return true; +} + +// Fallback for residual cores to be destroyed in Phase 3 in case +// ScheduleTransferCoreAfterImbue() fails to remove the core from the giver +bool LadyVashjDestroyTaintedCoreAction::Execute(Event /*event*/) +{ + if (Item* core = bot->GetItemByEntry(ITEM_TAINTED_CORE)) + { + bot->DestroyItem(core->GetBagSlot(), core->GetSlot(), true); + return true; + } + + return false; +} + +// This needs to be separate from the general map erasing logic because +// Bots may end up out of combat during the Vashj encounter +bool LadyVashjEraseCorePassingTrackersAction::Execute(Event /*event*/) +{ + Unit* vashj = AI_VALUE2(Unit*, "find target", "lady vashj"); + if (!vashj) + return false; + + const uint32 instanceId = vashj->GetMap()->GetInstanceId(); + + bool erased = false; + if (nearestTriggerGuid.erase(instanceId) > 0) + erased = true; + if (lastImbueAttempt.erase(instanceId) > 0) + erased = true; + if (lastCoreInInventoryTime.erase(instanceId) > 0) + erased = true; + if (intendedLineup.erase(bot->GetGUID()) > 0) + erased = true; + + return erased; +} + +// The standard "avoid aoe" strategy does work for Toxic Spores, but this method +// provides more buffer distance and limits the area in which bots can move +// so that they do not go down the stairs +bool LadyVashjAvoidToxicSporesAction::Execute(Event /*event*/) +{ + auto const& spores = GetAllSporeDropTriggers(botAI, bot); + if (spores.empty()) + return false; + + constexpr float hazardRadius = 7.0f; + bool inDanger = false; + for (Unit* spore : spores) + { + if (bot->GetExactDist2d(spore) < hazardRadius) + { + inDanger = true; + break; + } + } + + if (!inDanger) + return false; + + const Position& vashjCenter = VASHJ_PLATFORM_CENTER_POSITION; + constexpr float maxRadius = 60.0f; + + Position safestPos = FindSafestNearbyPosition(spores, vashjCenter, maxRadius, hazardRadius); + + Unit* vashj = AI_VALUE2(Unit*, "find target", "lady vashj"); + bool backwards = (vashj && vashj->GetVictim() == bot); + return MoveTo(SSC_MAP_ID, safestPos.GetPositionX(), safestPos.GetPositionY(), + safestPos.GetPositionZ(), false, false, false, true, + MovementPriority::MOVEMENT_COMBAT, true, backwards); +} + +Position LadyVashjAvoidToxicSporesAction::FindSafestNearbyPosition( + const std::vector& spores, const Position& vashjCenter, + float maxRadius, float hazardRadius) +{ + constexpr float searchStep = M_PI / 8.0f; + constexpr float minDistance = 2.0f; + constexpr float maxDistance = 40.0f; + constexpr float distanceStep = 1.0f; + + Position bestPos; + float minMoveDistance = std::numeric_limits::max(); + bool foundSafe = false; + + for (float distance = minDistance; + distance <= maxDistance; distance += distanceStep) + { + for (float angle = 0.0f; angle < 2 * M_PI; angle += searchStep) + { + float x = bot->GetPositionX() + distance * std::cos(angle); + float y = bot->GetPositionY() + distance * std::sin(angle); + float z = bot->GetPositionZ(); + + if (vashjCenter.GetExactDist2d(x, y) > maxRadius) + continue; + + bool isSafe = true; + for (Unit* spore : spores) + { + if (spore->GetExactDist2d(x, y) < hazardRadius) + { + isSafe = false; + break; + } + } + + if (!isSafe) + continue; + + Position testPos(x, y, z); + + bool pathSafe = + IsPathSafeFromSpores(bot->GetPosition(), testPos, spores, hazardRadius); + if (pathSafe || !foundSafe) + { + float moveDistance = bot->GetExactDist2d(x, y); + + if (pathSafe && (!foundSafe || moveDistance < minMoveDistance)) + { + bestPos = testPos; + minMoveDistance = moveDistance; + foundSafe = true; + } + else if (!foundSafe && moveDistance < minMoveDistance) + { + bestPos = testPos; + minMoveDistance = moveDistance; + } + } + } + + if (foundSafe) + break; + } + + return bestPos; +} + +bool LadyVashjAvoidToxicSporesAction::IsPathSafeFromSpores(const Position& start, + const Position& end, const std::vector& spores, float hazardRadius) +{ + constexpr uint8 numChecks = 10; + float dx = end.GetPositionX() - start.GetPositionX(); + float dy = end.GetPositionY() - start.GetPositionY(); + + for (uint8 i = 1; i <= numChecks; ++i) + { + float ratio = static_cast(i) / numChecks; + float checkX = start.GetPositionX() + dx * ratio; + float checkY = start.GetPositionY() + dy * ratio; + + for (Unit* spore : spores) + { + float distToSpore = spore->GetExactDist2d(checkX, checkY); + if (distToSpore < hazardRadius) + return false; + } + } + + return true; +} + +// When Toxic Sporebats spit poison, they summon "Spore Drop Trigger" NPCs +// that create the toxic pools +std::vector LadyVashjAvoidToxicSporesAction::GetAllSporeDropTriggers( + PlayerbotAI* botAI, Player* bot) +{ + std::vector sporeDropTriggers; + auto const& npcs = + botAI->GetAiObjectContext()->GetValue("nearest npcs")->Get(); + for (auto const& npcGuid : npcs) + { + constexpr float maxSearchRadius = 40.0f; + Unit* unit = botAI->GetUnit(npcGuid); + if (unit && unit->GetEntry() == NPC_SPORE_DROP_TRIGGER && + bot->GetExactDist2d(unit) < maxSearchRadius) + sporeDropTriggers.push_back(unit); + } + + return sporeDropTriggers; +} + +bool LadyVashjUseFreeActionAbilitiesAction::Execute(Event /*event*/) +{ + Group* group = bot->GetGroup(); + if (!group) + return false; + + auto const& spores = + LadyVashjAvoidToxicSporesAction::GetAllSporeDropTriggers(botAI, bot); + constexpr float toxicSporeRadius = 6.0f; + + // If Rogues are Entangled and either have Static Charge or + // are near a spore, use Cloak of Shadows + if (bot->getClass() == CLASS_ROGUE && bot->HasAura(SPELL_ENTANGLE)) + { + bool nearSpore = false; + for (Unit* spore : spores) + { + if (bot->GetExactDist2d(spore) < toxicSporeRadius) + { + nearSpore = true; + break; + } + } + if (bot->HasAura(SPELL_STATIC_CHARGE) || nearSpore) + { + if (botAI->CanCastSpell("cloak of shadows", bot)) + return botAI->CastSpell("cloak of shadows", bot); + } + } + + // The remainder of the logic is for Paladins to use Hand of Freedom + Player* mainTankToxic = nullptr; + Player* anyToxic = nullptr; + Player* mainTankStatic = nullptr; + Player* anyStatic = nullptr; + + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive() || !member->HasAura(SPELL_ENTANGLE) || + !botAI->IsMelee(member)) + continue; + + bool nearToxicSpore = false; + for (Unit* spore : spores) + { + if (member->GetExactDist2d(spore) < toxicSporeRadius) + { + nearToxicSpore = true; + break; + } + } + + if (nearToxicSpore) + { + if (botAI->IsMainTank(member)) + mainTankToxic = member; + + if (!anyToxic) + anyToxic = member; + } + + if (member->HasAura(SPELL_STATIC_CHARGE)) + { + if (botAI->IsMainTank(member)) + mainTankStatic = member; + + if (!anyStatic) + anyStatic = member; + } + } + + if (bot->getClass() == CLASS_PALADIN) + { + // Priority 1: Entangled in Toxic Spores (prefer main tank) + Player* toxicTarget = mainTankToxic ? mainTankToxic : anyToxic; + if (toxicTarget) + { + if (botAI->CanCastSpell("hand of freedom", toxicTarget)) + return botAI->CastSpell("hand of freedom", toxicTarget); + } + + // Priority 2: Entangled with Static Charge (prefer main tank) + Player* staticTarget = mainTankStatic ? mainTankStatic : anyStatic; + if (staticTarget) + { + if (botAI->CanCastSpell("hand of freedom", staticTarget)) + return botAI->CastSpell("hand of freedom", staticTarget); + } + } + + return false; +} diff --git a/src/Ai/Raid/SerpentshrineCavern/Action/RaidSSCActions.h b/src/Ai/Raid/SerpentshrineCavern/Action/RaidSSCActions.h new file mode 100644 index 0000000000..cbd2374021 --- /dev/null +++ b/src/Ai/Raid/SerpentshrineCavern/Action/RaidSSCActions.h @@ -0,0 +1,457 @@ +#ifndef _PLAYERBOT_RAIDSSCACTIONS_H +#define _PLAYERBOT_RAIDSSCACTIONS_H + +#include "Action.h" +#include "AttackAction.h" +#include "MovementActions.h" + +// General + +class SerpentShrineCavernEraseTimersAndTrackersAction : public Action +{ +public: + SerpentShrineCavernEraseTimersAndTrackersAction( + PlayerbotAI* botAI, std::string const name = "serpent shrine cavern erase timers and trackers") : Action(botAI, name) {} + bool Execute(Event event) override; +}; + +// Trash + +class UnderbogColossusEscapeToxicPoolAction : public MovementAction +{ +public: + UnderbogColossusEscapeToxicPoolAction( + PlayerbotAI* botAI, std::string const name = "underbog colossus escape toxic pool") : MovementAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class GreyheartTidecallerMarkWaterElementalTotemAction : public Action +{ +public: + GreyheartTidecallerMarkWaterElementalTotemAction( + PlayerbotAI* botAI, std::string const name = "greyheart tidecaller mark water elemental totem") : Action(botAI, name) {} + bool Execute(Event event) override; +}; + +// Hydross the Unstable + +class HydrossTheUnstablePositionFrostTankAction : public AttackAction +{ +public: + HydrossTheUnstablePositionFrostTankAction( + PlayerbotAI* botAI, std::string const name = "hydross the unstable position frost tank") : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class HydrossTheUnstablePositionNatureTankAction : public AttackAction +{ +public: + HydrossTheUnstablePositionNatureTankAction( + PlayerbotAI* botAI, std::string const name = "hydross the unstable position nature tank") : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class HydrossTheUnstablePrioritizeElementalAddsAction : public AttackAction +{ +public: + HydrossTheUnstablePrioritizeElementalAddsAction( + PlayerbotAI* botAI, std::string const name = "hydross the unstable prioritize elemental adds") : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class HydrossTheUnstableFrostPhaseSpreadOutAction : public MovementAction +{ +public: + HydrossTheUnstableFrostPhaseSpreadOutAction( + PlayerbotAI* botAI, std::string const name = "hydross the unstable frost phase spread out") : MovementAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class HydrossTheUnstableMisdirectBossToTankAction : public Action +{ +public: + HydrossTheUnstableMisdirectBossToTankAction( + PlayerbotAI* botAI, std::string const name = "hydross the unstable misdirect boss to tank") : Action(botAI, name) {} + bool Execute(Event event) override; + +private: + bool TryMisdirectToFrostTank(Unit* hydross, Group* group); + bool TryMisdirectToNatureTank(Unit* hydross, Group* group); +}; + +class HydrossTheUnstableStopDpsUponPhaseChangeAction : public Action +{ +public: + HydrossTheUnstableStopDpsUponPhaseChangeAction( + PlayerbotAI* botAI, std::string const name = "hydross the unstable stop dps upon phase change") : Action(botAI, name) {} + bool Execute(Event event) override; +}; + +class HydrossTheUnstableManageTimersAction : public Action +{ +public: + HydrossTheUnstableManageTimersAction( + PlayerbotAI* botAI, std::string const name = "hydross the unstable manage timers") : Action(botAI, name) {} + bool Execute(Event event) override; +}; + +// The Lurker Below + +class TheLurkerBelowRunAroundBehindBossAction : public MovementAction +{ +public: + TheLurkerBelowRunAroundBehindBossAction( + PlayerbotAI* botAI, std::string const name = "the lurker below run around behind boss") : MovementAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class TheLurkerBelowPositionMainTankAction : public AttackAction +{ +public: + TheLurkerBelowPositionMainTankAction( + PlayerbotAI* botAI, std::string const name = "the lurker below position main tank") : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class TheLurkerBelowSpreadRangedInArcAction : public MovementAction +{ +public: + TheLurkerBelowSpreadRangedInArcAction( + PlayerbotAI* botAI, std::string const name = "the lurker below spread ranged in arc") : MovementAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class TheLurkerBelowTanksPickUpAddsAction : public AttackAction +{ +public: + TheLurkerBelowTanksPickUpAddsAction( + PlayerbotAI* botAI, std::string const name = "the lurker below tanks pick up adds") : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class TheLurkerBelowManageSpoutTimerAction : public Action +{ +public: + TheLurkerBelowManageSpoutTimerAction( + PlayerbotAI* botAI, std::string const name = "the lurker below manage spout timer") : Action(botAI, name) {} + bool Execute(Event event) override; +}; + +// Leotheras the Blind + +class LeotherasTheBlindTargetSpellbindersAction : public Action +{ +public: + LeotherasTheBlindTargetSpellbindersAction( + PlayerbotAI* botAI, std::string const name = "leotheras the blind target spellbinders") : Action(botAI, name) {} + bool Execute(Event event) override; +}; + +class LeotherasTheBlindPositionRangedAction : public MovementAction +{ +public: + LeotherasTheBlindPositionRangedAction( + PlayerbotAI* botAI, std::string const name = "leotheras the blind position ranged") : MovementAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class LeotherasTheBlindDemonFormTankAttackBossAction : public AttackAction +{ +public: + LeotherasTheBlindDemonFormTankAttackBossAction( + PlayerbotAI* botAI, std::string const name = "leotheras the blind demon form tank attack boss") : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class LeotherasTheBlindMeleeTanksDontAttackDemonFormAction : public Action +{ +public: + LeotherasTheBlindMeleeTanksDontAttackDemonFormAction( + PlayerbotAI* botAI, std::string const name = "leotheras the blind melee tanks don't attack demon form") : Action(botAI, name) {} + bool Execute(Event event) override; +}; + +class LeotherasTheBlindRunAwayFromWhirlwindAction : public MovementAction +{ +public: + LeotherasTheBlindRunAwayFromWhirlwindAction( + PlayerbotAI* botAI, std::string const name = "leotheras the blind run away from whirlwind") : MovementAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class LeotherasTheBlindMeleeDpsRunAwayFromBossAction : public MovementAction +{ +public: + LeotherasTheBlindMeleeDpsRunAwayFromBossAction( + PlayerbotAI* botAI, std::string const name = "leotheras the blind melee dps run away from boss") : MovementAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class LeotherasTheBlindDestroyInnerDemonAction : public AttackAction +{ +public: + LeotherasTheBlindDestroyInnerDemonAction( + PlayerbotAI* botAI, std::string const name = "leotheras the blind destroy inner demon") : AttackAction(botAI, name) {} + bool Execute(Event event) override; + +private: + bool HandleFeralTankStrategy(Unit* innerDemon); + bool HandleHealerStrategy(Unit* innerDemon); +}; + +class LeotherasTheBlindFinalPhaseAssignDpsPriorityAction : public AttackAction +{ +public: + LeotherasTheBlindFinalPhaseAssignDpsPriorityAction( + PlayerbotAI* botAI, std::string const name = "leotheras the blind final phase assign dps priority") : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class LeotherasTheBlindMisdirectBossToDemonFormTankAction : public AttackAction +{ +public: + LeotherasTheBlindMisdirectBossToDemonFormTankAction( + PlayerbotAI* botAI, std::string const name = "leotheras the blind misdirect boss to demon form tank") : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class LeotherasTheBlindManageDpsWaitTimersAction : public Action +{ +public: + LeotherasTheBlindManageDpsWaitTimersAction( + PlayerbotAI* botAI, std::string const name = "leotheras the blind manage dps wait timers") : Action(botAI, name) {} + bool Execute(Event event) override; +}; + +// Fathom-Lord Karathress + +class FathomLordKarathressMainTankPositionBossAction : public AttackAction +{ +public: + FathomLordKarathressMainTankPositionBossAction( + PlayerbotAI* botAI, std::string const name = "fathom-lord karathress main tank position boss") : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class FathomLordKarathressFirstAssistTankPositionCaribdisAction : public AttackAction +{ +public: + FathomLordKarathressFirstAssistTankPositionCaribdisAction( + PlayerbotAI* botAI, std::string const name = "fathom-lord karathress first assist tank position caribdis") : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class FathomLordKarathressSecondAssistTankPositionSharkkisAction : public AttackAction +{ +public: + FathomLordKarathressSecondAssistTankPositionSharkkisAction( + PlayerbotAI* botAI, std::string const name = "fathom-lord karathress second assist tank position sharkkis") : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class FathomLordKarathressThirdAssistTankPositionTidalvessAction : public AttackAction +{ +public: + FathomLordKarathressThirdAssistTankPositionTidalvessAction( + PlayerbotAI* botAI, std::string const name = "fathom-lord karathress third assist tank position tidalvess") : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class FathomLordKarathressPositionCaribdisTankHealerAction : public MovementAction +{ +public: + FathomLordKarathressPositionCaribdisTankHealerAction( + PlayerbotAI* botAI, std::string const name = "fathom-lord karathress position caribdis tank healer") : MovementAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class FathomLordKarathressMisdirectBossesToTanksAction : public AttackAction +{ +public: + FathomLordKarathressMisdirectBossesToTanksAction( + PlayerbotAI* botAI, std::string const name = "fathom-lord karathress misdirect bosses to tanks") : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class FathomLordKarathressAssignDpsPriorityAction : public AttackAction +{ +public: + FathomLordKarathressAssignDpsPriorityAction( + PlayerbotAI* botAI, std::string const name = "fathom-lord karathress assign dps priority") : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class FathomLordKarathressManageDpsTimerAction : public Action +{ +public: + FathomLordKarathressManageDpsTimerAction( + PlayerbotAI* botAI, std::string const name = "fathom-lord karathress manage dps timer") : Action(botAI, name) {} + bool Execute(Event event) override; +}; + +// Morogrim Tidewalker + +class MorogrimTidewalkerMisdirectBossToMainTankAction : public AttackAction +{ +public: + MorogrimTidewalkerMisdirectBossToMainTankAction( + PlayerbotAI* botAI, std::string const name = "morogrim tidewalker misdirect boss to main tank") : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class MorogrimTidewalkerMoveBossToTankPositionAction : public AttackAction +{ +public: + MorogrimTidewalkerMoveBossToTankPositionAction( + PlayerbotAI* botAI, std::string const name = "morogrim tidewalker move boss to tank position") : AttackAction(botAI, name) {} + bool Execute(Event event) override; + +private: + bool MoveToPhase1TankPosition(Unit* tidewalker); + bool MoveToPhase2TankPosition(Unit* tidewalker); +}; + +class MorogrimTidewalkerPhase2RepositionRangedAction : public MovementAction +{ +public: + MorogrimTidewalkerPhase2RepositionRangedAction( + PlayerbotAI* botAI, std::string const name = "morogrim tidewalker phase 2 reposition ranged") : MovementAction(botAI, name) {} + bool Execute(Event event) override; +}; + +// Lady Vashj + +class LadyVashjMainTankPositionBossAction : public AttackAction +{ +public: + LadyVashjMainTankPositionBossAction( + PlayerbotAI* botAI, std::string const name = "lady vashj main tank position boss") : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class LadyVashjPhase1SpreadRangedInArcAction : public MovementAction +{ +public: + LadyVashjPhase1SpreadRangedInArcAction( + PlayerbotAI* botAI, std::string const name = "lady vashj phase 1 spread ranged in arc") : MovementAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class LadyVashjSetGroundingTotemInMainTankGroupAction : public MovementAction +{ +public: + LadyVashjSetGroundingTotemInMainTankGroupAction( + PlayerbotAI* botAI, std::string const name = "lady vashj set grounding totem in main tank group") : MovementAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class LadyVashjStaticChargeMoveAwayFromGroupAction : public MovementAction +{ +public: + LadyVashjStaticChargeMoveAwayFromGroupAction( + PlayerbotAI* botAI, std::string const name = "lady vashj static charge move away from group") : MovementAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class LadyVashjMisdirectBossToMainTankAction : public AttackAction +{ +public: + LadyVashjMisdirectBossToMainTankAction( + PlayerbotAI* botAI, std::string const name = "lady vashj misdirect boss to main tank") : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class LadyVashjAssignPhase2AndPhase3DpsPriorityAction : public AttackAction +{ +public: + LadyVashjAssignPhase2AndPhase3DpsPriorityAction( + PlayerbotAI* botAI, std::string const name = "lady vashj assign phase 2 and phase 3 dps priority") : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class LadyVashjMisdirectStriderToFirstAssistTankAction : public AttackAction +{ +public: + LadyVashjMisdirectStriderToFirstAssistTankAction( + PlayerbotAI* botAI, std::string const name = "lady vashj misdirect strider to first assist tank") : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class LadyVashjTankAttackAndMoveAwayStriderAction : public AttackAction +{ +public: + LadyVashjTankAttackAndMoveAwayStriderAction( + PlayerbotAI* botAI, std::string const name = "lady vashj tank attack and move away strider") : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class LadyVashjTeleportToTaintedElementalAction : public AttackAction +{ +public: + LadyVashjTeleportToTaintedElementalAction( + PlayerbotAI* botAI, std::string const name = "lady vashj teleport to tainted elemental") : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class LadyVashjLootTaintedCoreAction : public MovementAction +{ +public: + LadyVashjLootTaintedCoreAction( + PlayerbotAI* botAI, std::string const name = "lady vashj loot tainted core") : MovementAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class LadyVashjPassTheTaintedCoreAction : public MovementAction +{ +public: + LadyVashjPassTheTaintedCoreAction( + PlayerbotAI* botAI, std::string const name = "lady vashj pass the tainted core") : MovementAction(botAI, name) {} + bool Execute(Event event) override; + +private: + bool LineUpFirstCorePasser(Player* designatedLooter, Unit* closestTrigger); + bool LineUpSecondCorePasser(Player* firstCorePasser, Unit* closestTrigger); + bool LineUpThirdCorePasser(Player* designatedLooter, Player* firstCorePasser, Player* secondCorePasser, Unit* closestTrigger); + bool LineUpFourthCorePasser(Player* firstCorePasser, Player* secondCorePasser, Player* thirdCorePasser, Unit* closestTrigger); + bool IsFirstCorePasserInIntendedPosition(Player* designatedLooter, Player* firstCorePasser, Unit* closestTrigger); + bool IsSecondCorePasserInIntendedPosition(Player* firstCorePasser, Player* secondCorePasser, Unit* closestTrigger); + bool IsThirdCorePasserInIntendedPosition(Player* secondCorePasser, Player* thirdCorePasser, Unit* closestTrigger); + bool IsFourthCorePasserInIntendedPosition(Player* thirdCorePasser, Player* fourthCorePasser, Unit* closestTrigger); + void ScheduleTransferCoreAfterImbue(PlayerbotAI* botAI, Player* giver, Player* receiver); + bool UseCoreOnNearestGenerator(const uint32 instanceId); +}; + +class LadyVashjDestroyTaintedCoreAction : public Action +{ +public: + LadyVashjDestroyTaintedCoreAction(PlayerbotAI* botAI, std::string const name = "lady vashj destroy tainted core") : Action(botAI, name) {} + bool Execute(Event event) override; +}; + +class LadyVashjEraseCorePassingTrackersAction : public Action +{ +public: + LadyVashjEraseCorePassingTrackersAction(PlayerbotAI* botAI, std::string const name = "lady vashj erase core passing trackers") : Action(botAI, name) {} + bool Execute(Event event) override; +}; + +class LadyVashjAvoidToxicSporesAction : public MovementAction +{ +public: + LadyVashjAvoidToxicSporesAction(PlayerbotAI* botAI, std::string const name = "lady vashj avoid toxic spores") : MovementAction(botAI, name) {} + bool Execute(Event event) override; + static std::vector GetAllSporeDropTriggers(PlayerbotAI* botAI, Player* bot); + +private: + Position FindSafestNearbyPosition(const std::vector& spores, const Position& position, float maxRadius, float hazardRadius); + bool IsPathSafeFromSpores(const Position& start, const Position& end, const std::vector& spores, float hazardRadius); +}; + +class LadyVashjUseFreeActionAbilitiesAction : public Action +{ +public: + LadyVashjUseFreeActionAbilitiesAction(PlayerbotAI* botAI, std::string const name = "lady vashj use free action abilities") : Action(botAI, name) {} + bool Execute(Event event) override; +}; + +#endif diff --git a/src/Ai/Raid/SerpentshrineCavern/Multiplier/RaidSSCMultipliers.cpp b/src/Ai/Raid/SerpentshrineCavern/Multiplier/RaidSSCMultipliers.cpp new file mode 100644 index 0000000000..c99cafa3c7 --- /dev/null +++ b/src/Ai/Raid/SerpentshrineCavern/Multiplier/RaidSSCMultipliers.cpp @@ -0,0 +1,799 @@ +#include "RaidSSCMultipliers.h" +#include "RaidSSCActions.h" +#include "RaidSSCHelpers.h" +#include "ChooseTargetActions.h" +#include "DestroyItemAction.h" +#include "DKActions.h" +#include "DruidActions.h" +#include "DruidBearActions.h" +#include "DruidCatActions.h" +#include "DruidShapeshiftActions.h" +#include "FollowActions.h" +#include "GenericSpellActions.h" +#include "HunterActions.h" +#include "LootAction.h" +#include "MageActions.h" +#include "PaladinActions.h" +#include "Playerbots.h" +#include "ReachTargetActions.h" +#include "RogueActions.h" +#include "ShamanActions.h" +#include "WarlockActions.h" +#include "WarriorActions.h" +#include "WipeAction.h" + +using namespace SerpentShrineCavernHelpers; + +// Trash + +float UnderbogColossusEscapeToxicPoolMultiplier::GetValue(Action* action) +{ + if (bot->HasAura(SPELL_TOXIC_POOL)) + { + if (dynamic_cast(action) && + !dynamic_cast(action)) + return 0.0f; + } + + return 1.0f; +} + +// Hydross the Unstable + +float HydrossTheUnstableDisableTankActionsMultiplier::GetValue(Action* action) +{ + if (!botAI->IsMainTank(bot) && !botAI->IsAssistTankOfIndex(bot, 0, true)) + return 1.0f; + + Unit* hydross = AI_VALUE2(Unit*, "find target", "hydross the unstable"); + if (!hydross) + return 1.0f; + + if (dynamic_cast(action) || + dynamic_cast(action)) + return 0.0f; + + if (dynamic_cast(action) || + dynamic_cast(action) || + (dynamic_cast(action) && + !dynamic_cast(action) && + !dynamic_cast(action))) + { + if ((botAI->IsMainTank(bot) && hydross->HasAura(SPELL_CORRUPTION)) || + (botAI->IsAssistTankOfIndex(bot, 0, true) && !hydross->HasAura(SPELL_CORRUPTION))) + return 0.0f; + } + + return 1.0f; +} + +float HydrossTheUnstableWaitForDpsMultiplier::GetValue(Action* action) +{ + Unit* hydross = AI_VALUE2(Unit*, "find target", "hydross the unstable"); + if (!hydross) + return 1.0f; + + Unit* waterElemental = AI_VALUE2(Unit*, "find target", "pure spawn of hydross"); + Unit* natureElemental = AI_VALUE2(Unit*, "find target", "tainted spawn of hydross"); + if (botAI->IsAssistTank(bot) && !botAI->IsAssistTankOfIndex(bot, 0, true) && + (waterElemental || natureElemental)) + return 1.0f; + + if (dynamic_cast(action)) + return 1.0f; + + const uint32 instanceId = hydross->GetMap()->GetInstanceId(); + const time_t now = std::time(nullptr); + constexpr uint8 phaseChangeWaitSeconds = 1; + constexpr uint8 dpsWaitSeconds = 5; + + if (!hydross->HasAura(SPELL_CORRUPTION) && !botAI->IsMainTank(bot)) + { + auto itDps = hydrossFrostDpsWaitTimer.find(instanceId); + auto itPhase = hydrossChangeToFrostPhaseTimer.find(instanceId); + + bool justChanged = (itDps == hydrossFrostDpsWaitTimer.end() || + (now - itDps->second) < dpsWaitSeconds); + bool aboutToChange = (itPhase != hydrossChangeToFrostPhaseTimer.end() && + (now - itPhase->second) > phaseChangeWaitSeconds); + + if (justChanged || aboutToChange) + { + if (dynamic_cast(action) || + (dynamic_cast(action) && + !dynamic_cast(action))) + return 0.0f; + } + } + + if (hydross->HasAura(SPELL_CORRUPTION) && !botAI->IsAssistTankOfIndex(bot, 0, true)) + { + auto itDps = hydrossNatureDpsWaitTimer.find(instanceId); + auto itPhase = hydrossChangeToNaturePhaseTimer.find(instanceId); + + bool justChanged = (itDps == hydrossNatureDpsWaitTimer.end() || + (now - itDps->second) < dpsWaitSeconds); + bool aboutToChange = (itPhase != hydrossChangeToNaturePhaseTimer.end() && + (now - itPhase->second) > phaseChangeWaitSeconds); + + if (justChanged || aboutToChange) + { + if (dynamic_cast(action) || + (dynamic_cast(action) && + !dynamic_cast(action))) + return 0.0f; + } + } + + return 1.0f; +} + +float HydrossTheUnstableControlMisdirectionMultiplier::GetValue(Action* action) +{ + if (bot->getClass() != CLASS_HUNTER) + return 1.0f; + + if (AI_VALUE2(Unit*, "find target", "hydross the unstable")) + { + if (dynamic_cast(action)) + return 0.0f; + } + + return 1.0f; +} + +// The Lurker Below + +float TheLurkerBelowStayAwayFromSpoutMultiplier::GetValue(Action* action) +{ + Unit* lurker = AI_VALUE2(Unit*, "find target", "the lurker below"); + if (!lurker) + return 1.0f; + + const time_t now = std::time(nullptr); + + auto it = lurkerSpoutTimer.find(lurker->GetMap()->GetInstanceId()); + if (it != lurkerSpoutTimer.end() && it->second > now) + { + if (dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action)) + return 0.0f; + + if (dynamic_cast(action) && + !dynamic_cast(action) && + !dynamic_cast(action)) + return 0.0f; + } + + return 1.0f; +} + +float TheLurkerBelowMaintainRangedSpreadMultiplier::GetValue(Action* action) +{ + if (!botAI->IsRanged(bot)) + return 1.0f; + + if (AI_VALUE2(Unit*, "find target", "the lurker below")) + { + if (dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action)) + return 0.0f; + } + + return 1.0f; +} + +// Disable tank assist during Submerge only if there are 3 or more tanks in the raid +float TheLurkerBelowDisableTankAssistMultiplier::GetValue(Action* action) +{ + if (!botAI->IsTank(bot)) + return 1.0f; + + if (bot->GetVictim() == nullptr) + return 1.0f; + + Unit* lurker = AI_VALUE2(Unit*, "find target", "the lurker below"); + if (!lurker || lurker->getStandState() != UNIT_STAND_STATE_SUBMERGED) + return 1.0f; + + Group* group = bot->GetGroup(); + if (!group) + return 1.0f; + + uint8 tankCount = 0; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive()) + continue; + + if (botAI->IsTank(member)) + ++tankCount; + } + + if (tankCount >= 3) + { + if (dynamic_cast(action)) + return 0.0f; + } + + return 1.0f; +} + +// Leotheras the Blind + +float LeotherasTheBlindAvoidWhirlwindMultiplier::GetValue(Action* action) +{ + if (botAI->IsTank(bot)) + return 1.0f; + + if (bot->HasAura(SPELL_INSIDIOUS_WHISPER)) + return 1.0f; + + Unit* leotherasHuman = GetLeotherasHuman(botAI); + if (!leotherasHuman) + return 1.0f; + + if (!leotherasHuman->HasAura(SPELL_LEOTHERAS_BANISHED) && + (leotherasHuman->HasAura(SPELL_WHIRLWIND) || + leotherasHuman->HasAura(SPELL_WHIRLWIND_CHANNEL))) + { + if (dynamic_cast(action)) + return 0.0f; + + if (dynamic_cast(action) && + !dynamic_cast(action) && + !dynamic_cast(action)) + return 0.0f; + } + + return 1.0f; +} + +float LeotherasTheBlindDisableTankActionsMultiplier::GetValue(Action* action) +{ + if (!botAI->IsTank(bot) || bot->HasAura(SPELL_INSIDIOUS_WHISPER)) + return 1.0f; + + if (!AI_VALUE2(Unit*, "find target", "leotheras the blind")) + return 1.0f; + + if (GetPhase2LeotherasDemon(botAI) && dynamic_cast(action)) + return 0.0f; + + if (!GetPhase3LeotherasDemon(botAI) && dynamic_cast(action)) + return 0.0f; + + return 1.0f; +} + +float LeotherasTheBlindFocusOnInnerDemonMultiplier::GetValue(Action* action) +{ + if (bot->HasAura(SPELL_INSIDIOUS_WHISPER)) + { + if (dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + 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 LeotherasTheBlindMeleeDpsAvoidChaosBlastMultiplier::GetValue(Action* action) +{ + if (botAI->IsRanged(bot) || botAI->IsTank(bot)) + return 1.0f; + + if (!GetPhase2LeotherasDemon(botAI)) + return 1.0f; + + Aura* chaosBlast = bot->GetAura(SPELL_CHAOS_BLAST); + if (chaosBlast && chaosBlast->GetStackAmount() >= 5) + { + if (dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action)) + return 0.0f; + } + + return 1.0f; +} + +float LeotherasTheBlindWaitForDpsMultiplier::GetValue(Action* action) +{ + Unit* leotheras = AI_VALUE2(Unit*, "find target", "leotheras the blind"); + if (!leotheras) + return 1.0f; + + if (bot->HasAura(SPELL_INSIDIOUS_WHISPER)) + return 1.0f; + + if (dynamic_cast(action)) + return 1.0f; + + const uint32 instanceId = leotheras->GetMap()->GetInstanceId(); + const time_t now = std::time(nullptr); + + constexpr uint8 dpsWaitSecondsPhase1 = 5; + Unit* leotherasHuman = GetLeotherasHuman(botAI); + Unit* leotherasPhase3Demon = GetPhase3LeotherasDemon(botAI); + if (leotherasHuman && !leotherasHuman->HasAura(SPELL_LEOTHERAS_BANISHED) && + !leotherasPhase3Demon) + { + if (botAI->IsTank(bot)) + return 1.0f; + + auto it = leotherasHumanFormDpsWaitTimer.find(instanceId); + if (it == leotherasHumanFormDpsWaitTimer.end() || + (now - it->second) < dpsWaitSecondsPhase1) + { + if (dynamic_cast(action) || + (dynamic_cast(action) && + !dynamic_cast(action))) + return 0.0f; + } + } + + constexpr uint8 dpsWaitSecondsPhase2 = 12; + Unit* leotherasPhase2Demon = GetPhase2LeotherasDemon(botAI); + Player* demonFormTank = GetLeotherasDemonFormTank(bot); + if (leotherasPhase2Demon) + { + if (demonFormTank && demonFormTank == bot) + return 1.0f; + + if (!demonFormTank && botAI->IsTank(bot)) + return 1.0f; + + auto it = leotherasDemonFormDpsWaitTimer.find(instanceId); + if (it == leotherasDemonFormDpsWaitTimer.end() || + (now - it->second) < dpsWaitSecondsPhase2) + { + if (dynamic_cast(action) || + (dynamic_cast(action) && + !dynamic_cast(action))) + return 0.0f; + } + } + + constexpr uint8 dpsWaitSecondsPhase3 = 8; + if (leotherasPhase3Demon) + { + if ((demonFormTank && demonFormTank == bot) || botAI->IsTank(bot)) + return 1.0f; + + auto it = leotherasFinalPhaseDpsWaitTimer.find(instanceId); + if (it == leotherasFinalPhaseDpsWaitTimer.end() || + (now - it->second) < dpsWaitSecondsPhase3) + { + if (dynamic_cast(action) || + (dynamic_cast(action) && + !dynamic_cast(action))) + return 0.0f; + } + } + + return 1.0f; +} + +// Don't use Bloodlust/Heroism during the Channeler phase +float LeotherasTheBlindDelayBloodlustAndHeroismMultiplier::GetValue(Action* action) +{ + if (bot->getClass() != CLASS_SHAMAN) + return 1.0f; + + Unit* leotheras = AI_VALUE2(Unit*, "find target", "leotheras the blind"); + if (leotheras && leotheras->HasAura(SPELL_LEOTHERAS_BANISHED)) + { + if (dynamic_cast(action) || + dynamic_cast(action)) + return 0.0f; + } + + return 1.0f; +} + +// Fathom-Lord Karathress + +float FathomLordKarathressDisableTankActionsMultiplier::GetValue(Action* action) +{ + if (!botAI->IsTank(bot)) + return 1.0f; + + if (!AI_VALUE2(Unit*, "find target", "fathom-lord karathress")) + return 1.0f; + + if (bot->GetVictim() != nullptr && dynamic_cast(action)) + return 0.0f; + + if (dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + 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 FathomLordKarathressDisableAoeMultiplier::GetValue(Action* action) +{ + if (!botAI->IsDps(bot)) + return 1.0f; + + if (AI_VALUE2(Unit*, "find target", "fathom-lord karathress")) + { + if (auto castSpellAction = dynamic_cast(action)) + { + if (castSpellAction->getThreatType() == Action::ActionThreatType::Aoe) + return 0.0f; + } + } + + return 1.0f; +} + +float FathomLordKarathressControlMisdirectionMultiplier::GetValue(Action* action) +{ + if (bot->getClass() != CLASS_HUNTER) + return 1.0f; + + if (AI_VALUE2(Unit*, "find target", "fathom-lord karathress")) + { + if (dynamic_cast(action)) + return 0.0f; + } + + return 1.0f; +} + +float FathomLordKarathressWaitForDpsMultiplier::GetValue(Action* action) +{ + if (botAI->IsTank(bot)) + return 1.0f; + + Unit* karathress = AI_VALUE2(Unit*, "find target", "fathom-lord karathress"); + if (!karathress) + return 1.0f; + + if (dynamic_cast(action)) + return 1.0f; + + const time_t now = std::time(nullptr); + constexpr uint8 dpsWaitSeconds = 12; + + auto it = karathressDpsWaitTimer.find(karathress->GetMap()->GetInstanceId()); + if (it == karathressDpsWaitTimer.end() || (now - it->second) < dpsWaitSeconds) + { + if (dynamic_cast(action) || + (dynamic_cast(action) && + !dynamic_cast(action))) + return 0.0f; + } + + return 1.0f; +} + +float FathomLordKarathressCaribdisTankHealerMaintainPositionMultiplier::GetValue(Action* action) +{ + if (!botAI->IsAssistHealOfIndex(bot, 0, true)) + return 1.0f; + + if (AI_VALUE2(Unit*, "find target", "fathom-guard caribdis")) + { + if (dynamic_cast(action) || + dynamic_cast(action)) + return 0.0f; + } + + return 1.0f; +} + +// Morogrim Tidewalker + +// Use Bloodlust/Heroism after the first Murloc spawn +float MorogrimTidewalkerDelayBloodlustAndHeroismMultiplier::GetValue(Action* action) +{ + if (bot->getClass() != CLASS_SHAMAN) + return 1.0f; + + if (!AI_VALUE2(Unit*, "find target", "morogrim tidewalker")) + return 1.0f; + + if (!AI_VALUE2(Unit*, "find target", "tidewalker lurker")) + { + if (dynamic_cast(action) || + dynamic_cast(action)) + return 0.0f; + } + + return 1.0f; +} + +float MorogrimTidewalkerDisableTankActionsMultiplier::GetValue(Action* action) +{ + if (!botAI->IsMainTank(bot)) + return 1.0f; + + if (AI_VALUE2(Unit*, "find target", "morogrim tidewalker")) + { + if (dynamic_cast(action)) + return 0.0f; + } + + return 1.0f; +} + +float MorogrimTidewalkerMaintainPhase2StackingMultiplier::GetValue(Action* action) +{ + if (!botAI->IsRanged(bot)) + return 1.0f; + + Unit* tidewalker = AI_VALUE2(Unit*, "find target", "morogrim tidewalker"); + if (!tidewalker) + return 1.0f; + + if (tidewalker->GetHealthPct() < 25.0f) + { + if (dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action)) + return 0.0f; + } + + return 1.0f; +} + +// Lady Vashj + +// Wait until phase 3 to use Bloodlust/Heroism +// Don't use other major cooldowns in Phase 1, either +float LadyVashjDelayCooldownsMultiplier::GetValue(Action* action) +{ + if (!AI_VALUE2(Unit*, "find target", "lady vashj")) + return 1.0f; + + if (bot->getClass() == CLASS_SHAMAN) + { + if (IsLadyVashjInPhase3(botAI)) + return 1.0f; + + if (dynamic_cast(action) || + dynamic_cast(action)) + return 0.0f; + } + + if (botAI->IsDps(bot) && IsLadyVashjInPhase1(botAI)) + { + if (dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + 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 LadyVashjMaintainPhase1RangedSpreadMultiplier::GetValue(Action* action) +{ + if (!botAI->IsRanged(bot)) + return 1.0f; + + if (AI_VALUE2(Unit*, "find target", "lady vashj") && + IsLadyVashjInPhase1(botAI)) + { + if (dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action)) + return 0.0f; + } + + return 1.0f; +} + +float LadyVashjStaticChargeStayAwayFromGroupMultiplier::GetValue(Action* action) +{ + if (botAI->IsMainTank(bot) || !bot->HasAura(SPELL_STATIC_CHARGE)) + return 1.0f; + + if (!AI_VALUE2(Unit*, "find target", "lady vashj")) + return 1.0f; + + if (dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action)) + return 0.0f; + + return 1.0f; +} + +// Bots should not loot the core with normal looting logic +float LadyVashjDoNotLootTheTaintedCoreMultiplier::GetValue(Action* action) +{ + if (AI_VALUE2(Unit*, "find target", "lady vashj")) + { + if (dynamic_cast(action)) + return 0.0f; + } + + return 1.0f; +} + +float LadyVashjCorePassersPrioritizePositioningMultiplier::GetValue(Action* action) +{ + if (!AI_VALUE2(Unit*, "find target", "lady vashj") || + !IsLadyVashjInPhase2(botAI)) + return 1.0f; + + if (dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action)) + return 1.0f; + + Group* group = bot->GetGroup(); + if (!group) + return 1.0f; + + Player* designatedLooter = GetDesignatedCoreLooter(group, botAI); + Player* firstCorePasser = GetFirstTaintedCorePasser(group, botAI); + Player* secondCorePasser = GetSecondTaintedCorePasser(group, botAI); + Player* thirdCorePasser = GetThirdTaintedCorePasser(group, botAI); + Player* fourthCorePasser = GetFourthTaintedCorePasser(group, botAI); + + auto hasCore = [](Player* player) + { + return player && player->HasItemCount(ITEM_TAINTED_CORE, 1, false); + }; + + if (hasCore(bot)) + { + if (!dynamic_cast(action)) + return 0.0f; + } + + if (bot == designatedLooter) + { + if (!hasCore(bot)) + return 1.0f; + } + else if (bot == firstCorePasser) + { + if (hasCore(secondCorePasser) || hasCore(thirdCorePasser) || + hasCore(fourthCorePasser)) + return 1.0f; + } + else if (bot == secondCorePasser) + { + if (hasCore(thirdCorePasser) || hasCore(fourthCorePasser)) + return 1.0f; + } + else if (bot == thirdCorePasser) + { + if (hasCore(fourthCorePasser)) + return 1.0f; + } + else if (bot != fourthCorePasser) + return 1.0f; + + if (AI_VALUE2(Unit*, "find target", "tainted elemental") && + (bot == firstCorePasser || bot == secondCorePasser)) + { + if (dynamic_cast(action) && + !dynamic_cast(action)) + return 0.0f; + } + + if (AnyRecentCoreInInventory(group, botAI)) + { + if (dynamic_cast(action) && + !dynamic_cast(action)) + return 0.0f; + } + + return 1.0f; +} + +// All of phases 2 and 3 require a custom movement and targeting system +// So the standard target selection system must be disabled +float LadyVashjDisableAutomaticTargetingAndMovementModifier::GetValue(Action *action) +{ + if (!AI_VALUE2(Unit*, "find target", "lady vashj")) + return 1.0f; + + if (dynamic_cast(action)) + return 0.0f; + + if (IsLadyVashjInPhase2(botAI)) + { + if (dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action)) + return 0.0f; + + if (!botAI->IsHeal(bot) && dynamic_cast(action)) + return 0.0f; + + Unit* enchanted = AI_VALUE2(Unit*, "find target", "enchanted elemental"); + if (enchanted && bot->GetVictim() == enchanted) + { + if (dynamic_cast(action)) + return 0.0f; + } + } + + if (IsLadyVashjInPhase3(botAI)) + { + if (dynamic_cast(action)) + return 0.0f; + + Unit* enchanted = AI_VALUE2(Unit*, "find target", "enchanted elemental"); + Unit* strider = AI_VALUE2(Unit*, "find target", "coilfang strider"); + Unit* elite = AI_VALUE2(Unit*, "find target", "coilfang elite"); + if (enchanted || strider || elite) + { + if (dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action)) + return 0.0f; + + if (enchanted && bot->GetVictim() == enchanted) + { + if (dynamic_cast(action)) + return 0.0f; + } + } + else if (dynamic_cast(action)) + return 0.0f; + } + + return 1.0f; +} diff --git a/src/Ai/Raid/SerpentshrineCavern/Multiplier/RaidSSCMultipliers.h b/src/Ai/Raid/SerpentshrineCavern/Multiplier/RaidSSCMultipliers.h new file mode 100644 index 0000000000..6630dc2060 --- /dev/null +++ b/src/Ai/Raid/SerpentshrineCavern/Multiplier/RaidSSCMultipliers.h @@ -0,0 +1,236 @@ +#ifndef _PLAYERBOT_RAIDSSCMULTIPLIERS_H +#define _PLAYERBOT_RAIDSSCMULTIPLIERS_H + +#include "Multiplier.h" + +// Trash + +class UnderbogColossusEscapeToxicPoolMultiplier : public Multiplier +{ +public: + UnderbogColossusEscapeToxicPoolMultiplier( + PlayerbotAI* botAI) : Multiplier(botAI, "underbog colossus escape toxic pool") {} + virtual float GetValue(Action* action); +}; + +// Hydross the Unstable + +class HydrossTheUnstableDisableTankActionsMultiplier : public Multiplier +{ +public: + HydrossTheUnstableDisableTankActionsMultiplier( + PlayerbotAI* botAI) : Multiplier(botAI, "hydross the unstable disable tank actions") {} + virtual float GetValue(Action* action); +}; + +class HydrossTheUnstableWaitForDpsMultiplier : public Multiplier +{ +public: + HydrossTheUnstableWaitForDpsMultiplier( + PlayerbotAI* botAI) : Multiplier(botAI, "hydross the unstable wait for dps") {} + virtual float GetValue(Action* action); +}; + +class HydrossTheUnstableControlMisdirectionMultiplier : public Multiplier +{ +public: + HydrossTheUnstableControlMisdirectionMultiplier( + PlayerbotAI* botAI) : Multiplier(botAI, "hydross the unstable control misdirection") {} + virtual float GetValue(Action* action); +}; + +// The Lurker Below + +class TheLurkerBelowStayAwayFromSpoutMultiplier : public Multiplier +{ +public: + TheLurkerBelowStayAwayFromSpoutMultiplier( + PlayerbotAI* botAI) : Multiplier(botAI, "the lurker below stay away from spout") {} + virtual float GetValue(Action* action); +}; + +class TheLurkerBelowMaintainRangedSpreadMultiplier : public Multiplier +{ +public: + TheLurkerBelowMaintainRangedSpreadMultiplier( + PlayerbotAI* botAI) : Multiplier(botAI, "the lurker below maintain ranged spread") {} + virtual float GetValue(Action* action); +}; + +class TheLurkerBelowDisableTankAssistMultiplier : public Multiplier +{ +public: + TheLurkerBelowDisableTankAssistMultiplier( + PlayerbotAI* botAI) : Multiplier(botAI, "the lurker below disable tank assist") {} + virtual float GetValue(Action* action); +}; + +// Leotheras the Blind + +class LeotherasTheBlindAvoidWhirlwindMultiplier : public Multiplier +{ +public: + LeotherasTheBlindAvoidWhirlwindMultiplier( + PlayerbotAI* botAI) : Multiplier(botAI, "leotheras the blind avoid whirlwind") {} + virtual float GetValue(Action* action); +}; + +class LeotherasTheBlindDisableTankActionsMultiplier : public Multiplier +{ +public: + LeotherasTheBlindDisableTankActionsMultiplier( + PlayerbotAI* botAI) : Multiplier(botAI, "leotheras the blind disable tank actions") {} + virtual float GetValue(Action* action); +}; + +class LeotherasTheBlindMeleeDpsAvoidChaosBlastMultiplier : public Multiplier +{ +public: + LeotherasTheBlindMeleeDpsAvoidChaosBlastMultiplier( + PlayerbotAI* botAI) : Multiplier(botAI, "leotheras the blind melee dps avoid chaos blast") {} + virtual float GetValue(Action* action); +}; + +class LeotherasTheBlindFocusOnInnerDemonMultiplier : public Multiplier +{ +public: + LeotherasTheBlindFocusOnInnerDemonMultiplier( + PlayerbotAI* botAI) : Multiplier(botAI, "leotheras the blind focus on inner demon") {} + virtual float GetValue(Action* action); +}; + +class LeotherasTheBlindWaitForDpsMultiplier : public Multiplier +{ +public: + LeotherasTheBlindWaitForDpsMultiplier( + PlayerbotAI* botAI) : Multiplier(botAI, "leotheras the blind wait for dps") {} + virtual float GetValue(Action* action); +}; + +class LeotherasTheBlindDelayBloodlustAndHeroismMultiplier : public Multiplier +{ +public: + LeotherasTheBlindDelayBloodlustAndHeroismMultiplier( + PlayerbotAI* botAI) : Multiplier(botAI, "leotheras the blind delay bloodlust and heroism") {} + virtual float GetValue(Action* action); +}; + +// Fathom-Lord Karathress + +class FathomLordKarathressDisableTankActionsMultiplier : public Multiplier +{ +public: + FathomLordKarathressDisableTankActionsMultiplier( + PlayerbotAI* botAI) : Multiplier(botAI, "fathom-lord karathress disable tank actions") {} + virtual float GetValue(Action* action); +}; + +class FathomLordKarathressDisableAoeMultiplier : public Multiplier +{ +public: + FathomLordKarathressDisableAoeMultiplier( + PlayerbotAI* botAI) : Multiplier(botAI, "fathom-lord karathress disable aoe") {} + virtual float GetValue(Action* action); +}; + +class FathomLordKarathressControlMisdirectionMultiplier : public Multiplier +{ +public: + FathomLordKarathressControlMisdirectionMultiplier( + PlayerbotAI* botAI) : Multiplier(botAI, "fathom-lord karathress control misdirection") {} + virtual float GetValue(Action* action); +}; + +class FathomLordKarathressWaitForDpsMultiplier : public Multiplier +{ +public: + FathomLordKarathressWaitForDpsMultiplier( + PlayerbotAI* botAI) : Multiplier(botAI, "fathom-lord karathress wait for dps") {} + virtual float GetValue(Action* action); +}; + +class FathomLordKarathressCaribdisTankHealerMaintainPositionMultiplier : public Multiplier +{ +public: + FathomLordKarathressCaribdisTankHealerMaintainPositionMultiplier( + PlayerbotAI* botAI) : Multiplier(botAI, "fathom-lord karathress caribdis tank healer maintain position") {} + virtual float GetValue(Action* action); +}; + +// Morogrim Tidewalker + +class MorogrimTidewalkerDelayBloodlustAndHeroismMultiplier : public Multiplier +{ +public: + MorogrimTidewalkerDelayBloodlustAndHeroismMultiplier( + PlayerbotAI* botAI) : Multiplier(botAI, "morogrim tidewalker delay bloodlust and heroism") {} + virtual float GetValue(Action* action); +}; + +class MorogrimTidewalkerDisableTankActionsMultiplier : public Multiplier +{ +public: + MorogrimTidewalkerDisableTankActionsMultiplier( + PlayerbotAI* botAI) : Multiplier(botAI, "morogrim tidewalker disable tank actions") {} + virtual float GetValue(Action* action); +}; + +class MorogrimTidewalkerMaintainPhase2StackingMultiplier : public Multiplier +{ +public: + MorogrimTidewalkerMaintainPhase2StackingMultiplier( + PlayerbotAI* botAI) : Multiplier(botAI, "morogrim tidewalker maintain phase2 stacking") {} + virtual float GetValue(Action* action); +}; + +// Lady Vashj + +class LadyVashjDelayCooldownsMultiplier : public Multiplier +{ +public: + LadyVashjDelayCooldownsMultiplier( + PlayerbotAI* botAI) : Multiplier(botAI, "lady vashj delay cooldowns") {} + virtual float GetValue(Action* action); +}; + +class LadyVashjMaintainPhase1RangedSpreadMultiplier : public Multiplier +{ +public: + LadyVashjMaintainPhase1RangedSpreadMultiplier( + PlayerbotAI* botAI) : Multiplier(botAI, "lady vashj maintain phase1 ranged spread") {} + virtual float GetValue(Action* action); +}; + +class LadyVashjStaticChargeStayAwayFromGroupMultiplier : public Multiplier +{ +public: + LadyVashjStaticChargeStayAwayFromGroupMultiplier( + PlayerbotAI* botAI) : Multiplier(botAI, "lady vashj static charge stay away from group") {} + virtual float GetValue(Action* action); +}; + +class LadyVashjDoNotLootTheTaintedCoreMultiplier : public Multiplier +{ +public: + LadyVashjDoNotLootTheTaintedCoreMultiplier( + PlayerbotAI* botAI) : Multiplier(botAI, "lady vashj do not loot the tainted core") {} + virtual float GetValue(Action* action); +}; + +class LadyVashjCorePassersPrioritizePositioningMultiplier : public Multiplier +{ +public: + LadyVashjCorePassersPrioritizePositioningMultiplier( + PlayerbotAI* botAI) : Multiplier(botAI, "lady vashj core passers prioritize positioning") {} + virtual float GetValue(Action* action); +}; + +class LadyVashjDisableAutomaticTargetingAndMovementModifier : public Multiplier +{ +public: + LadyVashjDisableAutomaticTargetingAndMovementModifier( + PlayerbotAI* botAI) : Multiplier(botAI, "lady vashj disable automatic targeting and movement") {} + virtual float GetValue(Action* action); +}; + +#endif diff --git a/src/Ai/Raid/SerpentshrineCavern/RaidSSCActionContext.h b/src/Ai/Raid/SerpentshrineCavern/RaidSSCActionContext.h new file mode 100644 index 0000000000..e6dce16949 --- /dev/null +++ b/src/Ai/Raid/SerpentshrineCavern/RaidSSCActionContext.h @@ -0,0 +1,337 @@ +#ifndef _PLAYERBOT_RAIDSSCACTIONCONTEXT_H +#define _PLAYERBOT_RAIDSSCACTIONCONTEXT_H + +#include "RaidSSCActions.h" +#include "NamedObjectContext.h" + +class RaidSSCActionContext : public NamedObjectContext +{ +public: + RaidSSCActionContext() + { + // General + creators["serpent shrine cavern erase timers and trackers"] = + &RaidSSCActionContext::serpent_shrine_cavern_erase_timers_and_trackers; + + // Trash + creators["underbog colossus escape toxic pool"] = + &RaidSSCActionContext::underbog_colossus_escape_toxic_pool; + + creators["greyheart tidecaller mark water elemental totem"] = + &RaidSSCActionContext::greyheart_tidecaller_mark_water_elemental_totem; + + // Hydross the Unstable + creators["hydross the unstable position frost tank"] = + &RaidSSCActionContext::hydross_the_unstable_position_frost_tank; + + creators["hydross the unstable position nature tank"] = + &RaidSSCActionContext::hydross_the_unstable_position_nature_tank; + + creators["hydross the unstable prioritize elemental adds"] = + &RaidSSCActionContext::hydross_the_unstable_prioritize_elemental_adds; + + creators["hydross the unstable frost phase spread out"] = + &RaidSSCActionContext::hydross_the_unstable_frost_phase_spread_out; + + creators["hydross the unstable misdirect boss to tank"] = + &RaidSSCActionContext::hydross_the_unstable_misdirect_boss_to_tank; + + creators["hydross the unstable stop dps upon phase change"] = + &RaidSSCActionContext::hydross_the_unstable_stop_dps_upon_phase_change; + + creators["hydross the unstable manage timers"] = + &RaidSSCActionContext::hydross_the_unstable_manage_timers; + + // The Lurker Below + creators["the lurker below run around behind boss"] = + &RaidSSCActionContext::the_lurker_below_run_around_behind_boss; + + creators["the lurker below position main tank"] = + &RaidSSCActionContext::the_lurker_below_position_main_tank; + + creators["the lurker below spread ranged in arc"] = + &RaidSSCActionContext::the_lurker_below_spread_ranged_in_arc; + + creators["the lurker below tanks pick up adds"] = + &RaidSSCActionContext::the_lurker_below_tanks_pick_up_adds; + + creators["the lurker below manage spout timer"] = + &RaidSSCActionContext::the_lurker_below_manage_spout_timer; + + // Leotheras the Blind + creators["leotheras the blind target spellbinders"] = + &RaidSSCActionContext::leotheras_the_blind_target_spellbinders; + + creators["leotheras the blind demon form tank attack boss"] = + &RaidSSCActionContext::leotheras_the_blind_demon_form_tank_attack_boss; + + creators["leotheras the blind melee tanks don't attack demon form"] = + &RaidSSCActionContext::leotheras_the_blind_melee_tanks_dont_attack_demon_form; + + creators["leotheras the blind position ranged"] = + &RaidSSCActionContext::leotheras_the_blind_position_ranged; + + creators["leotheras the blind run away from whirlwind"] = + &RaidSSCActionContext::leotheras_the_blind_run_away_from_whirlwind; + + creators["leotheras the blind melee dps run away from boss"] = + &RaidSSCActionContext::leotheras_the_blind_melee_dps_run_away_from_boss; + + creators["leotheras the blind destroy inner demon"] = + &RaidSSCActionContext::leotheras_the_blind_destroy_inner_demon; + + creators["leotheras the blind final phase assign dps priority"] = + &RaidSSCActionContext::leotheras_the_blind_final_phase_assign_dps_priority; + + creators["leotheras the blind misdirect boss to demon form tank"] = + &RaidSSCActionContext::leotheras_the_blind_misdirect_boss_to_demon_form_tank; + + creators["leotheras the blind manage dps wait timers"] = + &RaidSSCActionContext::leotheras_the_blind_manage_dps_wait_timers; + + // Fathom-Lord Karathress + creators["fathom-lord karathress main tank position boss"] = + &RaidSSCActionContext::fathom_lord_karathress_main_tank_position_boss; + + creators["fathom-lord karathress first assist tank position caribdis"] = + &RaidSSCActionContext::fathom_lord_karathress_first_assist_tank_position_caribdis; + + creators["fathom-lord karathress second assist tank position sharkkis"] = + &RaidSSCActionContext::fathom_lord_karathress_second_assist_tank_position_sharkkis; + + creators["fathom-lord karathress third assist tank position tidalvess"] = + &RaidSSCActionContext::fathom_lord_karathress_third_assist_tank_position_tidalvess; + + creators["fathom-lord karathress position caribdis tank healer"] = + &RaidSSCActionContext::fathom_lord_karathress_position_caribdis_tank_healer; + + creators["fathom-lord karathress misdirect bosses to tanks"] = + &RaidSSCActionContext::fathom_lord_karathress_misdirect_bosses_to_tanks; + + creators["fathom-lord karathress assign dps priority"] = + &RaidSSCActionContext::fathom_lord_karathress_assign_dps_priority; + + creators["fathom-lord karathress manage dps timer"] = + &RaidSSCActionContext::fathom_lord_karathress_manage_dps_timer; + + // Morogrim Tidewalker + creators["morogrim tidewalker misdirect boss to main tank"] = + &RaidSSCActionContext::morogrim_tidewalker_misdirect_boss_to_main_tank; + + creators["morogrim tidewalker move boss to tank position"] = + &RaidSSCActionContext::morogrim_tidewalker_move_boss_to_tank_position; + + creators["morogrim tidewalker phase 2 reposition ranged"] = + &RaidSSCActionContext::morogrim_tidewalker_phase_2_reposition_ranged; + + // Lady Vashj + creators["lady vashj main tank position boss"] = + &RaidSSCActionContext::lady_vashj_main_tank_position_boss; + + creators["lady vashj phase 1 spread ranged in arc"] = + &RaidSSCActionContext::lady_vashj_phase_1_spread_ranged_in_arc; + + creators["lady vashj set grounding totem in main tank group"] = + &RaidSSCActionContext::lady_vashj_set_grounding_totem_in_main_tank_group; + + creators["lady vashj static charge move away from group"] = + &RaidSSCActionContext::lady_vashj_static_charge_move_away_from_group; + + creators["lady vashj misdirect boss to main tank"] = + &RaidSSCActionContext::lady_vashj_misdirect_boss_to_main_tank; + + creators["lady vashj assign phase 2 and phase 3 dps priority"] = + &RaidSSCActionContext::lady_vashj_assign_phase_2_and_phase_3_dps_priority; + + creators["lady vashj misdirect strider to first assist tank"] = + &RaidSSCActionContext::lady_vashj_misdirect_strider_to_first_assist_tank; + + creators["lady vashj tank attack and move away strider"] = + &RaidSSCActionContext::lady_vashj_tank_attack_and_move_away_strider; + + creators["lady vashj loot tainted core"] = + &RaidSSCActionContext::lady_vashj_loot_tainted_core; + + creators["lady vashj teleport to tainted elemental"] = + &RaidSSCActionContext::lady_vashj_teleport_to_tainted_elemental; + + creators["lady vashj pass the tainted core"] = + &RaidSSCActionContext::lady_vashj_pass_the_tainted_core; + + creators["lady vashj destroy tainted core"] = + &RaidSSCActionContext::lady_vashj_destroy_tainted_core; + + creators["lady vashj erase core passing trackers"] = + &RaidSSCActionContext::lady_vashj_erase_core_passing_trackers; + + creators["lady vashj avoid toxic spores"] = + &RaidSSCActionContext::lady_vashj_avoid_toxic_spores; + + creators["lady vashj use free action abilities"] = + &RaidSSCActionContext::lady_vashj_use_free_action_abilities; + } + +private: + // General + static Action* serpent_shrine_cavern_erase_timers_and_trackers( + PlayerbotAI* botAI) { return new SerpentShrineCavernEraseTimersAndTrackersAction(botAI); } + + // Trash + static Action* underbog_colossus_escape_toxic_pool( + PlayerbotAI* botAI) { return new UnderbogColossusEscapeToxicPoolAction(botAI); } + + static Action* greyheart_tidecaller_mark_water_elemental_totem( + PlayerbotAI* botAI) { return new GreyheartTidecallerMarkWaterElementalTotemAction(botAI); } + + // Hydross the Unstable + static Action* hydross_the_unstable_position_frost_tank( + PlayerbotAI* botAI) { return new HydrossTheUnstablePositionFrostTankAction(botAI); } + + static Action* hydross_the_unstable_position_nature_tank( + PlayerbotAI* botAI) { return new HydrossTheUnstablePositionNatureTankAction(botAI); } + + static Action* hydross_the_unstable_prioritize_elemental_adds( + PlayerbotAI* botAI) { return new HydrossTheUnstablePrioritizeElementalAddsAction(botAI); } + + static Action* hydross_the_unstable_frost_phase_spread_out( + PlayerbotAI* botAI) { return new HydrossTheUnstableFrostPhaseSpreadOutAction(botAI); } + + static Action* hydross_the_unstable_misdirect_boss_to_tank( + PlayerbotAI* botAI) { return new HydrossTheUnstableMisdirectBossToTankAction(botAI); } + + static Action* hydross_the_unstable_stop_dps_upon_phase_change( + PlayerbotAI* botAI) { return new HydrossTheUnstableStopDpsUponPhaseChangeAction(botAI); } + + static Action* hydross_the_unstable_manage_timers( + PlayerbotAI* botAI) { return new HydrossTheUnstableManageTimersAction(botAI); } + + // The Lurker Below + static Action* the_lurker_below_run_around_behind_boss( + PlayerbotAI* botAI) { return new TheLurkerBelowRunAroundBehindBossAction(botAI); } + + static Action* the_lurker_below_position_main_tank( + PlayerbotAI* botAI) { return new TheLurkerBelowPositionMainTankAction(botAI); } + + static Action* the_lurker_below_spread_ranged_in_arc( + PlayerbotAI* botAI) { return new TheLurkerBelowSpreadRangedInArcAction(botAI); } + + static Action* the_lurker_below_tanks_pick_up_adds( + PlayerbotAI* botAI) { return new TheLurkerBelowTanksPickUpAddsAction(botAI); } + + static Action* the_lurker_below_manage_spout_timer( + PlayerbotAI* botAI) { return new TheLurkerBelowManageSpoutTimerAction(botAI); } + + // Leotheras the Blind + static Action* leotheras_the_blind_target_spellbinders( + PlayerbotAI* botAI) { return new LeotherasTheBlindTargetSpellbindersAction(botAI); } + + static Action* leotheras_the_blind_demon_form_tank_attack_boss( + PlayerbotAI* botAI) { return new LeotherasTheBlindDemonFormTankAttackBossAction(botAI); } + + static Action* leotheras_the_blind_melee_tanks_dont_attack_demon_form( + PlayerbotAI* botAI) { return new LeotherasTheBlindMeleeTanksDontAttackDemonFormAction(botAI); } + + static Action* leotheras_the_blind_position_ranged( + PlayerbotAI* botAI) { return new LeotherasTheBlindPositionRangedAction(botAI); } + + static Action* leotheras_the_blind_run_away_from_whirlwind( + PlayerbotAI* botAI) { return new LeotherasTheBlindRunAwayFromWhirlwindAction(botAI); } + + static Action* leotheras_the_blind_melee_dps_run_away_from_boss( + PlayerbotAI* botAI) { return new LeotherasTheBlindMeleeDpsRunAwayFromBossAction(botAI); } + + static Action* leotheras_the_blind_destroy_inner_demon( + PlayerbotAI* botAI) { return new LeotherasTheBlindDestroyInnerDemonAction(botAI); } + + static Action* leotheras_the_blind_misdirect_boss_to_demon_form_tank( + PlayerbotAI* botAI) { return new LeotherasTheBlindMisdirectBossToDemonFormTankAction(botAI); } + + static Action* leotheras_the_blind_final_phase_assign_dps_priority( + PlayerbotAI* botAI) { return new LeotherasTheBlindFinalPhaseAssignDpsPriorityAction(botAI); } + + static Action* leotheras_the_blind_manage_dps_wait_timers( + PlayerbotAI* botAI) { return new LeotherasTheBlindManageDpsWaitTimersAction(botAI); } + + // Fathom-Lord Karathress + static Action* fathom_lord_karathress_main_tank_position_boss( + PlayerbotAI* botAI) { return new FathomLordKarathressMainTankPositionBossAction(botAI); } + + static Action* fathom_lord_karathress_first_assist_tank_position_caribdis( + PlayerbotAI* botAI) { return new FathomLordKarathressFirstAssistTankPositionCaribdisAction(botAI); } + + static Action* fathom_lord_karathress_second_assist_tank_position_sharkkis( + PlayerbotAI* botAI) { return new FathomLordKarathressSecondAssistTankPositionSharkkisAction(botAI); } + + static Action* fathom_lord_karathress_third_assist_tank_position_tidalvess( + PlayerbotAI* botAI) { return new FathomLordKarathressThirdAssistTankPositionTidalvessAction(botAI); } + + static Action* fathom_lord_karathress_position_caribdis_tank_healer( + PlayerbotAI* botAI) { return new FathomLordKarathressPositionCaribdisTankHealerAction(botAI); } + + static Action* fathom_lord_karathress_misdirect_bosses_to_tanks( + PlayerbotAI* botAI) { return new FathomLordKarathressMisdirectBossesToTanksAction(botAI); } + + static Action* fathom_lord_karathress_assign_dps_priority( + PlayerbotAI* botAI) { return new FathomLordKarathressAssignDpsPriorityAction(botAI); } + + static Action* fathom_lord_karathress_manage_dps_timer( + PlayerbotAI* botAI) { return new FathomLordKarathressManageDpsTimerAction(botAI); } + + // Morogrim Tidewalker + static Action* morogrim_tidewalker_misdirect_boss_to_main_tank( + PlayerbotAI* botAI) { return new MorogrimTidewalkerMisdirectBossToMainTankAction(botAI); } + + static Action* morogrim_tidewalker_move_boss_to_tank_position( + PlayerbotAI* botAI) { return new MorogrimTidewalkerMoveBossToTankPositionAction(botAI); } + + static Action* morogrim_tidewalker_phase_2_reposition_ranged( + PlayerbotAI* botAI) { return new MorogrimTidewalkerPhase2RepositionRangedAction(botAI); } + + // Lady Vashj + static Action* lady_vashj_main_tank_position_boss( + PlayerbotAI* botAI) { return new LadyVashjMainTankPositionBossAction(botAI); } + + static Action* lady_vashj_phase_1_spread_ranged_in_arc( + PlayerbotAI* botAI) { return new LadyVashjPhase1SpreadRangedInArcAction(botAI); } + + static Action* lady_vashj_set_grounding_totem_in_main_tank_group( + PlayerbotAI* botAI) { return new LadyVashjSetGroundingTotemInMainTankGroupAction(botAI); } + + static Action* lady_vashj_static_charge_move_away_from_group( + PlayerbotAI* botAI) { return new LadyVashjStaticChargeMoveAwayFromGroupAction(botAI); } + + static Action* lady_vashj_misdirect_boss_to_main_tank( + PlayerbotAI* botAI) { return new LadyVashjMisdirectBossToMainTankAction(botAI); } + + static Action* lady_vashj_assign_phase_2_and_phase_3_dps_priority( + PlayerbotAI* botAI) { return new LadyVashjAssignPhase2AndPhase3DpsPriorityAction(botAI); } + + static Action* lady_vashj_misdirect_strider_to_first_assist_tank( + PlayerbotAI* botAI) { return new LadyVashjMisdirectStriderToFirstAssistTankAction(botAI); } + + static Action* lady_vashj_tank_attack_and_move_away_strider( + PlayerbotAI* botAI) { return new LadyVashjTankAttackAndMoveAwayStriderAction(botAI); } + + static Action* lady_vashj_teleport_to_tainted_elemental( + PlayerbotAI* botAI) { return new LadyVashjTeleportToTaintedElementalAction(botAI); } + + static Action* lady_vashj_loot_tainted_core( + PlayerbotAI* botAI) { return new LadyVashjLootTaintedCoreAction(botAI); } + + static Action* lady_vashj_pass_the_tainted_core( + PlayerbotAI* botAI) { return new LadyVashjPassTheTaintedCoreAction(botAI); } + + static Action* lady_vashj_destroy_tainted_core( + PlayerbotAI* botAI) { return new LadyVashjDestroyTaintedCoreAction(botAI); } + + static Action* lady_vashj_erase_core_passing_trackers( + PlayerbotAI* botAI) { return new LadyVashjEraseCorePassingTrackersAction(botAI); } + + static Action* lady_vashj_avoid_toxic_spores( + PlayerbotAI* botAI) { return new LadyVashjAvoidToxicSporesAction(botAI); } + + static Action* lady_vashj_use_free_action_abilities( + PlayerbotAI* botAI) { return new LadyVashjUseFreeActionAbilitiesAction(botAI); } +}; + +#endif diff --git a/src/Ai/Raid/SerpentshrineCavern/RaidSSCTriggerContext.h b/src/Ai/Raid/SerpentshrineCavern/RaidSSCTriggerContext.h new file mode 100644 index 0000000000..13135bf3e5 --- /dev/null +++ b/src/Ai/Raid/SerpentshrineCavern/RaidSSCTriggerContext.h @@ -0,0 +1,325 @@ +#ifndef _PLAYERBOT_RAIDSSCTRIGGERCONTEXT_H +#define _PLAYERBOT_RAIDSSCTRIGGERCONTEXT_H + +#include "RaidSSCTriggers.h" +#include "AiObjectContext.h" + +class RaidSSCTriggerContext : public NamedObjectContext +{ +public: + RaidSSCTriggerContext() + { + // General + creators["serpent shrine cavern bot is not in combat"] = + &RaidSSCTriggerContext::serpent_shrine_cavern_bot_is_not_in_combat; + + // Trash + creators["underbog colossus spawned toxic pool after death"] = + &RaidSSCTriggerContext::underbog_colossus_spawned_toxic_pool_after_death; + + creators["greyheart tidecaller water elemental totem spawned"] = + &RaidSSCTriggerContext::greyheart_tidecaller_water_elemental_totem_spawned; + + // Hydross the Unstable + creators["hydross the unstable bot is frost tank"] = + &RaidSSCTriggerContext::hydross_the_unstable_bot_is_frost_tank; + + creators["hydross the unstable bot is nature tank"] = + &RaidSSCTriggerContext::hydross_the_unstable_bot_is_nature_tank; + + creators["hydross the unstable elementals spawned"] = + &RaidSSCTriggerContext::hydross_the_unstable_elementals_spawned; + + creators["hydross the unstable danger from water tombs"] = + &RaidSSCTriggerContext::hydross_the_unstable_danger_from_water_tombs; + + creators["hydross the unstable tank needs aggro upon phase change"] = + &RaidSSCTriggerContext::hydross_the_unstable_tank_needs_aggro_upon_phase_change; + + creators["hydross the unstable aggro resets upon phase change"] = + &RaidSSCTriggerContext::hydross_the_unstable_aggro_resets_upon_phase_change; + + creators["hydross the unstable need to manage timers"] = + &RaidSSCTriggerContext::hydross_the_unstable_need_to_manage_timers; + + // The Lurker Below + creators["the lurker below spout is active"] = + &RaidSSCTriggerContext::the_lurker_below_spout_is_active; + + creators["the lurker below boss is active for main tank"] = + &RaidSSCTriggerContext::the_lurker_below_boss_is_active_for_main_tank; + + creators["the lurker below boss casts geyser"] = + &RaidSSCTriggerContext::the_lurker_below_boss_casts_geyser; + + creators["the lurker below boss is submerged"] = + &RaidSSCTriggerContext::the_lurker_below_boss_is_submerged; + + creators["the lurker below need to prepare timer for spout"] = + &RaidSSCTriggerContext::the_lurker_below_need_to_prepare_timer_for_spout; + + // Leotheras the Blind + creators["leotheras the blind boss is inactive"] = + &RaidSSCTriggerContext::leotheras_the_blind_boss_is_inactive; + + creators["leotheras the blind boss transformed into demon form"] = + &RaidSSCTriggerContext::leotheras_the_blind_boss_transformed_into_demon_form; + + creators["leotheras the blind only warlock should tank demon form"] = + &RaidSSCTriggerContext::leotheras_the_blind_only_warlock_should_tank_demon_form; + + creators["leotheras the blind boss engaged by ranged"] = + &RaidSSCTriggerContext::leotheras_the_blind_boss_engaged_by_ranged; + + creators["leotheras the blind boss channeling whirlwind"] = + &RaidSSCTriggerContext::leotheras_the_blind_boss_channeling_whirlwind; + + creators["leotheras the blind bot has too many chaos blast stacks"] = + &RaidSSCTriggerContext::leotheras_the_blind_bot_has_too_many_chaos_blast_stacks; + + creators["leotheras the blind inner demon has awakened"] = + &RaidSSCTriggerContext::leotheras_the_blind_inner_demon_has_awakened; + + creators["leotheras the blind entered final phase"] = + &RaidSSCTriggerContext::leotheras_the_blind_entered_final_phase; + + creators["leotheras the blind demon form tank needs aggro"] = + &RaidSSCTriggerContext::leotheras_the_blind_demon_form_tank_needs_aggro; + + creators["leotheras the blind boss wipes aggro upon phase change"] = + &RaidSSCTriggerContext::leotheras_the_blind_boss_wipes_aggro_upon_phase_change; + + // Fathom-Lord Karathress + creators["fathom-lord karathress boss engaged by main tank"] = + &RaidSSCTriggerContext::fathom_lord_karathress_boss_engaged_by_main_tank; + + creators["fathom-lord karathress caribdis engaged by first assist tank"] = + &RaidSSCTriggerContext::fathom_lord_karathress_caribdis_engaged_by_first_assist_tank; + + creators["fathom-lord karathress sharkkis engaged by second assist tank"] = + &RaidSSCTriggerContext::fathom_lord_karathress_sharkkis_engaged_by_second_assist_tank; + + creators["fathom-lord karathress tidalvess engaged by third assist tank"] = + &RaidSSCTriggerContext::fathom_lord_karathress_tidalvess_engaged_by_third_assist_tank; + + creators["fathom-lord karathress caribdis tank needs dedicated healer"] = + &RaidSSCTriggerContext::fathom_lord_karathress_caribdis_tank_needs_dedicated_healer; + + creators["fathom-lord karathress pulling bosses"] = + &RaidSSCTriggerContext::fathom_lord_karathress_pulling_bosses; + + creators["fathom-lord karathress determining kill order"] = + &RaidSSCTriggerContext::fathom_lord_karathress_determining_kill_order; + + creators["fathom-lord karathress tanks need to establish aggro"] = + &RaidSSCTriggerContext::fathom_lord_karathress_tanks_need_to_establish_aggro; + + // Morogrim Tidewalker + creators["morogrim tidewalker boss engaged by main tank"] = + &RaidSSCTriggerContext::morogrim_tidewalker_boss_engaged_by_main_tank; + + creators["morogrim tidewalker pulling boss"] = + &RaidSSCTriggerContext::morogrim_tidewalker_pulling_boss; + + creators["morogrim tidewalker water globules are incoming"] = + &RaidSSCTriggerContext::morogrim_tidewalker_water_globules_are_incoming; + + // Lady Vashj + creators["lady vashj boss engaged by main tank"] = + &RaidSSCTriggerContext::lady_vashj_boss_engaged_by_main_tank; + + creators["lady vashj boss engaged by ranged in phase 1"] = + &RaidSSCTriggerContext::lady_vashj_boss_engaged_by_ranged_in_phase_1; + + creators["lady vashj casts shock blast on highest aggro"] = + &RaidSSCTriggerContext::lady_vashj_casts_shock_blast_on_highest_aggro; + + creators["lady vashj bot has static charge"] = + &RaidSSCTriggerContext::lady_vashj_bot_has_static_charge; + + creators["lady vashj pulling boss in phase 1 and phase 3"] = + &RaidSSCTriggerContext::lady_vashj_pulling_boss_in_phase_1_and_phase_3; + + creators["lady vashj adds spawn in phase 2 and phase 3"] = + &RaidSSCTriggerContext::lady_vashj_adds_spawn_in_phase_2_and_phase_3; + + creators["lady vashj coilfang strider is approaching"] = + &RaidSSCTriggerContext::lady_vashj_coilfang_strider_is_approaching; + + creators["lady vashj tainted elemental cheat"] = + &RaidSSCTriggerContext::lady_vashj_tainted_elemental_cheat; + + creators["lady vashj tainted core was looted"] = + &RaidSSCTriggerContext::lady_vashj_tainted_core_was_looted; + + creators["lady vashj tainted core is unusable"] = + &RaidSSCTriggerContext::lady_vashj_tainted_core_is_unusable; + + creators["lady vashj need to reset core passing trackers"] = + &RaidSSCTriggerContext::lady_vashj_need_to_reset_core_passing_trackers; + + creators["lady vashj toxic sporebats are spewing poison clouds"] = + &RaidSSCTriggerContext::lady_vashj_toxic_sporebats_are_spewing_poison_clouds; + + creators["lady vashj bot is entangled in toxic spores or static charge"] = + &RaidSSCTriggerContext::lady_vashj_bot_is_entangled_in_toxic_spores_or_static_charge; + } + +private: + // General + static Trigger* serpent_shrine_cavern_bot_is_not_in_combat( + PlayerbotAI* botAI) { return new SerpentShrineCavernBotIsNotInCombatTrigger(botAI); } + + // Trash + static Trigger* underbog_colossus_spawned_toxic_pool_after_death( + PlayerbotAI* botAI) { return new UnderbogColossusSpawnedToxicPoolAfterDeathTrigger(botAI); } + + static Trigger* greyheart_tidecaller_water_elemental_totem_spawned( + PlayerbotAI* botAI) { return new GreyheartTidecallerWaterElementalTotemSpawnedTrigger(botAI); } + + // Hydross the Unstable + static Trigger* hydross_the_unstable_bot_is_frost_tank( + PlayerbotAI* botAI) { return new HydrossTheUnstableBotIsFrostTankTrigger(botAI); } + + static Trigger* hydross_the_unstable_bot_is_nature_tank( + PlayerbotAI* botAI) { return new HydrossTheUnstableBotIsNatureTankTrigger(botAI); } + + static Trigger* hydross_the_unstable_elementals_spawned( + PlayerbotAI* botAI) { return new HydrossTheUnstableElementalsSpawnedTrigger(botAI); } + + static Trigger* hydross_the_unstable_danger_from_water_tombs( + PlayerbotAI* botAI) { return new HydrossTheUnstableDangerFromWaterTombsTrigger(botAI); } + + static Trigger* hydross_the_unstable_tank_needs_aggro_upon_phase_change( + PlayerbotAI* botAI) { return new HydrossTheUnstableTankNeedsAggroUponPhaseChangeTrigger(botAI); } + + static Trigger* hydross_the_unstable_aggro_resets_upon_phase_change( + PlayerbotAI* botAI) { return new HydrossTheUnstableAggroResetsUponPhaseChangeTrigger(botAI); } + + static Trigger* hydross_the_unstable_need_to_manage_timers( + PlayerbotAI* botAI) { return new HydrossTheUnstableNeedToManageTimersTrigger(botAI); } + + // The Lurker Below + static Trigger* the_lurker_below_spout_is_active( + PlayerbotAI* botAI) { return new TheLurkerBelowSpoutIsActiveTrigger(botAI); } + + static Trigger* the_lurker_below_boss_is_active_for_main_tank( + PlayerbotAI* botAI) { return new TheLurkerBelowBossIsActiveForMainTankTrigger(botAI); } + + static Trigger* the_lurker_below_boss_casts_geyser( + PlayerbotAI* botAI) { return new TheLurkerBelowBossCastsGeyserTrigger(botAI); } + + static Trigger* the_lurker_below_boss_is_submerged( + PlayerbotAI* botAI) { return new TheLurkerBelowBossIsSubmergedTrigger(botAI); } + + static Trigger* the_lurker_below_need_to_prepare_timer_for_spout( + PlayerbotAI* botAI) { return new TheLurkerBelowNeedToPrepareTimerForSpoutTrigger(botAI); } + + // Leotheras the Blind + static Trigger* leotheras_the_blind_boss_is_inactive( + PlayerbotAI* botAI) { return new LeotherasTheBlindBossIsInactiveTrigger(botAI); } + + static Trigger* leotheras_the_blind_boss_transformed_into_demon_form( + PlayerbotAI* botAI) { return new LeotherasTheBlindBossTransformedIntoDemonFormTrigger(botAI); } + + static Trigger* leotheras_the_blind_only_warlock_should_tank_demon_form( + PlayerbotAI* botAI) { return new LeotherasTheBlindOnlyWarlockShouldTankDemonFormTrigger(botAI); } + + static Trigger* leotheras_the_blind_boss_engaged_by_ranged( + PlayerbotAI* botAI) { return new LeotherasTheBlindBossEngagedByRangedTrigger(botAI); } + + static Trigger* leotheras_the_blind_boss_channeling_whirlwind( + PlayerbotAI* botAI) { return new LeotherasTheBlindBossChannelingWhirlwindTrigger(botAI); } + + static Trigger* leotheras_the_blind_bot_has_too_many_chaos_blast_stacks( + PlayerbotAI* botAI) { return new LeotherasTheBlindBotHasTooManyChaosBlastStacksTrigger(botAI); } + + static Trigger* leotheras_the_blind_inner_demon_has_awakened( + PlayerbotAI* botAI) { return new LeotherasTheBlindInnerDemonHasAwakenedTrigger(botAI); } + + static Trigger* leotheras_the_blind_entered_final_phase( + PlayerbotAI* botAI) { return new LeotherasTheBlindEnteredFinalPhaseTrigger(botAI); } + + static Trigger* leotheras_the_blind_demon_form_tank_needs_aggro( + PlayerbotAI* botAI) { return new LeotherasTheBlindDemonFormTankNeedsAggro(botAI); } + + static Trigger* leotheras_the_blind_boss_wipes_aggro_upon_phase_change( + PlayerbotAI* botAI) { return new LeotherasTheBlindBossWipesAggroUponPhaseChangeTrigger(botAI); } + + // Fathom-Lord Karathress + static Trigger* fathom_lord_karathress_boss_engaged_by_main_tank( + PlayerbotAI* botAI) { return new FathomLordKarathressBossEngagedByMainTankTrigger(botAI); } + + static Trigger* fathom_lord_karathress_caribdis_engaged_by_first_assist_tank( + PlayerbotAI* botAI) { return new FathomLordKarathressCaribdisEngagedByFirstAssistTankTrigger(botAI); } + + static Trigger* fathom_lord_karathress_sharkkis_engaged_by_second_assist_tank( + PlayerbotAI* botAI) { return new FathomLordKarathressSharkkisEngagedBySecondAssistTankTrigger(botAI); } + + static Trigger* fathom_lord_karathress_tidalvess_engaged_by_third_assist_tank( + PlayerbotAI* botAI) { return new FathomLordKarathressTidalvessEngagedByThirdAssistTankTrigger(botAI); } + + static Trigger* fathom_lord_karathress_caribdis_tank_needs_dedicated_healer( + PlayerbotAI* botAI) { return new FathomLordKarathressCaribdisTankNeedsDedicatedHealerTrigger(botAI); } + + static Trigger* fathom_lord_karathress_pulling_bosses( + PlayerbotAI* botAI) { return new FathomLordKarathressPullingBossesTrigger(botAI); } + + static Trigger* fathom_lord_karathress_determining_kill_order( + PlayerbotAI* botAI) { return new FathomLordKarathressDeterminingKillOrderTrigger(botAI); } + + static Trigger* fathom_lord_karathress_tanks_need_to_establish_aggro( + PlayerbotAI* botAI) { return new FathomLordKarathressTanksNeedToEstablishAggroTrigger(botAI); } + + // Morogrim Tidewalker + static Trigger* morogrim_tidewalker_boss_engaged_by_main_tank( + PlayerbotAI* botAI) { return new MorogrimTidewalkerBossEngagedByMainTankTrigger(botAI); } + + static Trigger* morogrim_tidewalker_pulling_boss( + PlayerbotAI* botAI) { return new MorogrimTidewalkerPullingBossTrigger(botAI); } + + static Trigger* morogrim_tidewalker_water_globules_are_incoming( + PlayerbotAI* botAI) { return new MorogrimTidewalkerWaterGlobulesAreIncomingTrigger(botAI); } + + // Lady Vashj + static Trigger* lady_vashj_boss_engaged_by_main_tank( + PlayerbotAI* botAI) { return new LadyVashjBossEngagedByMainTankTrigger(botAI); } + + static Trigger* lady_vashj_boss_engaged_by_ranged_in_phase_1( + PlayerbotAI* botAI) { return new LadyVashjBossEngagedByRangedInPhase1Trigger(botAI); } + + static Trigger* lady_vashj_casts_shock_blast_on_highest_aggro( + PlayerbotAI* botAI) { return new LadyVashjCastsShockBlastOnHighestAggroTrigger(botAI); } + + static Trigger* lady_vashj_bot_has_static_charge( + PlayerbotAI* botAI) { return new LadyVashjBotHasStaticChargeTrigger(botAI); } + + static Trigger* lady_vashj_pulling_boss_in_phase_1_and_phase_3( + PlayerbotAI* botAI) { return new LadyVashjPullingBossInPhase1AndPhase3Trigger(botAI); } + + static Trigger* lady_vashj_adds_spawn_in_phase_2_and_phase_3( + PlayerbotAI* botAI) { return new LadyVashjAddsSpawnInPhase2AndPhase3Trigger(botAI); } + + static Trigger* lady_vashj_coilfang_strider_is_approaching( + PlayerbotAI* botAI) { return new LadyVashjCoilfangStriderIsApproachingTrigger(botAI); } + + static Trigger* lady_vashj_tainted_elemental_cheat( + PlayerbotAI* botAI) { return new LadyVashjTaintedElementalCheatTrigger(botAI); } + + static Trigger* lady_vashj_tainted_core_was_looted( + PlayerbotAI* botAI) { return new LadyVashjTaintedCoreWasLootedTrigger(botAI); } + + static Trigger* lady_vashj_tainted_core_is_unusable( + PlayerbotAI* botAI) { return new LadyVashjTaintedCoreIsUnusableTrigger(botAI); } + + static Trigger* lady_vashj_need_to_reset_core_passing_trackers( + PlayerbotAI* botAI) { return new LadyVashjNeedToResetCorePassingTrackersTrigger(botAI); } + + static Trigger* lady_vashj_toxic_sporebats_are_spewing_poison_clouds( + PlayerbotAI* botAI) { return new LadyVashjToxicSporebatsAreSpewingPoisonCloudsTrigger(botAI); } + + static Trigger* lady_vashj_bot_is_entangled_in_toxic_spores_or_static_charge( + PlayerbotAI* botAI) { return new LadyVashjBotIsEntangledInToxicSporesOrStaticChargeTrigger(botAI); } +}; + +#endif diff --git a/src/Ai/Raid/SerpentshrineCavern/Strategy/RaidSSCStrategy.cpp b/src/Ai/Raid/SerpentshrineCavern/Strategy/RaidSSCStrategy.cpp new file mode 100644 index 0000000000..139667dc66 --- /dev/null +++ b/src/Ai/Raid/SerpentshrineCavern/Strategy/RaidSSCStrategy.cpp @@ -0,0 +1,206 @@ +#include "RaidSSCStrategy.h" +#include "RaidSSCMultipliers.h" + +void RaidSSCStrategy::InitTriggers(std::vector& triggers) +{ + // General + triggers.push_back(new TriggerNode("serpent shrine cavern bot is not in combat", { + NextAction("serpent shrine cavern erase timers and trackers", ACTION_EMERGENCY + 11) })); + + // Trash Mobs + triggers.push_back(new TriggerNode("underbog colossus spawned toxic pool after death", { + NextAction("underbog colossus escape toxic pool", ACTION_EMERGENCY + 10) })); + + triggers.push_back(new TriggerNode("greyheart tidecaller water elemental totem spawned", { + NextAction("greyheart tidecaller mark water elemental totem", ACTION_RAID + 1) })); + + // Hydross the Unstable + triggers.push_back(new TriggerNode("hydross the unstable bot is frost tank", { + NextAction("hydross the unstable position frost tank", ACTION_RAID + 1) })); + + triggers.push_back(new TriggerNode("hydross the unstable bot is nature tank", { + NextAction("hydross the unstable position nature tank", ACTION_RAID + 1) })); + + triggers.push_back(new TriggerNode("hydross the unstable elementals spawned", { + NextAction("hydross the unstable prioritize elemental adds", ACTION_RAID + 1) })); + + triggers.push_back(new TriggerNode("hydross the unstable danger from water tombs", { + NextAction("hydross the unstable frost phase spread out", ACTION_EMERGENCY + 1) })); + + triggers.push_back(new TriggerNode("hydross the unstable tank needs aggro upon phase change", { + NextAction("hydross the unstable misdirect boss to tank", ACTION_EMERGENCY + 6) })); + + triggers.push_back(new TriggerNode("hydross the unstable aggro resets upon phase change", { + NextAction("hydross the unstable stop dps upon phase change", ACTION_EMERGENCY + 9) })); + + triggers.push_back(new TriggerNode("hydross the unstable need to manage timers", { + NextAction("hydross the unstable manage timers", ACTION_EMERGENCY + 10) })); + + // The Lurker Below + triggers.push_back(new TriggerNode("the lurker below spout is active", { + NextAction("the lurker below run around behind boss", ACTION_EMERGENCY + 6) })); + + triggers.push_back(new TriggerNode("the lurker below boss is active for main tank", { + NextAction("the lurker below position main tank", ACTION_RAID + 1) })); + + triggers.push_back(new TriggerNode("the lurker below boss casts geyser", { + NextAction("the lurker below spread ranged in arc", ACTION_RAID + 1) })); + + triggers.push_back(new TriggerNode("the lurker below boss is submerged", { + NextAction("the lurker below tanks pick up adds", ACTION_EMERGENCY + 1) })); + + triggers.push_back(new TriggerNode("the lurker below need to prepare timer for spout", { + NextAction("the lurker below manage spout timer", ACTION_EMERGENCY + 10) })); + + // Leotheras the Blind + triggers.push_back(new TriggerNode("leotheras the blind boss is inactive", { + NextAction("leotheras the blind target spellbinders", ACTION_RAID + 1) })); + + triggers.push_back(new TriggerNode("leotheras the blind boss transformed into demon form", { + NextAction("leotheras the blind demon form tank attack boss", ACTION_RAID + 1) })); + + triggers.push_back(new TriggerNode("leotheras the blind only warlock should tank demon form", { + NextAction("leotheras the blind melee tanks don't attack demon form", ACTION_RAID + 1) })); + + triggers.push_back(new TriggerNode("leotheras the blind boss engaged by ranged", { + NextAction("leotheras the blind position ranged", ACTION_RAID + 1) })); + + triggers.push_back(new TriggerNode("leotheras the blind boss channeling whirlwind", { + NextAction("leotheras the blind run away from whirlwind", ACTION_EMERGENCY + 1) })); + + triggers.push_back(new TriggerNode("leotheras the blind bot has too many chaos blast stacks", { + NextAction("leotheras the blind melee dps run away from boss", ACTION_EMERGENCY + 6) })); + + triggers.push_back(new TriggerNode("leotheras the blind inner demon has awakened", { + NextAction("leotheras the blind destroy inner demon", ACTION_EMERGENCY + 7) })); + + triggers.push_back(new TriggerNode("leotheras the blind entered final phase", { + NextAction("leotheras the blind final phase assign dps priority", ACTION_RAID + 1) })); + + triggers.push_back(new TriggerNode("leotheras the blind demon form tank needs aggro", { + NextAction("leotheras the blind misdirect boss to demon form tank", ACTION_RAID + 2) })); + + triggers.push_back(new TriggerNode("leotheras the blind boss wipes aggro upon phase change", { + NextAction("leotheras the blind manage dps wait timers", ACTION_EMERGENCY + 10) })); + + // Fathom-Lord Karathress + triggers.push_back(new TriggerNode("fathom-lord karathress boss engaged by main tank", { + NextAction("fathom-lord karathress main tank position boss", ACTION_RAID + 1) })); + + triggers.push_back(new TriggerNode("fathom-lord karathress caribdis engaged by first assist tank", { + NextAction("fathom-lord karathress first assist tank position caribdis", ACTION_RAID + 1) })); + + triggers.push_back(new TriggerNode("fathom-lord karathress sharkkis engaged by second assist tank", { + NextAction("fathom-lord karathress second assist tank position sharkkis", ACTION_RAID + 1) })); + + triggers.push_back(new TriggerNode("fathom-lord karathress tidalvess engaged by third assist tank", { + NextAction("fathom-lord karathress third assist tank position tidalvess", ACTION_RAID + 1) })); + + triggers.push_back(new TriggerNode("fathom-lord karathress caribdis tank needs dedicated healer", { + NextAction("fathom-lord karathress position caribdis tank healer", ACTION_RAID + 1) })); + + triggers.push_back(new TriggerNode("fathom-lord karathress pulling bosses", { + NextAction("fathom-lord karathress misdirect bosses to tanks", ACTION_RAID + 2) })); + + triggers.push_back(new TriggerNode("fathom-lord karathress determining kill order", { + NextAction("fathom-lord karathress assign dps priority", ACTION_RAID + 1) })); + + triggers.push_back(new TriggerNode("fathom-lord karathress tanks need to establish aggro", { + NextAction("fathom-lord karathress manage dps timer", ACTION_EMERGENCY + 10) })); + + // Morogrim Tidewalker + triggers.push_back(new TriggerNode("morogrim tidewalker boss engaged by main tank", { + NextAction("morogrim tidewalker move boss to tank position", ACTION_RAID + 1) })); + + triggers.push_back(new TriggerNode("morogrim tidewalker water globules are incoming", { + NextAction("morogrim tidewalker phase 2 reposition ranged", ACTION_RAID + 1) })); + + triggers.push_back(new TriggerNode("morogrim tidewalker pulling boss", { + NextAction("morogrim tidewalker misdirect boss to main tank", ACTION_RAID + 1) })); + + // Lady Vashj + triggers.push_back(new TriggerNode("lady vashj boss engaged by main tank", { + NextAction("lady vashj main tank position boss", ACTION_RAID + 1) })); + + triggers.push_back(new TriggerNode("lady vashj boss engaged by ranged in phase 1", { + NextAction("lady vashj phase 1 spread ranged in arc", ACTION_RAID + 1) })); + + triggers.push_back(new TriggerNode("lady vashj casts shock blast on highest aggro", { + NextAction("lady vashj set grounding totem in main tank group", ACTION_EMERGENCY + 1) })); + + triggers.push_back(new TriggerNode("lady vashj bot has static charge", { + NextAction("lady vashj static charge move away from group", ACTION_EMERGENCY + 7) })); + + triggers.push_back(new TriggerNode("lady vashj pulling boss in phase 1 and phase 3", { + NextAction("lady vashj misdirect boss to main tank", ACTION_RAID + 2) })); + + triggers.push_back(new TriggerNode("lady vashj tainted elemental cheat", { + NextAction("lady vashj teleport to tainted elemental", ACTION_EMERGENCY + 10), + NextAction("lady vashj loot tainted core", ACTION_EMERGENCY + 10) })); + + triggers.push_back(new TriggerNode("lady vashj tainted core was looted", { + NextAction("lady vashj pass the tainted core", ACTION_EMERGENCY + 10) })); + + triggers.push_back(new TriggerNode("lady vashj tainted core is unusable", { + NextAction("lady vashj destroy tainted core", ACTION_EMERGENCY + 1) })); + + triggers.push_back(new TriggerNode("lady vashj need to reset core passing trackers", { + NextAction("lady vashj erase core passing trackers", ACTION_EMERGENCY + 10) })); + + triggers.push_back(new TriggerNode("lady vashj adds spawn in phase 2 and phase 3", { + NextAction("lady vashj assign phase 2 and phase 3 dps priority", ACTION_RAID + 1) })); + + triggers.push_back(new TriggerNode("lady vashj coilfang strider is approaching", { + NextAction("lady vashj misdirect strider to first assist tank", ACTION_EMERGENCY + 2), + NextAction("lady vashj tank attack and move away strider", ACTION_EMERGENCY + 1) })); + + triggers.push_back(new TriggerNode("lady vashj toxic sporebats are spewing poison clouds", { + NextAction("lady vashj avoid toxic spores", ACTION_EMERGENCY + 6) })); + + triggers.push_back(new TriggerNode("lady vashj bot is entangled in toxic spores or static charge", { + NextAction("lady vashj use free action abilities", ACTION_EMERGENCY + 7) })); +} + +void RaidSSCStrategy::InitMultipliers(std::vector& multipliers) +{ + // Trash Mobs + multipliers.push_back(new UnderbogColossusEscapeToxicPoolMultiplier(botAI)); + + // Hydross the Unstable + multipliers.push_back(new HydrossTheUnstableDisableTankActionsMultiplier(botAI)); + multipliers.push_back(new HydrossTheUnstableWaitForDpsMultiplier(botAI)); + multipliers.push_back(new HydrossTheUnstableControlMisdirectionMultiplier(botAI)); + + // The Lurker Below + multipliers.push_back(new TheLurkerBelowStayAwayFromSpoutMultiplier(botAI)); + multipliers.push_back(new TheLurkerBelowMaintainRangedSpreadMultiplier(botAI)); + multipliers.push_back(new TheLurkerBelowDisableTankAssistMultiplier(botAI)); + + // Leotheras the Blind + multipliers.push_back(new LeotherasTheBlindAvoidWhirlwindMultiplier(botAI)); + multipliers.push_back(new LeotherasTheBlindDisableTankActionsMultiplier(botAI)); + multipliers.push_back(new LeotherasTheBlindMeleeDpsAvoidChaosBlastMultiplier(botAI)); + multipliers.push_back(new LeotherasTheBlindFocusOnInnerDemonMultiplier(botAI)); + multipliers.push_back(new LeotherasTheBlindWaitForDpsMultiplier(botAI)); + multipliers.push_back(new LeotherasTheBlindDelayBloodlustAndHeroismMultiplier(botAI)); + + // Fathom-Lord Karathress + multipliers.push_back(new FathomLordKarathressDisableTankActionsMultiplier(botAI)); + multipliers.push_back(new FathomLordKarathressDisableAoeMultiplier(botAI)); + multipliers.push_back(new FathomLordKarathressControlMisdirectionMultiplier(botAI)); + multipliers.push_back(new FathomLordKarathressWaitForDpsMultiplier(botAI)); + multipliers.push_back(new FathomLordKarathressCaribdisTankHealerMaintainPositionMultiplier(botAI)); + + // Morogrim Tidewalker + multipliers.push_back(new MorogrimTidewalkerDelayBloodlustAndHeroismMultiplier(botAI)); + multipliers.push_back(new MorogrimTidewalkerDisableTankActionsMultiplier(botAI)); + multipliers.push_back(new MorogrimTidewalkerMaintainPhase2StackingMultiplier(botAI)); + + // Lady Vashj + multipliers.push_back(new LadyVashjDelayCooldownsMultiplier(botAI)); + multipliers.push_back(new LadyVashjMaintainPhase1RangedSpreadMultiplier(botAI)); + multipliers.push_back(new LadyVashjStaticChargeStayAwayFromGroupMultiplier(botAI)); + multipliers.push_back(new LadyVashjDoNotLootTheTaintedCoreMultiplier(botAI)); + multipliers.push_back(new LadyVashjCorePassersPrioritizePositioningMultiplier(botAI)); + multipliers.push_back(new LadyVashjDisableAutomaticTargetingAndMovementModifier(botAI)); +} diff --git a/src/Ai/Raid/SerpentshrineCavern/Strategy/RaidSSCStrategy.h b/src/Ai/Raid/SerpentshrineCavern/Strategy/RaidSSCStrategy.h new file mode 100644 index 0000000000..3c2c05f586 --- /dev/null +++ b/src/Ai/Raid/SerpentshrineCavern/Strategy/RaidSSCStrategy.h @@ -0,0 +1,18 @@ +#ifndef _PLAYERBOT_RAIDSSCSTRATEGY_H_ +#define _PLAYERBOT_RAIDSSCSTRATEGY_H_ + +#include "Strategy.h" +#include "Multiplier.h" + +class RaidSSCStrategy : public Strategy +{ +public: + RaidSSCStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} + + std::string const getName() override { return "ssc"; } + + void InitTriggers(std::vector& triggers) override; + void InitMultipliers(std::vector& multipliers) override; +}; + +#endif diff --git a/src/Ai/Raid/SerpentshrineCavern/Trigger/RaidSSCTriggers.cpp b/src/Ai/Raid/SerpentshrineCavern/Trigger/RaidSSCTriggers.cpp new file mode 100644 index 0000000000..e77e63642e --- /dev/null +++ b/src/Ai/Raid/SerpentshrineCavern/Trigger/RaidSSCTriggers.cpp @@ -0,0 +1,670 @@ +#include "RaidSSCTriggers.h" +#include "RaidSSCHelpers.h" +#include "RaidSSCActions.h" +#include "AiFactory.h" +#include "Corpse.h" +#include "LootObjectStack.h" +#include "ObjectAccessor.h" +#include "Playerbots.h" +#include "RaidBossHelpers.h" + +using namespace SerpentShrineCavernHelpers; + +// General +bool SerpentShrineCavernBotIsNotInCombatTrigger::IsActive() +{ + return !bot->IsInCombat(); +} + +// Trash Mobs + +bool UnderbogColossusSpawnedToxicPoolAfterDeathTrigger::IsActive() +{ + return bot->HasAura(SPELL_TOXIC_POOL); +} + +bool GreyheartTidecallerWaterElementalTotemSpawnedTrigger::IsActive() +{ + return botAI->IsDps(bot) && + GetFirstAliveUnitByEntry(botAI, NPC_WATER_ELEMENTAL_TOTEM); +} + +// Hydross the Unstable + +bool HydrossTheUnstableBotIsFrostTankTrigger::IsActive() +{ + return AI_VALUE2(Unit*, "find target", "hydross the unstable") && + botAI->IsMainTank(bot); +} + +bool HydrossTheUnstableBotIsNatureTankTrigger::IsActive() +{ + return AI_VALUE2(Unit*, "find target", "hydross the unstable") && + botAI->IsAssistTankOfIndex(bot, 0, true); +} + +bool HydrossTheUnstableElementalsSpawnedTrigger::IsActive() +{ + Unit* hydross = AI_VALUE2(Unit*, "find target", "hydross the unstable"); + if (hydross && hydross->GetHealthPct() < 10.0f) + return false; + + if (!AI_VALUE2(Unit*, "find target", "pure spawn of hydross") && + !AI_VALUE2(Unit*, "find target", "tainted spawn of hydross")) + return false; + + return !botAI->IsHeal(bot) && !botAI->IsMainTank(bot) && + !botAI->IsAssistTankOfIndex(bot, 0, true); +} + +bool HydrossTheUnstableDangerFromWaterTombsTrigger::IsActive() +{ + return botAI->IsRanged(bot) && + AI_VALUE2(Unit*, "find target", "hydross the unstable"); +} + +bool HydrossTheUnstableTankNeedsAggroUponPhaseChangeTrigger::IsActive() +{ + return bot->getClass() == CLASS_HUNTER && + AI_VALUE2(Unit*, "find target", "hydross the unstable"); +} + +bool HydrossTheUnstableAggroResetsUponPhaseChangeTrigger::IsActive() +{ + if (!AI_VALUE2(Unit*, "find target", "hydross the unstable")) + return false; + + return bot->getClass() != CLASS_HUNTER && + !botAI->IsHeal(bot) && + !botAI->IsMainTank(bot) && + !botAI->IsAssistTankOfIndex(bot, 0, true); +} + +bool HydrossTheUnstableNeedToManageTimersTrigger::IsActive() +{ + return AI_VALUE2(Unit*, "find target", "hydross the unstable") && + IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID, nullptr); +} + +// The Lurker Below + +bool TheLurkerBelowSpoutIsActiveTrigger::IsActive() +{ + Unit* lurker = AI_VALUE2(Unit*, "find target", "the lurker below"); + if (!lurker) + return false; + + const time_t now = std::time(nullptr); + + auto it = lurkerSpoutTimer.find(lurker->GetMap()->GetInstanceId()); + return it != lurkerSpoutTimer.end() && it->second > now; +} + +bool TheLurkerBelowBossIsActiveForMainTankTrigger::IsActive() +{ + Unit* lurker = AI_VALUE2(Unit*, "find target", "the lurker below"); + if (!lurker) + return false; + + if (!botAI->IsMainTank(bot)) + return false; + + const time_t now = std::time(nullptr); + + auto it = lurkerSpoutTimer.find(lurker->GetMap()->GetInstanceId()); + return lurker->getStandState() != UNIT_STAND_STATE_SUBMERGED && + (it == lurkerSpoutTimer.end() || it->second <= now); +} + +bool TheLurkerBelowBossCastsGeyserTrigger::IsActive() +{ + if (!botAI->IsRanged(bot)) + return false; + + Unit* lurker = AI_VALUE2(Unit*, "find target", "the lurker below"); + if (!lurker) + return false; + + const time_t now = std::time(nullptr); + + auto it = lurkerSpoutTimer.find(lurker->GetMap()->GetInstanceId()); + return lurker->getStandState() != UNIT_STAND_STATE_SUBMERGED && + (it == lurkerSpoutTimer.end() || it->second <= now); +} + +// Trigger will be active only if there are at least 3 tanks in the raid +bool TheLurkerBelowBossIsSubmergedTrigger::IsActive() +{ + Unit* lurker = AI_VALUE2(Unit*, "find target", "the lurker below"); + if (!lurker || lurker->getStandState() != UNIT_STAND_STATE_SUBMERGED) + return false; + + Player* mainTank = nullptr; + Player* firstAssistTank = nullptr; + Player* secondAssistTank = nullptr; + + 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; + + PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member); + if (!memberAI) + continue; + + if (!mainTank && memberAI->IsMainTank(member)) + mainTank = member; + else if (!firstAssistTank && memberAI->IsAssistTankOfIndex(member, 0, true)) + firstAssistTank = member; + else if (!secondAssistTank && memberAI->IsAssistTankOfIndex(member, 1, true)) + secondAssistTank = member; + } + + if (!mainTank || !firstAssistTank || !secondAssistTank) + return false; + + return bot == mainTank || bot == firstAssistTank || bot == secondAssistTank; +} + +bool TheLurkerBelowNeedToPrepareTimerForSpoutTrigger::IsActive() +{ + return AI_VALUE2(Unit*, "find target", "the lurker below") && + IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID, nullptr); +} + +// Leotheras the Blind + +bool LeotherasTheBlindBossIsInactiveTrigger::IsActive() +{ + return AI_VALUE2(Unit*, "find target", "greyheart spellbinder"); +} + +bool LeotherasTheBlindBossTransformedIntoDemonFormTrigger::IsActive() +{ + if (!AI_VALUE2(Unit*, "find target", "leotheras the blind")) + return false; + + if (GetLeotherasDemonFormTank(bot) != bot) + return false; + + return GetActiveLeotherasDemon(botAI); +} + +bool LeotherasTheBlindOnlyWarlockShouldTankDemonFormTrigger::IsActive() +{ + if (botAI->IsRanged(bot) || !botAI->IsTank(bot)) + return false; + + if (!AI_VALUE2(Unit*, "find target", "leotheras the blind")) + return false; + + if (bot->HasAura(SPELL_INSIDIOUS_WHISPER)) + return false; + + if (!GetLeotherasDemonFormTank(bot)) + return false; + + return GetPhase2LeotherasDemon(botAI); +} + +bool LeotherasTheBlindBossEngagedByRangedTrigger::IsActive() +{ + if (bot->HasAura(SPELL_INSIDIOUS_WHISPER)) + return false; + + if (!botAI->IsRanged(bot)) + return false; + + Unit* leotheras = AI_VALUE2(Unit*, "find target", "leotheras the blind"); + if (!leotheras) + return false; + + return !leotheras->HasAura(SPELL_LEOTHERAS_BANISHED) && + !leotheras->HasAura(SPELL_WHIRLWIND) && + !leotheras->HasAura(SPELL_WHIRLWIND_CHANNEL); +} + +bool LeotherasTheBlindBossChannelingWhirlwindTrigger::IsActive() +{ + if (bot->HasAura(SPELL_INSIDIOUS_WHISPER)) + return false; + + if (botAI->IsTank(bot) && botAI->IsMelee(bot)) + return false; + + Unit* leotheras = AI_VALUE2(Unit*, "find target", "leotheras the blind"); + if (!leotheras || leotheras->HasAura(SPELL_LEOTHERAS_BANISHED)) + return false; + + return leotheras->HasAura(SPELL_WHIRLWIND) || + leotheras->HasAura(SPELL_WHIRLWIND_CHANNEL); +} + +bool LeotherasTheBlindBotHasTooManyChaosBlastStacksTrigger::IsActive() +{ + if (bot->HasAura(SPELL_INSIDIOUS_WHISPER)) + return false; + + if (botAI->IsRanged(bot)) + return false; + + Aura* chaosBlast = bot->GetAura(SPELL_CHAOS_BLAST); + if (!chaosBlast || chaosBlast->GetStackAmount() < 5) + return false; + + if (!GetLeotherasDemonFormTank(bot) && botAI->IsMainTank(bot)) + return false; + + return GetPhase2LeotherasDemon(botAI); +} + +bool LeotherasTheBlindInnerDemonHasAwakenedTrigger::IsActive() +{ + return bot->HasAura(SPELL_INSIDIOUS_WHISPER) && + GetLeotherasDemonFormTank(bot) != bot; +} + +bool LeotherasTheBlindEnteredFinalPhaseTrigger::IsActive() +{ + if (bot->HasAura(SPELL_INSIDIOUS_WHISPER)) + return false; + + if (botAI->IsHeal(bot)) + return false; + + if (GetLeotherasDemonFormTank(bot) == bot) + return false; + + return GetPhase3LeotherasDemon(botAI) && + GetLeotherasHuman(botAI); +} + +bool LeotherasTheBlindDemonFormTankNeedsAggro::IsActive() +{ + if (bot->HasAura(SPELL_INSIDIOUS_WHISPER)) + return false; + + if (bot->getClass() != CLASS_HUNTER) + return false; + + return AI_VALUE2(Unit*, "find target", "leotheras the blind"); +} + +bool LeotherasTheBlindBossWipesAggroUponPhaseChangeTrigger::IsActive() +{ + return AI_VALUE2(Unit*, "find target", "leotheras the blind") && + IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID, nullptr); +} + +// Fathom-Lord Karathress + +bool FathomLordKarathressBossEngagedByMainTankTrigger::IsActive() +{ + return AI_VALUE2(Unit*, "find target", "fathom-lord karathress") && + botAI->IsMainTank(bot); +} + +bool FathomLordKarathressCaribdisEngagedByFirstAssistTankTrigger::IsActive() +{ + return AI_VALUE2(Unit*, "find target", "fathom-guard caribdis") && + botAI->IsAssistTankOfIndex(bot, 0, false); +} + +bool FathomLordKarathressSharkkisEngagedBySecondAssistTankTrigger::IsActive() +{ + return AI_VALUE2(Unit*, "find target", "fathom-guard sharkkis") && + botAI->IsAssistTankOfIndex(bot, 1, false); +} + +bool FathomLordKarathressTidalvessEngagedByThirdAssistTankTrigger::IsActive() +{ + return AI_VALUE2(Unit*, "find target", "fathom-guard tidalvess") && + botAI->IsAssistTankOfIndex(bot, 2, false); +} + +bool FathomLordKarathressCaribdisTankNeedsDedicatedHealerTrigger::IsActive() +{ + Unit* caribdis = AI_VALUE2(Unit*, "find target", "fathom-guard caribdis"); + if (!caribdis) + return false; + + if (!botAI->IsAssistHealOfIndex(bot, 0, true)) + return false; + + Player* firstAssistTank = nullptr; + 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->IsAssistTankOfIndex(member, 0, false)) + { + firstAssistTank = member; + break; + } + } + } + + return firstAssistTank; +} + +bool FathomLordKarathressPullingBossesTrigger::IsActive() +{ + if (bot->getClass() != CLASS_HUNTER) + return false; + + Unit* karathress = AI_VALUE2(Unit*, "find target", "fathom-lord karathress"); + return karathress && karathress->GetHealthPct() > 98.0f; +} + +bool FathomLordKarathressDeterminingKillOrderTrigger::IsActive() +{ + if (!AI_VALUE2(Unit*, "find target", "fathom-lord karathress")) + return false; + + if (botAI->IsHeal(bot)) + return false; + + if (botAI->IsDps(bot)) + return true; + else if (botAI->IsAssistTankOfIndex(bot, 0, false)) + return !AI_VALUE2(Unit*, "find target", "fathom-guard caribdis"); + else if (botAI->IsAssistTankOfIndex(bot, 1, false)) + return !AI_VALUE2(Unit*, "find target", "fathom-guard sharkkis"); + else if (botAI->IsAssistTankOfIndex(bot, 2, false)) + return !AI_VALUE2(Unit*, "find target", "fathom-guard tidalvess"); + else + return false; +} + +bool FathomLordKarathressTanksNeedToEstablishAggroTrigger::IsActive() +{ + return AI_VALUE2(Unit*, "find target", "fathom-lord karathress") && + IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID, nullptr); +} + +// Morogrim Tidewalker + +bool MorogrimTidewalkerPullingBossTrigger::IsActive() +{ + if (bot->getClass() != CLASS_HUNTER) + return false; + + Unit* tidewalker = AI_VALUE2(Unit*, "find target", "morogrim tidewalker"); + return tidewalker && tidewalker->GetHealthPct() > 95.0f; +} + +bool MorogrimTidewalkerBossEngagedByMainTankTrigger::IsActive() +{ + return AI_VALUE2(Unit*, "find target", "morogrim tidewalker") && + botAI->IsMainTank(bot); +} + +bool MorogrimTidewalkerWaterGlobulesAreIncomingTrigger::IsActive() +{ + if (!botAI->IsRanged(bot)) + return false; + + Unit* tidewalker = AI_VALUE2(Unit*, "find target", "morogrim tidewalker"); + return tidewalker && tidewalker->GetHealthPct() < 25.0f; +} + +// Lady Vashj + +bool LadyVashjBossEngagedByMainTankTrigger::IsActive() +{ + return AI_VALUE2(Unit*, "find target", "lady vashj") && + !IsLadyVashjInPhase2(botAI) && botAI->IsMainTank(bot); +} + +bool LadyVashjBossEngagedByRangedInPhase1Trigger::IsActive() +{ + return botAI->IsRanged(bot) && IsLadyVashjInPhase1(botAI); +} + +bool LadyVashjCastsShockBlastOnHighestAggroTrigger::IsActive() +{ + if (bot->getClass() != CLASS_SHAMAN) + return false; + + if (!AI_VALUE2(Unit*, "find target", "lady vashj") || + IsLadyVashjInPhase2(botAI)) + return false; + + if (!IsMainTankInSameSubgroup(bot)) + return false; + + return true; +} + +bool LadyVashjBotHasStaticChargeTrigger::IsActive() +{ + if (!AI_VALUE2(Unit*, "find target", "lady vashj")) + return false; + + if (Group* group = bot->GetGroup()) + { + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (member && member->HasAura(SPELL_STATIC_CHARGE)) + return true; + } + } + + return false; +} + +bool LadyVashjPullingBossInPhase1AndPhase3Trigger::IsActive() +{ + if (bot->getClass() != CLASS_HUNTER) + return false; + + Unit* vashj = AI_VALUE2(Unit*, "find target", "lady vashj"); + if (!vashj) + return false; + + return (vashj->GetHealthPct() <= 100.0f && vashj->GetHealthPct() > 90.0f) || + (!vashj->HasUnitState(UNIT_STATE_ROOT) && vashj->GetHealthPct() <= 50.0f && + vashj->GetHealthPct() > 40.0f); +} + +bool LadyVashjAddsSpawnInPhase2AndPhase3Trigger::IsActive() +{ + if (botAI->IsHeal(bot)) + return false; + + return AI_VALUE2(Unit*, "find target", "lady vashj") && + !IsLadyVashjInPhase1(botAI); +} + +bool LadyVashjCoilfangStriderIsApproachingTrigger::IsActive() +{ + return AI_VALUE2(Unit*, "find target", "coilfang strider"); +} + +bool LadyVashjTaintedElementalCheatTrigger::IsActive() +{ + if (!botAI->HasCheat(BotCheatMask::raid)) + return false; + + if (!AI_VALUE2(Unit*, "find target", "lady vashj")) + return false; + + bool taintedPresent = false; + Unit* taintedUnit = AI_VALUE2(Unit*, "find target", "tainted elemental"); + if (taintedUnit) + taintedPresent = true; + else + { + GuidVector corpses = AI_VALUE(GuidVector, "nearest corpses"); + for (auto const& guid : corpses) + { + LootObject loot(bot, guid); + WorldObject* object = loot.GetWorldObject(bot); + if (!object) + continue; + + if (Creature* creature = object->ToCreature()) + { + if (creature->GetEntry() == NPC_TAINTED_ELEMENTAL && !creature->IsAlive()) + { + taintedPresent = true; + break; + } + } + } + } + + if (!taintedPresent) + return false; + + Group* group = bot->GetGroup(); + if (!group) + return false; + + return (GetDesignatedCoreLooter(group, botAI) == bot && + !bot->HasItemCount(ITEM_TAINTED_CORE, 1, false)); +} + +bool LadyVashjTaintedCoreWasLootedTrigger::IsActive() +{ + if (!AI_VALUE2(Unit*, "find target", "lady vashj") || !IsLadyVashjInPhase2(botAI)) + return false; + + Group* group = bot->GetGroup(); + if (!group) + return false; + + Player* designatedLooter = GetDesignatedCoreLooter(group, botAI); + Player* firstCorePasser = GetFirstTaintedCorePasser(group, botAI); + Player* secondCorePasser = GetSecondTaintedCorePasser(group, botAI); + Player* thirdCorePasser = GetThirdTaintedCorePasser(group, botAI); + Player* fourthCorePasser = GetFourthTaintedCorePasser(group, botAI); + + auto hasCore = [](Player* player) -> bool + { + return player && player->HasItemCount(ITEM_TAINTED_CORE, 1, false); + }; + + if (bot == designatedLooter) + { + if (!hasCore(bot)) + return false; + } + else if (bot == firstCorePasser) + { + if (hasCore(secondCorePasser) || hasCore(thirdCorePasser) || + hasCore(fourthCorePasser)) + return false; + } + else if (bot == secondCorePasser) + { + if (hasCore(thirdCorePasser) || hasCore(fourthCorePasser)) + return false; + } + else if (bot == thirdCorePasser) + { + if (hasCore(fourthCorePasser)) + return false; + } + else if (bot != fourthCorePasser) + return false; + + if (AnyRecentCoreInInventory(group, botAI)) + return true; + + // First and second passers move to positions as soon as the elemental appears + if (AI_VALUE2(Unit*, "find target", "tainted elemental") && + (bot == firstCorePasser || bot == secondCorePasser)) + return true; + + return false; +} + +bool LadyVashjTaintedCoreIsUnusableTrigger::IsActive() +{ + Unit* vashj = AI_VALUE2(Unit*, "find target", "lady vashj"); + if (!vashj) + return false; + + if (!IsLadyVashjInPhase2(botAI)) + return bot->HasItemCount(ITEM_TAINTED_CORE, 1, false); + + Group* group = bot->GetGroup(); + if (!group) + return false; + + Player* coreHandlers[] = + { + GetDesignatedCoreLooter(group, botAI), + GetFirstTaintedCorePasser(group, botAI), + GetSecondTaintedCorePasser(group, botAI), + GetThirdTaintedCorePasser(group, botAI), + GetFourthTaintedCorePasser(group, botAI) + }; + + if (bot->HasItemCount(ITEM_TAINTED_CORE, 1, false)) + { + for (Player* coreHandler : coreHandlers) + { + if (coreHandler && bot == coreHandler) + return false; + } + return true; + } + + return false; +} + +bool LadyVashjNeedToResetCorePassingTrackersTrigger::IsActive() +{ + Unit* vashj = AI_VALUE2(Unit*, "find target", "lady vashj"); + if (!vashj || IsLadyVashjInPhase2(botAI)) + return false; + + Group* group = bot->GetGroup(); + if (!group) + return false; + + return IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID, nullptr) || + GetDesignatedCoreLooter(group, botAI) == bot || + GetFirstTaintedCorePasser(group, botAI) == bot || + GetSecondTaintedCorePasser(group, botAI) == bot || + GetThirdTaintedCorePasser(group, botAI) == bot || + GetFourthTaintedCorePasser(group, botAI) == bot; +} + +bool LadyVashjToxicSporebatsAreSpewingPoisonCloudsTrigger::IsActive() +{ + return IsLadyVashjInPhase3(botAI); +} + +bool LadyVashjBotIsEntangledInToxicSporesOrStaticChargeTrigger::IsActive() +{ + if (!AI_VALUE2(Unit*, "find target", "lady vashj")) + return false; + + if (Group* group = bot->GetGroup()) + { + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->HasAura(SPELL_ENTANGLE)) + continue; + + if (botAI->IsMelee(member)) + return true; + } + } + + return false; +} diff --git a/src/Ai/Raid/SerpentshrineCavern/Trigger/RaidSSCTriggers.h b/src/Ai/Raid/SerpentshrineCavern/Trigger/RaidSSCTriggers.h new file mode 100644 index 0000000000..e106b58f3e --- /dev/null +++ b/src/Ai/Raid/SerpentshrineCavern/Trigger/RaidSSCTriggers.h @@ -0,0 +1,414 @@ +#ifndef _PLAYERBOT_RAIDSSCTRIGGERS_H +#define _PLAYERBOT_RAIDSSCTRIGGERS_H + +#include "Trigger.h" + +// General + +class SerpentShrineCavernBotIsNotInCombatTrigger : public Trigger +{ +public: + SerpentShrineCavernBotIsNotInCombatTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "serpent shrine cavern bot is not in combat") {} + bool IsActive() override; +}; + +// Trash + +class UnderbogColossusSpawnedToxicPoolAfterDeathTrigger : public Trigger +{ +public: + UnderbogColossusSpawnedToxicPoolAfterDeathTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "underbog colossus spawned toxic pool after death") {} + bool IsActive() override; +}; + +class GreyheartTidecallerWaterElementalTotemSpawnedTrigger : public Trigger +{ +public: + GreyheartTidecallerWaterElementalTotemSpawnedTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "greyheart tidecaller water elemental totem spawned") {} + bool IsActive() override; +}; + +// Hydross the Unstable + +class HydrossTheUnstableBotIsFrostTankTrigger : public Trigger +{ +public: + HydrossTheUnstableBotIsFrostTankTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "hydross the unstable bot is frost tank") {} + bool IsActive() override; +}; + +class HydrossTheUnstableBotIsNatureTankTrigger : public Trigger +{ +public: + HydrossTheUnstableBotIsNatureTankTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "hydross the unstable bot is nature tank") {} + bool IsActive() override; +}; + +class HydrossTheUnstableElementalsSpawnedTrigger : public Trigger +{ +public: + HydrossTheUnstableElementalsSpawnedTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "hydross the unstable elementals spawned") {} + bool IsActive() override; +}; + +class HydrossTheUnstableDangerFromWaterTombsTrigger : public Trigger +{ +public: + HydrossTheUnstableDangerFromWaterTombsTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "hydross the unstable danger from water tombs") {} + bool IsActive() override; +}; + +class HydrossTheUnstableTankNeedsAggroUponPhaseChangeTrigger : public Trigger +{ +public: + HydrossTheUnstableTankNeedsAggroUponPhaseChangeTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "hydross the unstable tank needs aggro upon phase change") {} + bool IsActive() override; +}; + +class HydrossTheUnstableAggroResetsUponPhaseChangeTrigger : public Trigger +{ +public: + HydrossTheUnstableAggroResetsUponPhaseChangeTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "hydross the unstable aggro resets upon phase change") {} + bool IsActive() override; +}; + +class HydrossTheUnstableNeedToManageTimersTrigger : public Trigger +{ +public: + HydrossTheUnstableNeedToManageTimersTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "hydross the unstable need to manage timers") {} + bool IsActive() override; +}; + +// The Lurker Below + +class TheLurkerBelowSpoutIsActiveTrigger : public Trigger +{ +public: + TheLurkerBelowSpoutIsActiveTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "the lurker below spout is active") {} + bool IsActive() override; +}; + +class TheLurkerBelowBossIsActiveForMainTankTrigger : public Trigger +{ +public: + TheLurkerBelowBossIsActiveForMainTankTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "the lurker below boss is active for main tank") {} + bool IsActive() override; +}; + +class TheLurkerBelowBossCastsGeyserTrigger : public Trigger +{ +public: + TheLurkerBelowBossCastsGeyserTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "the lurker below boss casts geyser") {} + bool IsActive() override; +}; + +class TheLurkerBelowBossIsSubmergedTrigger : public Trigger +{ +public: + TheLurkerBelowBossIsSubmergedTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "the lurker below boss is submerged") {} + bool IsActive() override; +}; + +class TheLurkerBelowNeedToPrepareTimerForSpoutTrigger : public Trigger +{ +public: + TheLurkerBelowNeedToPrepareTimerForSpoutTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "the lurker below need to prepare timer for spout") {} + bool IsActive() override; +}; + +// Leotheras the Blind + +class LeotherasTheBlindBossIsInactiveTrigger : public Trigger +{ +public: + LeotherasTheBlindBossIsInactiveTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind boss is inactive") {} + bool IsActive() override; +}; + +class LeotherasTheBlindOnlyWarlockShouldTankDemonFormTrigger : public Trigger +{ +public: + LeotherasTheBlindOnlyWarlockShouldTankDemonFormTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind only warlock should tank demon form") {} + bool IsActive() override; +}; + +class LeotherasTheBlindBossTransformedIntoDemonFormTrigger : public Trigger +{ +public: + LeotherasTheBlindBossTransformedIntoDemonFormTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind boss transformed into demon form") {} + bool IsActive() override; +}; + +class LeotherasTheBlindBossEngagedByRangedTrigger : public Trigger +{ +public: + LeotherasTheBlindBossEngagedByRangedTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind boss engaged by ranged") {} + bool IsActive() override; +}; + +class LeotherasTheBlindBossChannelingWhirlwindTrigger : public Trigger +{ +public: + LeotherasTheBlindBossChannelingWhirlwindTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind boss channeling whirlwind") {} + bool IsActive() override; +}; + +class LeotherasTheBlindBotHasTooManyChaosBlastStacksTrigger : public Trigger +{ +public: + LeotherasTheBlindBotHasTooManyChaosBlastStacksTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind bot has too many chaos blast stacks") {} + bool IsActive() override; +}; + +class LeotherasTheBlindInnerDemonHasAwakenedTrigger : public Trigger +{ +public: + LeotherasTheBlindInnerDemonHasAwakenedTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind inner demon has awakened") {} + bool IsActive() override; +}; + +class LeotherasTheBlindEnteredFinalPhaseTrigger : public Trigger +{ +public: + LeotherasTheBlindEnteredFinalPhaseTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind entered final phase") {} + bool IsActive() override; +}; + +class LeotherasTheBlindDemonFormTankNeedsAggro : public Trigger +{ +public: + LeotherasTheBlindDemonFormTankNeedsAggro( + PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind demon form tank needs aggro") {} + bool IsActive() override; +}; + +class LeotherasTheBlindBossWipesAggroUponPhaseChangeTrigger : public Trigger +{ +public: + LeotherasTheBlindBossWipesAggroUponPhaseChangeTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind boss wipes aggro upon phase change") {} + bool IsActive() override; +}; + +// Fathom-Lord Karathress + +class FathomLordKarathressBossEngagedByMainTankTrigger : public Trigger +{ +public: + FathomLordKarathressBossEngagedByMainTankTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "fathom-lord karathress boss engaged by main tank") {} + bool IsActive() override; +}; + +class FathomLordKarathressCaribdisEngagedByFirstAssistTankTrigger : public Trigger +{ +public: + FathomLordKarathressCaribdisEngagedByFirstAssistTankTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "fathom-lord karathress caribdis engaged by first assist tank") {} + bool IsActive() override; +}; + +class FathomLordKarathressSharkkisEngagedBySecondAssistTankTrigger : public Trigger +{ +public: + FathomLordKarathressSharkkisEngagedBySecondAssistTankTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "fathom-lord karathress sharkkis engaged by second assist tank") {} + bool IsActive() override; +}; + +class FathomLordKarathressTidalvessEngagedByThirdAssistTankTrigger : public Trigger +{ +public: + FathomLordKarathressTidalvessEngagedByThirdAssistTankTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "fathom-lord karathress tidalvess engaged by third assist tank") {} + bool IsActive() override; +}; + +class FathomLordKarathressCaribdisTankNeedsDedicatedHealerTrigger : public Trigger +{ +public: + FathomLordKarathressCaribdisTankNeedsDedicatedHealerTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "fathom-lord karathress caribdis tank needs dedicated healer") {} + bool IsActive() override; +}; + +class FathomLordKarathressPullingBossesTrigger : public Trigger +{ +public: + FathomLordKarathressPullingBossesTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "fathom-lord karathress pulling bosses") {} + bool IsActive() override; +}; + +class FathomLordKarathressDeterminingKillOrderTrigger : public Trigger +{ +public: + FathomLordKarathressDeterminingKillOrderTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "fathom-lord karathress determining kill order") {} + bool IsActive() override; +}; + +class FathomLordKarathressTanksNeedToEstablishAggroTrigger : public Trigger +{ +public: + FathomLordKarathressTanksNeedToEstablishAggroTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "fathom-lord karathress tanks need to establish aggro") {} + bool IsActive() override; +}; + +// Morogrim Tidewalker + +class MorogrimTidewalkerPullingBossTrigger : public Trigger +{ +public: + MorogrimTidewalkerPullingBossTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "morogrim tidewalker pulling boss") {} + bool IsActive() override; +}; + +class MorogrimTidewalkerBossEngagedByMainTankTrigger : public Trigger +{ +public: + MorogrimTidewalkerBossEngagedByMainTankTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "morogrim tidewalker boss engaged by main tank") {} + bool IsActive() override; +}; + +class MorogrimTidewalkerWaterGlobulesAreIncomingTrigger : public Trigger +{ +public: + MorogrimTidewalkerWaterGlobulesAreIncomingTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "morogrim tidewalker water globules are incoming") {} + bool IsActive() override; +}; + +// Lady Vashj + +class LadyVashjBossEngagedByMainTankTrigger : public Trigger +{ +public: + LadyVashjBossEngagedByMainTankTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "lady vashj boss engaged by main tank") {} + bool IsActive() override; +}; + +class LadyVashjBossEngagedByRangedInPhase1Trigger : public Trigger +{ +public: + LadyVashjBossEngagedByRangedInPhase1Trigger( + PlayerbotAI* botAI) : Trigger(botAI, "lady vashj boss engaged by ranged in phase 1") {} + bool IsActive() override; +}; + +class LadyVashjCastsShockBlastOnHighestAggroTrigger : public Trigger +{ +public: + LadyVashjCastsShockBlastOnHighestAggroTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "lady vashj casts shock blast on highest aggro") {} + bool IsActive() override; +}; + +class LadyVashjBotHasStaticChargeTrigger : public Trigger +{ +public: + LadyVashjBotHasStaticChargeTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "lady vashj bot has static charge") {} + bool IsActive() override; +}; + +class LadyVashjPullingBossInPhase1AndPhase3Trigger : public Trigger +{ +public: + LadyVashjPullingBossInPhase1AndPhase3Trigger( + PlayerbotAI* botAI) : Trigger(botAI, "lady vashj pulling boss in phase 1 and phase 3") {} + bool IsActive() override; +}; + +class LadyVashjAddsSpawnInPhase2AndPhase3Trigger : public Trigger +{ +public: + LadyVashjAddsSpawnInPhase2AndPhase3Trigger( + PlayerbotAI* botAI) : Trigger(botAI, "lady vashj adds spawn in phase 2 and phase 3") {} + bool IsActive() override; +}; + +class LadyVashjCoilfangStriderIsApproachingTrigger : public Trigger +{ +public: + LadyVashjCoilfangStriderIsApproachingTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "lady vashj coilfang strider is approaching") {} + bool IsActive() override; +}; + +class LadyVashjTaintedElementalCheatTrigger : public Trigger +{ +public: + LadyVashjTaintedElementalCheatTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "lady vashj tainted elemental cheat") {} + bool IsActive() override; +}; + +class LadyVashjTaintedCoreWasLootedTrigger : public Trigger +{ +public: + LadyVashjTaintedCoreWasLootedTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "lady vashj tainted core was looted") {} + bool IsActive() override; +}; + +class LadyVashjTaintedCoreIsUnusableTrigger : public Trigger +{ +public: + LadyVashjTaintedCoreIsUnusableTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "lady vashj tainted core is unusable") {} + bool IsActive() override; +}; + +class LadyVashjNeedToResetCorePassingTrackersTrigger : public Trigger +{ +public: + LadyVashjNeedToResetCorePassingTrackersTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "lady vashj need to reset core passing trackers") {} + bool IsActive() override; +}; + +class LadyVashjToxicSporebatsAreSpewingPoisonCloudsTrigger : public Trigger +{ +public: + LadyVashjToxicSporebatsAreSpewingPoisonCloudsTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "lady vashj toxic sporebats are spewing poison clouds") {} + bool IsActive() override; +}; + +class LadyVashjBotIsEntangledInToxicSporesOrStaticChargeTrigger : public Trigger +{ +public: + LadyVashjBotIsEntangledInToxicSporesOrStaticChargeTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "lady vashj bot is entangled in toxic spores or static charge") {} + bool IsActive() override; +}; + +#endif diff --git a/src/Ai/Raid/SerpentshrineCavern/Util/RaidSSCHelpers.cpp b/src/Ai/Raid/SerpentshrineCavern/Util/RaidSSCHelpers.cpp new file mode 100644 index 0000000000..7bda085be1 --- /dev/null +++ b/src/Ai/Raid/SerpentshrineCavern/Util/RaidSSCHelpers.cpp @@ -0,0 +1,583 @@ +#include "RaidSSCHelpers.h" +#include "AiFactory.h" +#include "Creature.h" +#include "ObjectAccessor.h" +#include "Playerbots.h" +#include "RaidBossHelpers.h" + +namespace SerpentShrineCavernHelpers +{ + // Hydross the Unstable + + const Position HYDROSS_FROST_TANK_POSITION = { -236.669f, -358.352f, -0.828f }; + const Position HYDROSS_NATURE_TANK_POSITION = { -225.471f, -327.790f, -3.682f }; + + std::unordered_map hydrossFrostDpsWaitTimer; + std::unordered_map hydrossNatureDpsWaitTimer; + std::unordered_map hydrossChangeToFrostPhaseTimer; + std::unordered_map hydrossChangeToNaturePhaseTimer; + + bool HasMarkOfHydrossAt100Percent(Player* bot) + { + return bot->HasAura(SPELL_MARK_OF_HYDROSS_100) || + bot->HasAura(SPELL_MARK_OF_HYDROSS_250) || + bot->HasAura(SPELL_MARK_OF_HYDROSS_500); + } + + bool HasNoMarkOfHydross(Player* bot) + { + return !bot->HasAura(SPELL_MARK_OF_HYDROSS_10) && + !bot->HasAura(SPELL_MARK_OF_HYDROSS_25) && + !bot->HasAura(SPELL_MARK_OF_HYDROSS_50) && + !bot->HasAura(SPELL_MARK_OF_HYDROSS_100) && + !bot->HasAura(SPELL_MARK_OF_HYDROSS_250) && + !bot->HasAura(SPELL_MARK_OF_HYDROSS_500); + } + + bool HasMarkOfCorruptionAt100Percent(Player* bot) + { + return bot->HasAura(SPELL_MARK_OF_CORRUPTION_100) || + bot->HasAura(SPELL_MARK_OF_CORRUPTION_250) || + bot->HasAura(SPELL_MARK_OF_CORRUPTION_500); + } + + bool HasNoMarkOfCorruption(Player* bot) + { + return !bot->HasAura(SPELL_MARK_OF_CORRUPTION_10) && + !bot->HasAura(SPELL_MARK_OF_CORRUPTION_25) && + !bot->HasAura(SPELL_MARK_OF_CORRUPTION_50) && + !bot->HasAura(SPELL_MARK_OF_CORRUPTION_100) && + !bot->HasAura(SPELL_MARK_OF_CORRUPTION_250) && + !bot->HasAura(SPELL_MARK_OF_CORRUPTION_500); + } + + // The Lurker Below + + const Position LURKER_MAIN_TANK_POSITION = { 23.706f, -406.038f, -19.686f }; + + std::unordered_map lurkerSpoutTimer; + std::unordered_map lurkerRangedPositions; + + bool IsLurkerCastingSpout(Unit* lurker) + { + if (!lurker || !lurker->HasUnitState(UNIT_STATE_CASTING)) + return false; + + Spell* currentSpell = lurker->GetCurrentSpell(CURRENT_GENERIC_SPELL); + if (!currentSpell) + return false; + + uint32 spellId = currentSpell->m_spellInfo->Id; + bool isSpout = spellId == SPELL_SPOUT_VISUAL; + + return isSpout; + } + + // Leotheras the Blind + + std::unordered_map leotherasHumanFormDpsWaitTimer; + std::unordered_map leotherasDemonFormDpsWaitTimer; + std::unordered_map leotherasFinalPhaseDpsWaitTimer; + + Unit* GetLeotherasHuman(PlayerbotAI* botAI) + { + auto const& npcs = + botAI->GetAiObjectContext()->GetValue("nearest hostile npcs")->Get(); + for (auto const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (unit && unit->GetEntry() == NPC_LEOTHERAS_THE_BLIND && + unit->IsInCombat() && !unit->HasAura(SPELL_METAMORPHOSIS)) + return unit; + } + return nullptr; + } + + Unit* GetPhase2LeotherasDemon(PlayerbotAI* botAI) + { + auto const& npcs = + botAI->GetAiObjectContext()->GetValue("nearest hostile npcs")->Get(); + for (auto const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (unit && unit->GetEntry() == NPC_LEOTHERAS_THE_BLIND && + unit->HasAura(SPELL_METAMORPHOSIS)) + return unit; + } + return nullptr; + } + + Unit* GetPhase3LeotherasDemon(PlayerbotAI* botAI) + { + auto const& npcs = + botAI->GetAiObjectContext()->GetValue("nearest hostile npcs")->Get(); + for (auto const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (unit && unit->GetEntry() == NPC_SHADOW_OF_LEOTHERAS) + return unit; + } + return nullptr; + } + + Unit* GetActiveLeotherasDemon(PlayerbotAI* botAI) + { + Unit* phase2 = GetPhase2LeotherasDemon(botAI); + Unit* phase3 = GetPhase3LeotherasDemon(botAI); + return phase2 ? phase2 : phase3; + } + + Player* GetLeotherasDemonFormTank(Player* bot) + { + Group* group = bot->GetGroup(); + if (!group) + return nullptr; + + // (1) First loop: Return the first assistant Warlock (real player or bot) + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive() || member->getClass() != CLASS_WARLOCK) + continue; + + if (group->IsAssistant(member->GetGUID())) + return member; + } + + // (2) Fall back to first found bot Warlock + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) || + member->getClass() != CLASS_WARLOCK) + continue; + + return member; + } + + // (3) Return nullptr if none found + return nullptr; + } + + // Fathom-Lord Karathress + + const Position KARATHRESS_TANK_POSITION = { 474.403f, -531.118f, -7.548f }; + const Position TIDALVESS_TANK_POSITION = { 511.282f, -501.162f, -13.158f }; + const Position SHARKKIS_TANK_POSITION = { 508.057f, -541.109f, -10.133f }; + const Position CARIBDIS_TANK_POSITION = { 464.462f, -475.820f, -13.158f }; + const Position CARIBDIS_HEALER_POSITION = { 466.203f, -503.201f, -13.158f }; + const Position CARIBDIS_RANGED_DPS_POSITION = { 463.197f, -501.190f, -13.158f }; + + std::unordered_map karathressDpsWaitTimer; + + // Morogrim Tidewalker + + const Position TIDEWALKER_PHASE_1_TANK_POSITION = { 410.925f, -741.916f, -7.146f }; + const Position TIDEWALKER_PHASE_TRANSITION_WAYPOINT = { 407.035f, -759.479f, -7.168f }; + const Position TIDEWALKER_PHASE_2_TANK_POSITION = { 446.571f, -767.155f, -7.144f }; + const Position TIDEWALKER_PHASE_2_RANGED_POSITION = { 432.595f, -766.288f, -7.145f }; + + std::unordered_map tidewalkerTankStep; + std::unordered_map tidewalkerRangedStep; + + // Lady Vashj + + const Position VASHJ_PLATFORM_CENTER_POSITION = { 29.634f, -923.541f, 42.985f }; + + std::unordered_map vashjRangedPositions; + std::unordered_map hasReachedVashjRangedPosition; + std::unordered_map nearestTriggerGuid; + std::unordered_map intendedLineup; + std::unordered_map lastImbueAttempt; + std::unordered_map lastCoreInInventoryTime; + + bool IsMainTankInSameSubgroup(Player* bot) + { + Group* group = bot->GetGroup(); + if (!group || !group->isRaidGroup()) + return false; + + uint8 botSubGroup = group->GetMemberGroup(bot->GetGUID()); + if (botSubGroup >= MAX_RAID_SUBGROUPS) + return false; + + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || member == bot || !member->IsAlive()) + continue; + + if (group->GetMemberGroup(member->GetGUID()) != botSubGroup) + continue; + + if (PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member)) + { + if (memberAI->IsMainTank(member)) + return true; + } + } + + return false; + } + + bool IsLadyVashjInPhase1(PlayerbotAI* botAI) + { + Unit* vashj = + botAI->GetAiObjectContext()->GetValue("find target", "lady vashj")->Get(); + if (!vashj) + return false; + + Creature* vashjCreature = vashj->ToCreature(); + return vashjCreature && vashjCreature->GetHealthPct() > 70.0f && + vashjCreature->GetReactState() != REACT_PASSIVE; + } + + bool IsLadyVashjInPhase2(PlayerbotAI* botAI) + { + Unit* vashj = + botAI->GetAiObjectContext()->GetValue("find target", "lady vashj")->Get(); + if (!vashj) + return false; + + Creature* vashjCreature = vashj->ToCreature(); + return vashjCreature && vashjCreature->GetReactState() == REACT_PASSIVE; + } + + bool IsLadyVashjInPhase3(PlayerbotAI* botAI) + { + Unit* vashj = + botAI->GetAiObjectContext()->GetValue("find target", "lady vashj")->Get(); + if (!vashj) + return false; + + Creature* vashjCreature = vashj->ToCreature(); + return vashjCreature && vashjCreature->GetHealthPct() <= 50.0f && + vashjCreature->GetReactState() != REACT_PASSIVE; + } + + bool IsValidLadyVashjCombatNpc(Unit* unit, PlayerbotAI* botAI) + { + if (!unit || !unit->IsAlive()) + return false; + + uint32 entry = unit->GetEntry(); + + if (IsLadyVashjInPhase2(botAI)) + { + return entry == NPC_TAINTED_ELEMENTAL || entry == NPC_ENCHANTED_ELEMENTAL || + entry == NPC_COILFANG_ELITE || entry == NPC_COILFANG_STRIDER; + } + else if (IsLadyVashjInPhase3(botAI)) + { + return entry == NPC_TAINTED_ELEMENTAL || entry == NPC_ENCHANTED_ELEMENTAL || + entry == NPC_COILFANG_ELITE || entry == NPC_COILFANG_STRIDER || + entry == NPC_TOXIC_SPOREBAT || entry == NPC_LADY_VASHJ; + } + + return false; + } + + bool AnyRecentCoreInInventory(Group* group, PlayerbotAI* botAI, uint32 graceSeconds) + { + Unit* vashj = + botAI->GetAiObjectContext()->GetValue("find target", "lady vashj")->Get(); + if (!vashj) + return false; + + if (group) + { + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (member && member->HasItemCount(ITEM_TAINTED_CORE, 1, false)) + return true; + } + } + + const uint32 instanceId = vashj->GetMap()->GetInstanceId(); + const time_t now = std::time(nullptr); + + auto it = lastCoreInInventoryTime.find(instanceId); + if (it != lastCoreInInventoryTime.end()) + { + if ((now - it->second) <= static_cast(graceSeconds)) + return true; + } + + return false; + } + + Player* GetDesignatedCoreLooter(Group* group, PlayerbotAI* botAI) + { + if (!group) + return nullptr; + + Player* leader = nullptr; + ObjectGuid leaderGuid = group->GetLeaderGUID(); + if (!leaderGuid.IsEmpty()) + leader = ObjectAccessor::FindPlayer(leaderGuid); + + if (!botAI->HasCheat(BotCheatMask::raid)) + return leader; + + Player* fallback = leader; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive() || member == leader) + continue; + + PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member); + if (!memberAI) + continue; + + if (memberAI->IsMelee(member) && memberAI->IsDps(member)) + return member; + + if (!fallback && memberAI->IsRangedDps(member)) + fallback = member; + } + + return fallback ? fallback : leader; + } + + Player* GetFirstTaintedCorePasser(Group* group, PlayerbotAI* botAI) + { + if (!group) + return nullptr; + + Player* designatedLooter = GetDesignatedCoreLooter(group, botAI); + + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive() || member == designatedLooter) + continue; + + PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member); + if (!memberAI) + continue; + + if (memberAI->IsAssistHealOfIndex(member, 0, true)) + return member; + } + + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) || + botAI->IsTank(member) || member == designatedLooter) + continue; + return member; + } + + return nullptr; + } + + Player* GetSecondTaintedCorePasser(Group* group, PlayerbotAI* botAI) + { + if (!group) + return nullptr; + + Player* designatedLooter = GetDesignatedCoreLooter(group, botAI); + Player* firstCorePasser = GetFirstTaintedCorePasser(group, botAI); + + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive() || member == designatedLooter || + member == firstCorePasser) + continue; + + PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member); + if (!memberAI) + continue; + + if (memberAI->IsAssistHealOfIndex(member, 1, true)) + return member; + } + + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) || + botAI->IsTank(member) || member == designatedLooter || + member == firstCorePasser) + continue; + return member; + } + + return nullptr; + } + + Player* GetThirdTaintedCorePasser(Group* group, PlayerbotAI* botAI) + { + if (!group) + return nullptr; + + Player* designatedLooter = GetDesignatedCoreLooter(group, botAI); + Player* firstCorePasser = GetFirstTaintedCorePasser(group, botAI); + Player* secondCorePasser = GetSecondTaintedCorePasser(group, botAI); + + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive() || member == designatedLooter || + member == firstCorePasser || member == secondCorePasser) + continue; + + PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member); + if (!memberAI) + continue; + + if (memberAI->IsAssistHealOfIndex(member, 2, true)) + return member; + } + + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) || + botAI->IsTank(member) || member == designatedLooter || + member == firstCorePasser || member == secondCorePasser) + continue; + return member; + } + + return nullptr; + } + + Player* GetFourthTaintedCorePasser(Group* group, PlayerbotAI* botAI) + { + if (!group) + return nullptr; + + Player* designatedLooter = GetDesignatedCoreLooter(group, botAI); + Player* firstCorePasser = GetFirstTaintedCorePasser(group, botAI); + Player* secondCorePasser = GetSecondTaintedCorePasser(group, botAI); + Player* thirdCorePasser = GetThirdTaintedCorePasser(group, botAI); + + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive() || member == designatedLooter || + member == firstCorePasser || member == secondCorePasser || + member == thirdCorePasser) + continue; + + PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member); + if (!memberAI) + continue; + + if (memberAI->IsAssistRangedDpsOfIndex(member, 0, true)) + return member; + } + + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) || + botAI->IsTank(member) || member == designatedLooter || + member == firstCorePasser || member == secondCorePasser || + member == thirdCorePasser) + continue; + return member; + } + + return nullptr; + } + + const std::vector SHIELD_GENERATOR_DB_GUIDS = + { + 47482, // NW + 47483, // NE + 47484, // SE + 47485 // SW + }; + + // Get the positions of all active Shield Generators by their database GUIDs + std::vector GetAllGeneratorInfosByDbGuids( + Map* map, const std::vector& generatorDbGuids) + { + std::vector generators; + if (!map) + return generators; + + for (uint32 dbGuid : generatorDbGuids) + { + auto bounds = map->GetGameObjectBySpawnIdStore().equal_range(dbGuid); + if (bounds.first == bounds.second) + continue; + + GameObject* go = bounds.first->second; + if (!go) + continue; + + if (go->GetGoState() != GO_STATE_READY) + continue; + + GeneratorInfo info; + info.guid = go->GetGUID(); + info.x = go->GetPositionX(); + info.y = go->GetPositionY(); + info.z = go->GetPositionZ(); + generators.push_back(info); + } + + return generators; + } + + // Returns the nearest active Shield Generator to the bot + // Active generators are powered by NPC_WORLD_INVISIBLE_TRIGGER creatures, + // which depawn after use + Unit* GetNearestActiveShieldGeneratorTriggerByEntry(Unit* reference) + { + if (!reference) + return nullptr; + + std::list triggers; + constexpr float searchRange = 150.0f; + reference->GetCreatureListWithEntryInGrid( + triggers, NPC_WORLD_INVISIBLE_TRIGGER, searchRange); + + Creature* nearest = nullptr; + float minDist = std::numeric_limits::max(); + + for (Creature* creature : triggers) + { + if (!creature->IsAlive()) + continue; + + float dist = reference->GetDistance(creature); + if (dist < minDist) + { + minDist = dist; + nearest = creature; + } + } + + return nearest; + } + + const GeneratorInfo* GetNearestGeneratorToBot( + Player* bot, const std::vector& generators) + { + if (!bot || generators.empty()) + return nullptr; + + const GeneratorInfo* nearest = nullptr; + float minDist = std::numeric_limits::max(); + + for (auto const& gen : generators) + { + float dist = bot->GetExactDist(gen.x, gen.y, gen.z); + if (dist < minDist) + { + minDist = dist; + nearest = &gen; + } + } + + return nearest; + } +} diff --git a/src/Ai/Raid/SerpentshrineCavern/Util/RaidSSCHelpers.h b/src/Ai/Raid/SerpentshrineCavern/Util/RaidSSCHelpers.h new file mode 100644 index 0000000000..a725e28fcd --- /dev/null +++ b/src/Ai/Raid/SerpentshrineCavern/Util/RaidSSCHelpers.h @@ -0,0 +1,189 @@ +#ifndef _PLAYERBOT_RAIDSSCHELPERS_H_ +#define _PLAYERBOT_RAIDSSCHELPERS_H_ + +#include +#include + +#include "AiObject.h" +#include "Position.h" +#include "Unit.h" + +namespace SerpentShrineCavernHelpers +{ + enum SerpentShrineCavernSpells + { + // Trash Mobs + SPELL_TOXIC_POOL = 38718, + + // Hydross the Unstable + SPELL_MARK_OF_HYDROSS_10 = 38215, + SPELL_MARK_OF_HYDROSS_25 = 38216, + SPELL_MARK_OF_HYDROSS_50 = 38217, + SPELL_MARK_OF_HYDROSS_100 = 38218, + SPELL_MARK_OF_HYDROSS_250 = 38231, + SPELL_MARK_OF_HYDROSS_500 = 40584, + SPELL_MARK_OF_CORRUPTION_10 = 38219, + SPELL_MARK_OF_CORRUPTION_25 = 38220, + SPELL_MARK_OF_CORRUPTION_50 = 38221, + SPELL_MARK_OF_CORRUPTION_100 = 38222, + SPELL_MARK_OF_CORRUPTION_250 = 38230, + SPELL_MARK_OF_CORRUPTION_500 = 40583, + SPELL_CORRUPTION = 37961, + + // The Lurker Below + SPELL_SPOUT_VISUAL = 37431, + + // Leotheras the Blind + SPELL_LEOTHERAS_BANISHED = 37546, + SPELL_WHIRLWIND = 37640, + SPELL_WHIRLWIND_CHANNEL = 37641, + SPELL_METAMORPHOSIS = 37673, + SPELL_CHAOS_BLAST = 37675, + SPELL_INSIDIOUS_WHISPER = 37676, + + // Lady Vashj + SPELL_FEAR_WARD = 6346, + SPELL_POISON_BOLT = 38253, + SPELL_STATIC_CHARGE = 38280, + SPELL_ENTANGLE = 38316, + + // Druid + SPELL_CAT_FORM = 768, + SPELL_BEAR_FORM = 5487, + SPELL_DIRE_BEAR_FORM = 9634, + SPELL_TREE_OF_LIFE = 33891, + + // Hunter + SPELL_MISDIRECTION = 35079, + + // Mage + SPELL_SLOW = 31589, + + // Shaman + SPELL_GROUNDING_TOTEM_EFFECT = 8178, + + // Warlock + SPELL_CURSE_OF_EXHAUSTION = 18223, + + // Item + SPELL_HEAVY_NETHERWEAVE_NET = 31368, + }; + + enum SerpentShrineCavernNPCs + { + // Trash Mobs + NPC_WATER_ELEMENTAL_TOTEM = 22236, + + // Hydross the Unstable + NPC_PURE_SPAWN_OF_HYDROSS = 22035, + NPC_TAINTED_SPAWN_OF_HYDROSS = 22036, + + // The Lurker Below + NPC_COILFANG_GUARDIAN = 21873, + + // Leotheras the Blind + NPC_LEOTHERAS_THE_BLIND = 21215, + NPC_GREYHEART_SPELLBINDER = 21806, + NPC_INNER_DEMON = 21857, + NPC_SHADOW_OF_LEOTHERAS = 21875, + + // Fathom-Lord Karathress + NPC_SPITFIRE_TOTEM = 22091, + + // Lady Vashj + NPC_WORLD_INVISIBLE_TRIGGER = 12999, + NPC_LADY_VASHJ = 21212, + NPC_ENCHANTED_ELEMENTAL = 21958, + NPC_TAINTED_ELEMENTAL = 22009, + NPC_COILFANG_ELITE = 22055, + NPC_COILFANG_STRIDER = 22056, + NPC_TOXIC_SPOREBAT = 22140, + NPC_SPORE_DROP_TRIGGER = 22207, + }; + + enum SerpentShrineCavernItems + { + // Lady Vashj + ITEM_TAINTED_CORE = 31088, + + // Tailoring + ITEM_HEAVY_NETHERWEAVE_NET = 24269, + }; + + constexpr uint32 SSC_MAP_ID = 548; + + // Hydross the Unstable + extern const Position HYDROSS_FROST_TANK_POSITION; + extern const Position HYDROSS_NATURE_TANK_POSITION; + extern std::unordered_map hydrossFrostDpsWaitTimer; + extern std::unordered_map hydrossNatureDpsWaitTimer; + extern std::unordered_map hydrossChangeToFrostPhaseTimer; + extern std::unordered_map hydrossChangeToNaturePhaseTimer; + bool HasMarkOfHydrossAt100Percent(Player* bot); + bool HasNoMarkOfHydross(Player* bot); + bool HasMarkOfCorruptionAt100Percent(Player* bot); + bool HasNoMarkOfCorruption(Player* bot); + + // The Lurker Below + extern const Position LURKER_MAIN_TANK_POSITION; + extern std::unordered_map lurkerSpoutTimer; + extern std::unordered_map lurkerRangedPositions; + bool IsLurkerCastingSpout(Unit* lurker); + + // Leotheras the Blind + extern std::unordered_map leotherasHumanFormDpsWaitTimer; + extern std::unordered_map leotherasDemonFormDpsWaitTimer; + extern std::unordered_map leotherasFinalPhaseDpsWaitTimer; + Unit* GetLeotherasHuman(PlayerbotAI* botAI); + Unit* GetPhase2LeotherasDemon(PlayerbotAI* botAI); + Unit* GetPhase3LeotherasDemon(PlayerbotAI* botAI); + Unit* GetActiveLeotherasDemon(PlayerbotAI* botAI); + Player* GetLeotherasDemonFormTank(Player* bot); + + // Fathom-Lord Karathress + extern const Position KARATHRESS_TANK_POSITION; + extern const Position TIDALVESS_TANK_POSITION; + extern const Position SHARKKIS_TANK_POSITION; + extern const Position CARIBDIS_TANK_POSITION; + extern const Position CARIBDIS_HEALER_POSITION; + extern const Position CARIBDIS_RANGED_DPS_POSITION; + extern std::unordered_map karathressDpsWaitTimer; + + // Morogrim Tidewalker + extern const Position TIDEWALKER_PHASE_1_TANK_POSITION; + extern const Position TIDEWALKER_PHASE_TRANSITION_WAYPOINT; + extern const Position TIDEWALKER_PHASE_2_TANK_POSITION; + extern const Position TIDEWALKER_PHASE_2_RANGED_POSITION; + extern std::unordered_map tidewalkerTankStep; + extern std::unordered_map tidewalkerRangedStep; + + // Lady Vashj + constexpr float VASHJ_PLATFORM_Z = 42.985f; + extern const Position VASHJ_PLATFORM_CENTER_POSITION; + extern std::unordered_map vashjRangedPositions; + extern std::unordered_map hasReachedVashjRangedPosition; + extern std::unordered_map nearestTriggerGuid; + extern std::unordered_map intendedLineup; + extern std::unordered_map lastImbueAttempt; + extern std::unordered_map lastCoreInInventoryTime; + bool IsMainTankInSameSubgroup(Player* bot); + bool IsLadyVashjInPhase1(PlayerbotAI* botAI); + bool IsLadyVashjInPhase2(PlayerbotAI* botAI); + bool IsLadyVashjInPhase3(PlayerbotAI* botAI); + bool IsValidLadyVashjCombatNpc(Unit* unit, PlayerbotAI* botAI); + bool AnyRecentCoreInInventory(Group* group, PlayerbotAI* botAI, uint32 graceSeconds = 3); + Player* GetDesignatedCoreLooter(Group* group, PlayerbotAI* botAI); + Player* GetFirstTaintedCorePasser(Group* group, PlayerbotAI* botAI); + Player* GetSecondTaintedCorePasser(Group* group, PlayerbotAI* botAI); + Player* GetThirdTaintedCorePasser(Group* group, PlayerbotAI* botAI); + Player* GetFourthTaintedCorePasser(Group* group, PlayerbotAI* botAI); + struct GeneratorInfo { ObjectGuid guid; float x, y, z; }; + extern const std::vector SHIELD_GENERATOR_DB_GUIDS; + std::vector GetAllGeneratorInfosByDbGuids( + Map* map, const std::vector& generatorDbGuids); + Unit* GetNearestActiveShieldGeneratorTriggerByEntry(Unit* reference); + const GeneratorInfo* GetNearestGeneratorToBot( + Player* bot, const std::vector& generators); +} + +#endif diff --git a/src/Bot/Engine/AiObjectContext.cpp b/src/Bot/Engine/AiObjectContext.cpp index 250a392969..3a2037a082 100644 --- a/src/Bot/Engine/AiObjectContext.cpp +++ b/src/Bot/Engine/AiObjectContext.cpp @@ -41,6 +41,8 @@ #include "Ai/Raid/Magtheridon/RaidMagtheridonTriggerContext.h" #include "Ai/Raid/GruulsLair/RaidGruulsLairActionContext.h" #include "Ai/Raid/GruulsLair/RaidGruulsLairTriggerContext.h" +#include "Ai/Raid/SerpentshrineCavern/RaidSSCActionContext.h" +#include "Ai/Raid/SerpentshrineCavern/RaidSSCTriggerContext.h" #include "Ai/Raid/EyeOfEternity/RaidEoEActionContext.h" #include "Ai/Raid/EyeOfEternity/RaidEoETriggerContext.h" #include "Ai/Raid/VaultOfArchavon/RaidVoAActionContext.h" @@ -115,6 +117,7 @@ void AiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList Date: Sun, 8 Feb 2026 03:41:33 -0800 Subject: [PATCH 11/25] Refactor of EquipActions (#1994) #PR Description The root cause of issue #1987 was the AI Value item usage becoming a very expensive call when bots gained professions accidentally. My original approach was to eliminate it entirely, but after inputs and testing I decided to introduce a more focused Ai value "Item upgrade" that only checks equipment and ammo inheriting directly from item usage, so the logic is unified between them. Upgrades are now only assessed when receiving an item that can be equipped. Additionally, I noticed that winning loot rolls did not trigger the upgrade action, so I added a new package handler for that. Performance needs to be re-evaluated, but I expect a reduction in calls and in the cost of each call. I tested with bots and selfbot in deadmines and ahadowfang keep. --------- Co-authored-by: bashermens <31279994+hermensbas@users.noreply.github.com> --- conf/playerbots.conf.dist | 2 +- src/Ai/Base/Actions/EquipAction.cpp | 98 ++++----- src/Ai/Base/Actions/EquipAction.h | 8 +- src/Ai/Base/ChatActionContext.h | 3 +- .../Strategy/WorldPacketHandlerStrategy.cpp | 1 + src/Ai/Base/Value/ItemUsageValue.cpp | 186 ++++++++++-------- src/Ai/Base/Value/ItemUsageValue.h | 22 ++- src/Ai/Base/ValueContext.h | 2 + src/Ai/Base/WorldPacketTriggerContext.h | 2 + src/Bot/PlayerbotAI.cpp | 1 + 10 files changed, 176 insertions(+), 149 deletions(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 18a6addf6d..a2f06885c1 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -2182,4 +2182,4 @@ AiPlayerbot.SummonAtInnkeepersEnabled = 1 # 30% more damage, 40% damage reduction (tank bots), increased all resistances, reduced threat for non tank bots, increased threat for tank bots. # Buffs will be applied on PP, Sindragosa and Lich King -AiPlayerbot.EnableICCBuffs = 1 +AiPlayerbot.EnableICCBuffs = 1 \ No newline at end of file diff --git a/src/Ai/Base/Actions/EquipAction.cpp b/src/Ai/Base/Actions/EquipAction.cpp index 32508ef2e5..a8b262692b 100644 --- a/src/Ai/Base/Actions/EquipAction.cpp +++ b/src/Ai/Base/Actions/EquipAction.cpp @@ -328,7 +328,43 @@ void EquipAction::EquipItem(Item* item) botAI->TellMaster(out); } -bool EquipUpgradesAction::Execute(Event event) +ItemIds EquipAction::SelectInventoryItemsToEquip() +{ + CollectItemsVisitor visitor; + IterateItems(&visitor, ITERATE_ITEMS_IN_BAGS); + + ItemIds items; + for (auto i = visitor.items.begin(); i != visitor.items.end(); ++i) + { + Item* item = *i; + if (!item) + continue; + + ItemTemplate const* itemTemplate = item->GetTemplate(); + if (!itemTemplate) + continue; + + //TODO Expand to Glyphs and Gems, that can be placed in equipment + //Pre-filter non-equipable items + if (itemTemplate->InventoryType == INVTYPE_NON_EQUIP) + continue; + + int32 randomProperty = item->GetItemRandomPropertyId(); + uint32 itemId = item->GetTemplate()->ItemId; + std::string itemUsageParam; + if (randomProperty != 0) + itemUsageParam = std::to_string(itemId) + "," + std::to_string(randomProperty); + else + itemUsageParam = std::to_string(itemId); + + ItemUsage usage = AI_VALUE2(ItemUsage, "item upgrade", itemUsageParam); + if (usage == ITEM_USAGE_EQUIP || usage == ITEM_USAGE_REPLACE || usage == ITEM_USAGE_BAD_EQUIP) + items.insert(itemId); + } + return items; +} + +bool EquipUpgradesTriggeredAction::Execute(Event event) { if (!sPlayerbotAIConfig.autoEquipUpgradeLoot && !sRandomPlayerbotMgr.IsRandomBot(bot)) return false; @@ -361,72 +397,18 @@ bool EquipUpgradesAction::Execute(Event event) p >> itemId; ItemTemplate const* item = sObjectMgr->GetItemTemplate(itemId); - if (item->Class == ITEM_CLASS_TRADE_GOODS && item->SubClass == ITEM_SUBCLASS_MEAT) + if (item->InventoryType == INVTYPE_NON_EQUIP) return false; } - CollectItemsVisitor visitor; - IterateItems(&visitor, ITERATE_ITEMS_IN_BAGS); - - ItemIds items; - for (auto i = visitor.items.begin(); i != visitor.items.end(); ++i) - { - Item* item = *i; - if (!item) - break; - int32 randomProperty = item->GetItemRandomPropertyId(); - uint32 itemId = item->GetTemplate()->ItemId; - std::string itemUsageParam; - if (randomProperty != 0) - { - itemUsageParam = std::to_string(itemId) + "," + std::to_string(randomProperty); - } - else - { - itemUsageParam = std::to_string(itemId); - } - ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", itemUsageParam); - - if (usage == ITEM_USAGE_EQUIP || usage == ITEM_USAGE_REPLACE || usage == ITEM_USAGE_BAD_EQUIP) - { - items.insert(itemId); - } - } - + ItemIds items = SelectInventoryItemsToEquip(); EquipItems(items); return true; } bool EquipUpgradeAction::Execute(Event event) { - CollectItemsVisitor visitor; - IterateItems(&visitor, ITERATE_ITEMS_IN_BAGS); - - ItemIds items; - for (auto i = visitor.items.begin(); i != visitor.items.end(); ++i) - { - Item* item = *i; - if (!item) - break; - int32 randomProperty = item->GetItemRandomPropertyId(); - uint32 itemId = item->GetTemplate()->ItemId; - std::string itemUsageParam; - if (randomProperty != 0) - { - itemUsageParam = std::to_string(itemId) + "," + std::to_string(randomProperty); - } - else - { - itemUsageParam = std::to_string(itemId); - } - ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", itemUsageParam); - - if (usage == ITEM_USAGE_EQUIP || usage == ITEM_USAGE_REPLACE || usage == ITEM_USAGE_BAD_EQUIP) - { - items.insert(itemId); - } - } - + ItemIds items = SelectInventoryItemsToEquip(); EquipItems(items); return true; } diff --git a/src/Ai/Base/Actions/EquipAction.h b/src/Ai/Base/Actions/EquipAction.h index 518d39c8ee..4f84f942bd 100644 --- a/src/Ai/Base/Actions/EquipAction.h +++ b/src/Ai/Base/Actions/EquipAction.h @@ -8,6 +8,7 @@ #include "ChatHelper.h" #include "InventoryAction.h" +#include "Item.h" class FindItemVisitor; class Item; @@ -20,6 +21,7 @@ class EquipAction : public InventoryAction bool Execute(Event event) override; void EquipItems(ItemIds ids); + ItemIds SelectInventoryItemsToEquip(); private: void EquipItem(FindItemVisitor* visitor); @@ -27,10 +29,10 @@ class EquipAction : public InventoryAction void EquipItem(Item* item); }; -class EquipUpgradesAction : public EquipAction +class EquipUpgradesTriggeredAction : public EquipAction { public: - EquipUpgradesAction(PlayerbotAI* botAI, std::string const name = "equip upgrades") : EquipAction(botAI, name) {} + explicit EquipUpgradesTriggeredAction(PlayerbotAI* botAI, std::string const name = "equip upgrades") : EquipAction(botAI, name) {} bool Execute(Event event) override; }; @@ -38,7 +40,7 @@ class EquipUpgradesAction : public EquipAction class EquipUpgradeAction : public EquipAction { public: - EquipUpgradeAction(PlayerbotAI* botAI, std::string const name = "equip upgrade") : EquipAction(botAI, name) {} + explicit EquipUpgradeAction(PlayerbotAI* botAI, std::string const name = "equip upgrade") : EquipAction(botAI, name) {} bool Execute(Event event) override; }; diff --git a/src/Ai/Base/ChatActionContext.h b/src/Ai/Base/ChatActionContext.h index 5e215e1237..8e4ed9ccf8 100644 --- a/src/Ai/Base/ChatActionContext.h +++ b/src/Ai/Base/ChatActionContext.h @@ -120,7 +120,7 @@ class ChatActionContext : public NamedObjectContext creators["use"] = &ChatActionContext::use; creators["item count"] = &ChatActionContext::item_count; creators["equip"] = &ChatActionContext::equip; - creators["equip upgrades"] = &ChatActionContext::equip_upgrades; + creators["equip upgrades"] = &ChatActionContext::equip_upgrade; creators["unequip"] = &ChatActionContext::unequip; creators["sell"] = &ChatActionContext::sell; creators["buy"] = &ChatActionContext::buy; @@ -258,7 +258,6 @@ class ChatActionContext : public NamedObjectContext static Action* talents(PlayerbotAI* botAI) { return new ChangeTalentsAction(botAI); } static Action* equip(PlayerbotAI* botAI) { return new EquipAction(botAI); } - static Action* equip_upgrades(PlayerbotAI* botAI) { return new EquipUpgradesAction(botAI); } static Action* unequip(PlayerbotAI* botAI) { return new UnequipAction(botAI); } static Action* sell(PlayerbotAI* botAI) { return new SellAction(botAI); } static Action* buy(PlayerbotAI* botAI) { return new BuyAction(botAI); } diff --git a/src/Ai/Base/Strategy/WorldPacketHandlerStrategy.cpp b/src/Ai/Base/Strategy/WorldPacketHandlerStrategy.cpp index cb37d27179..2debc44cbd 100644 --- a/src/Ai/Base/Strategy/WorldPacketHandlerStrategy.cpp +++ b/src/Ai/Base/Strategy/WorldPacketHandlerStrategy.cpp @@ -42,6 +42,7 @@ void WorldPacketHandlerStrategy::InitTriggers(std::vector& trigger NextAction("query item usage", relevance), NextAction("equip upgrades", relevance) })); triggers.push_back(new TriggerNode("item push result", { NextAction("quest item push result", relevance) })); + triggers.push_back(new TriggerNode("loot roll won", { NextAction("equip upgrades", relevance) })); triggers.push_back(new TriggerNode("ready check finished", { NextAction("finish ready check", relevance) })); // triggers.push_back(new TriggerNode("often", { NextAction("security check", relevance), NextAction("check mail", relevance) })); triggers.push_back(new TriggerNode("guild invite", { NextAction("guild accept", relevance) })); diff --git a/src/Ai/Base/Value/ItemUsageValue.cpp b/src/Ai/Base/Value/ItemUsageValue.cpp index 6f48fa9739..c6183e30fd 100644 --- a/src/Ai/Base/Value/ItemUsageValue.cpp +++ b/src/Ai/Base/Value/ItemUsageValue.cpp @@ -19,19 +19,9 @@ ItemUsage ItemUsageValue::Calculate() { - uint32 itemId = 0; - uint32 randomPropertyId = 0; - size_t pos = qualifier.find(","); - if (pos != std::string::npos) - { - itemId = atoi(qualifier.substr(0, pos).c_str()); - randomPropertyId = atoi(qualifier.substr(pos + 1).c_str()); - } - else - { - itemId = atoi(qualifier.c_str()); - } - + ParsedItemUsage const parsed = GetItemIdFromQualifier(); + uint32 itemId = parsed.itemId; + uint32 randomPropertyId = parsed.randomPropertyId; if (!itemId) return ITEM_USAGE_NONE; @@ -142,96 +132,30 @@ ItemUsage ItemUsageValue::Calculate() // If the loot is from an item in the bot’s bags, ignore syncQuestWithPlayer if (isLootFromItem && botNeedsItemForQuest) - { return ITEM_USAGE_QUEST; - } // If the bot is NOT acting alone and the master needs this quest item, defer to the master if (!isSelfBot && masterNeedsItemForQuest) - { return ITEM_USAGE_NONE; - } // If the bot itself needs the item for a quest, allow looting if (botNeedsItemForQuest) - { return ITEM_USAGE_QUEST; - } if (proto->Class == ITEM_CLASS_PROJECTILE && bot->CanUseItem(proto) == EQUIP_ERR_OK) { - if (bot->getClass() == CLASS_HUNTER || bot->getClass() == CLASS_ROGUE || bot->getClass() == CLASS_WARRIOR) - { - Item* rangedWeapon = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED); - uint32 requiredSubClass = 0; - - if (rangedWeapon) - { - switch (rangedWeapon->GetTemplate()->SubClass) - { - case ITEM_SUBCLASS_WEAPON_GUN: - requiredSubClass = ITEM_SUBCLASS_BULLET; - break; - case ITEM_SUBCLASS_WEAPON_BOW: - case ITEM_SUBCLASS_WEAPON_CROSSBOW: - requiredSubClass = ITEM_SUBCLASS_ARROW; - break; - } - } - - // Ensure the item is the correct ammo type for the equipped ranged weapon - if (proto->SubClass == requiredSubClass) - { - float ammoCount = BetterStacks(proto, "ammo"); - float requiredAmmo = (bot->getClass() == CLASS_HUNTER) ? 8 : 2; // Hunters get 8 stacks, others 2 - uint32 currentAmmoId = bot->GetUInt32Value(PLAYER_AMMO_ID); - - // Check if the bot has an ammo type assigned - if (currentAmmoId == 0) - { - return ITEM_USAGE_EQUIP; // Equip the ammo if no ammo - } - // Compare new ammo vs current equipped ammo - ItemTemplate const* currentAmmoProto = sObjectMgr->GetItemTemplate(currentAmmoId); - if (currentAmmoProto) - { - uint32 currentAmmoDPS = (currentAmmoProto->Damage[0].DamageMin + currentAmmoProto->Damage[0].DamageMax) * 1000 / 2; - uint32 newAmmoDPS = (proto->Damage[0].DamageMin + proto->Damage[0].DamageMax) * 1000 / 2; - - if (newAmmoDPS > currentAmmoDPS) // New ammo meets upgrade condition - { - return ITEM_USAGE_EQUIP; - } - if (newAmmoDPS < currentAmmoDPS) // New ammo is worse - { - return ITEM_USAGE_NONE; - } - } - // Ensure we have enough ammo in the inventory - if (ammoCount < requiredAmmo) - { - ammoCount += CurrentStacks(proto); - - if (ammoCount < requiredAmmo) // Buy ammo to reach the proper supply - return ITEM_USAGE_AMMO; - else if (ammoCount < requiredAmmo + 1) - return ITEM_USAGE_KEEP; // Keep the ammo if we don't have too much. - } - } - } + ItemUsage ammoUsage = QueryItemUsageForAmmo(proto); + if (ammoUsage != ITEM_USAGE_NONE) + return ammoUsage; } - // Need to add something like free bagspace or item value. if (proto->SellPrice > 0) { if (proto->Quality >= ITEM_QUALITY_NORMAL && !isSoulbound) - { return ITEM_USAGE_AH; - } + else - { return ITEM_USAGE_VENDOR; - } } return ITEM_USAGE_NONE; @@ -480,6 +404,80 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto, return ITEM_USAGE_NONE; } +ItemUsage ItemUsageValue::QueryItemUsageForAmmo(ItemTemplate const* proto) +{ + if (bot->getClass() != CLASS_HUNTER || bot->getClass() != CLASS_ROGUE || bot->getClass() != CLASS_WARRIOR) + return ITEM_USAGE_NONE; + + Item* rangedWeapon = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED); + uint32 requiredSubClass = 0; + + if (rangedWeapon) + { + switch (rangedWeapon->GetTemplate()->SubClass) + { + case ITEM_SUBCLASS_WEAPON_GUN: + requiredSubClass = ITEM_SUBCLASS_BULLET; + break; + case ITEM_SUBCLASS_WEAPON_BOW: + case ITEM_SUBCLASS_WEAPON_CROSSBOW: + requiredSubClass = ITEM_SUBCLASS_ARROW; + break; + } + } + + // Ensure the item is the correct ammo type for the equipped ranged weapon + if (proto->SubClass == requiredSubClass) + { + float ammoCount = BetterStacks(proto, "ammo"); + float requiredAmmo = (bot->getClass() == CLASS_HUNTER) ? 8 : 2; // Hunters get 8 stacks, others 2 + uint32 currentAmmoId = bot->GetUInt32Value(PLAYER_AMMO_ID); + + // Check if the bot has an ammo type assigned + if (currentAmmoId == 0) + return ITEM_USAGE_EQUIP; // Equip the ammo if no ammo + // Compare new ammo vs current equipped ammo + ItemTemplate const* currentAmmoProto = sObjectMgr->GetItemTemplate(currentAmmoId); + if (currentAmmoProto) + { + uint32 currentAmmoDPS = (currentAmmoProto->Damage[0].DamageMin + currentAmmoProto->Damage[0].DamageMax) * 1000 / 2; + uint32 newAmmoDPS = (proto->Damage[0].DamageMin + proto->Damage[0].DamageMax) * 1000 / 2; + + if (newAmmoDPS > currentAmmoDPS) // New ammo meets upgrade condition + return ITEM_USAGE_EQUIP; + + if (newAmmoDPS < currentAmmoDPS) // New ammo is worse + return ITEM_USAGE_NONE; + } + // Ensure we have enough ammo in the inventory + if (ammoCount < requiredAmmo) + { + ammoCount += CurrentStacks(proto); + + if (ammoCount < requiredAmmo) // Buy ammo to reach the proper supply + return ITEM_USAGE_AMMO; + else if (ammoCount < requiredAmmo + 1) + return ITEM_USAGE_KEEP; // Keep the ammo if we don't have too much. + } + } + return ITEM_USAGE_NONE; +} + +ParsedItemUsage ItemUsageValue::GetItemIdFromQualifier() +{ + ParsedItemUsage parsed; + + size_t const pos = qualifier.find(","); + if (pos != std::string::npos) + { + parsed.itemId = atoi(qualifier.substr(0, pos).c_str()); + parsed.randomPropertyId = atoi(qualifier.substr(pos + 1).c_str()); + return parsed; + } + else + parsed.itemId = atoi(qualifier.c_str()); + return parsed; +} // Return smaltest bag size equipped uint32 ItemUsageValue::GetSmallestBagSize() { @@ -913,3 +911,25 @@ std::string const ItemUsageValue::GetConsumableType(ItemTemplate const* proto, b return ""; } + +ItemUsage ItemUpgradeValue::Calculate() +{ + ParsedItemUsage parsed = GetItemIdFromQualifier(); + uint32 itemId = parsed.itemId; + uint32 randomPropertyId = parsed.randomPropertyId; + if (!itemId) + return ITEM_USAGE_NONE; + + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); + if (!proto) + return ITEM_USAGE_NONE; + + ItemUsage equip = QueryItemUsageForEquip(proto, randomPropertyId); + if (equip != ITEM_USAGE_NONE) + return equip; + + if (proto->Class == ITEM_CLASS_PROJECTILE && bot->CanUseItem(proto) == EQUIP_ERR_OK) + return QueryItemUsageForAmmo(proto); + + return ITEM_USAGE_NONE; +} diff --git a/src/Ai/Base/Value/ItemUsageValue.h b/src/Ai/Base/Value/ItemUsageValue.h index d50ea1b060..b759bab6af 100644 --- a/src/Ai/Base/Value/ItemUsageValue.h +++ b/src/Ai/Base/Value/ItemUsageValue.h @@ -14,7 +14,11 @@ class Player; class PlayerbotAI; struct ItemTemplate; - +struct ParsedItemUsage +{ + uint32 itemId = 0; + int32 randomPropertyId = 0; +}; enum ItemUsage : uint32 { ITEM_USAGE_NONE = 0, @@ -42,8 +46,12 @@ class ItemUsageValue : public CalculatedValue, public Qualified ItemUsage Calculate() override; -private: +protected: ItemUsage QueryItemUsageForEquip(ItemTemplate const* proto, int32 randomPropertyId = 0); + ItemUsage QueryItemUsageForAmmo(ItemTemplate const* proto); + ParsedItemUsage GetItemIdFromQualifier(); + +private: uint32 GetSmallestBagSize(); bool IsItemUsefulForQuest(Player* player, ItemTemplate const* proto); bool IsItemNeededForSkill(ItemTemplate const* proto); @@ -61,4 +69,14 @@ class ItemUsageValue : public CalculatedValue, public Qualified static std::string const GetConsumableType(ItemTemplate const* proto, bool hasMana); }; +class ItemUpgradeValue : public ItemUsageValue +{ +public: + ItemUpgradeValue(PlayerbotAI* botAI, std::string const name = "item upgrade") : ItemUsageValue(botAI, name) + { + } + + ItemUsage Calculate() override; +}; + #endif diff --git a/src/Ai/Base/ValueContext.h b/src/Ai/Base/ValueContext.h index ab93bcea47..b67aee9bb4 100644 --- a/src/Ai/Base/ValueContext.h +++ b/src/Ai/Base/ValueContext.h @@ -216,6 +216,7 @@ class ValueContext : public NamedObjectContext creators["formation"] = &ValueContext::formation; creators["stance"] = &ValueContext::stance; creators["item usage"] = &ValueContext::item_usage; + creators["item upgrade"] = &ValueContext::item_upgrade; creators["speed"] = &ValueContext::speed; creators["last said"] = &ValueContext::last_said; creators["last emote"] = &ValueContext::last_emote; @@ -341,6 +342,7 @@ class ValueContext : public NamedObjectContext static UntypedValue* already_seen_players(PlayerbotAI* botAI) { return new AlreadySeenPlayersValue(botAI); } static UntypedValue* new_player_nearby(PlayerbotAI* botAI) { return new NewPlayerNearbyValue(botAI); } static UntypedValue* item_usage(PlayerbotAI* botAI) { return new ItemUsageValue(botAI); } + static UntypedValue* item_upgrade(PlayerbotAI* botAI) { return new ItemUpgradeValue(botAI); } static UntypedValue* formation(PlayerbotAI* botAI) { return new FormationValue(botAI); } static UntypedValue* stance(PlayerbotAI* botAI) { return new StanceValue(botAI); } static UntypedValue* mana_save_level(PlayerbotAI* botAI) { return new ManaSaveLevelValue(botAI); } diff --git a/src/Ai/Base/WorldPacketTriggerContext.h b/src/Ai/Base/WorldPacketTriggerContext.h index 1e73095244..62afafd1a9 100644 --- a/src/Ai/Base/WorldPacketTriggerContext.h +++ b/src/Ai/Base/WorldPacketTriggerContext.h @@ -46,6 +46,7 @@ class WorldPacketTriggerContext : public NamedObjectContext creators["questgiver quest details"] = &WorldPacketTriggerContext::questgiver_quest_details; creators["item push result"] = &WorldPacketTriggerContext::item_push_result; + creators["loot roll won"] = &WorldPacketTriggerContext::loot_roll_won; creators["party command"] = &WorldPacketTriggerContext::party_command; creators["taxi done"] = &WorldPacketTriggerContext::taxi_done; creators["cast failed"] = &WorldPacketTriggerContext::cast_failed; @@ -92,6 +93,7 @@ class WorldPacketTriggerContext : public NamedObjectContext static Trigger* taxi_done(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "taxi done"); } static Trigger* party_command(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "party command"); } static Trigger* item_push_result(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "item push result"); } + static Trigger* loot_roll_won(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "loot roll won"); } // quest static Trigger* quest_update_add_kill(PlayerbotAI* ai) { return new WorldPacketTrigger(ai, "quest update add kill"); } diff --git a/src/Bot/PlayerbotAI.cpp b/src/Bot/PlayerbotAI.cpp index f4be1d43ee..cc91f3ce99 100644 --- a/src/Bot/PlayerbotAI.cpp +++ b/src/Bot/PlayerbotAI.cpp @@ -185,6 +185,7 @@ PlayerbotAI::PlayerbotAI(Player* bot) botOutgoingPacketHandlers.AddHandler(SMSG_TRADE_STATUS_EXTENDED, "trade status extended"); botOutgoingPacketHandlers.AddHandler(SMSG_LOOT_RESPONSE, "loot response"); botOutgoingPacketHandlers.AddHandler(SMSG_ITEM_PUSH_RESULT, "item push result"); + botOutgoingPacketHandlers.AddHandler(SMSG_LOOT_ROLL_WON, "loot roll won"); botOutgoingPacketHandlers.AddHandler(SMSG_PARTY_COMMAND_RESULT, "party command"); botOutgoingPacketHandlers.AddHandler(SMSG_LEVELUP_INFO, "levelup"); botOutgoingPacketHandlers.AddHandler(SMSG_LOG_XPGAIN, "xpgain"); From e9e79ad696c053e1c1b7fad0c5943ba9d99d5ad5 Mon Sep 17 00:00:00 2001 From: Hokken Date: Sun, 8 Feb 2026 11:45:03 +0000 Subject: [PATCH 12/25] Fix LootRollLevel=1 to match documented 'greed' behavior (#2068) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Fixes `AiPlayerbot.LootRollLevel = 1` to actually behave as "greed" mode per the config documentation. ## Problem The config documentation states: ```conf # Bots' loot roll level (0 = pass, 1 = greed, 2 = need) # Default: 1 (greed) AiPlayerbot.LootRollLevel = 1 ``` However, level 1 was converting **all GREED votes to PASS**, causing bots to pass on almost everything: | Item Type | AI Decision | Level 1 Behavior (Before) | Expected | |-----------|-------------|---------------------------|----------| | Gear upgrade | NEED | GREED ✓ | GREED | | Usable gear (not upgrade) | GREED | **PASS** ✗ | GREED | | Crafting materials | GREED | **PASS** ✗ | GREED | | Recipes, consumables | GREED | **PASS** ✗ | GREED | The only items bots would greed on were direct gear upgrades (originally NEED, downgraded to GREED). ## Root Cause In `LootRollAction.cpp`, lines 104-107 were converting GREED to PASS: ```cpp else if (vote == GREED) { vote = PASS; // This breaks "greed" mode } ``` ## Fix Remove the GREED→PASS conversion. Level 1 now only downgrades NEED to GREED (as intended), preserving GREED votes for useful items. ## Behavior After Fix | Level | Description | Behavior | |-------|-------------|----------| | 0 | Pass | Always pass on all items | | 1 | Greed | Greed on useful items, never need | | 2 | Need | Full AI logic (need/greed/pass) | ## Test Plan - [ ] Set `AiPlayerbot.LootRollLevel = 1` - [ ] Kill mobs that drop crafting materials, recipes, or non-upgrade gear - [ ] Verify bots greed on useful items instead of passing - [ ] Verify bots still pass on junk items - [ ] Verify bots never roll need (only greed) Co-authored-by: Hokken --- src/Ai/Base/Actions/LootRollAction.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Ai/Base/Actions/LootRollAction.cpp b/src/Ai/Base/Actions/LootRollAction.cpp index e600f1f204..9a21c8139f 100644 --- a/src/Ai/Base/Actions/LootRollAction.cpp +++ b/src/Ai/Base/Actions/LootRollAction.cpp @@ -90,6 +90,8 @@ bool LootRollAction::Execute(Event event) } else if (sPlayerbotAIConfig.lootRollLevel == 1) { + // Level 1 = "greed" mode: bots greed on useful items but never need + // Only downgrade NEED to GREED, preserve GREED votes as-is if (vote == NEED) { if (RollUniqueCheck(proto, bot)) @@ -101,10 +103,6 @@ bool LootRollAction::Execute(Event event) vote = GREED; } } - else if (vote == GREED) - { - vote = PASS; - } } switch (group->GetLootMethod()) { From c9c936d5c1483d99af2b0967a8c68c88322874fb Mon Sep 17 00:00:00 2001 From: dillyns <49765217+dillyns@users.noreply.github.com> Date: Sun, 8 Feb 2026 12:36:27 -0500 Subject: [PATCH 13/25] Add Unending Breath to Warlock NonCombat Strat (#2074) # Pull Request Adds actions and triggers for Warlock class to cast Unending Breath when swimming, following the existing implementation for Shaman Water Breathing. --- ## Feature Evaluation Add triggers for Warlock noncombat strategy for Unending Breath on self and party. Triggers should only be active while swimming. Minimal runtime cost on Warlock bots trigger processing. --- ## How to Test the Changes - Bring a Warlock bot into water - It should cast Unending Breath on itself and anyone in the party ## Complexity & Impact - Does this change add new decision branches? - [ ] No - [x] Yes (**explain below**) It adds triggers to Warlock to decide when to cast Unending Breath on self or party members. - Does this change increase per-bot or per-tick processing? - [ ] No - [x] Yes (**describe and justify impact**) Minimal additional processing for Warlock triggers, same as already existing triggers for Shaman. - 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: - [ ] 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 - [x] Yes (**explain below**) Claude was used to explore the codebase to find similar implementations that already existed. --- ## 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: bashermens <31279994+hermensbas@users.noreply.github.com> --- src/Ai/Class/Warlock/Action/WarlockActions.h | 12 ++++++++++++ .../Strategy/GenericWarlockNonCombatStrategy.cpp | 2 ++ src/Ai/Class/Warlock/Trigger/WarlockTriggers.cpp | 10 ++++++++++ src/Ai/Class/Warlock/Trigger/WarlockTriggers.h | 14 ++++++++++++++ src/Ai/Class/Warlock/WarlockAiObjectContext.cpp | 8 ++++++++ 5 files changed, 46 insertions(+) diff --git a/src/Ai/Class/Warlock/Action/WarlockActions.h b/src/Ai/Class/Warlock/Action/WarlockActions.h index 787b518d69..09f3463703 100644 --- a/src/Ai/Class/Warlock/Action/WarlockActions.h +++ b/src/Ai/Class/Warlock/Action/WarlockActions.h @@ -41,6 +41,18 @@ class CastSoulLinkAction : public CastBuffSpellAction std::string const GetTargetName() override { return "pet target"; } }; +class CastUnendingBreathAction : public CastBuffSpellAction +{ +public: + CastUnendingBreathAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "unending breath") {} +}; + +class CastUnendingBreathOnPartyAction : public BuffOnPartyAction +{ +public: + CastUnendingBreathOnPartyAction(PlayerbotAI* botAI) : BuffOnPartyAction(botAI, "unending breath") {} +}; + class CreateSoulShardAction : public Action { public: diff --git a/src/Ai/Class/Warlock/Strategy/GenericWarlockNonCombatStrategy.cpp b/src/Ai/Class/Warlock/Strategy/GenericWarlockNonCombatStrategy.cpp index a78ff0a988..d22abe3cab 100644 --- a/src/Ai/Class/Warlock/Strategy/GenericWarlockNonCombatStrategy.cpp +++ b/src/Ai/Class/Warlock/Strategy/GenericWarlockNonCombatStrategy.cpp @@ -85,6 +85,8 @@ void GenericWarlockNonCombatStrategy::InitTriggers(std::vector& tr triggers.push_back(new TriggerNode("too many soul shards", { NextAction("destroy soul shard", 60.0f) })); triggers.push_back(new TriggerNode("soul link", { NextAction("soul link", 28.0f) })); triggers.push_back(new TriggerNode("demon armor", { NextAction("fel armor", 27.0f) })); + triggers.push_back(new TriggerNode("unending breath", { NextAction("unending breath", 12.0f) })); + triggers.push_back(new TriggerNode("unending breath on party", { NextAction("unending breath on party", 11.0f) })); triggers.push_back(new TriggerNode("no healthstone", { NextAction("create healthstone", 26.0f) })); triggers.push_back(new TriggerNode("no soulstone", { NextAction("create soulstone", 25.0f) })); triggers.push_back(new TriggerNode("life tap", { NextAction("life tap", 23.0f) })); diff --git a/src/Ai/Class/Warlock/Trigger/WarlockTriggers.cpp b/src/Ai/Class/Warlock/Trigger/WarlockTriggers.cpp index 53e77669b2..1df531e7fd 100644 --- a/src/Ai/Class/Warlock/Trigger/WarlockTriggers.cpp +++ b/src/Ai/Class/Warlock/Trigger/WarlockTriggers.cpp @@ -79,6 +79,16 @@ bool SoulLinkTrigger::IsActive() return !botAI->HasAura("soul link", target); } +bool UnendingBreathTrigger::IsActive() +{ + return BuffTrigger::IsActive() && AI_VALUE2(bool, "swimming", "self target"); +} + +bool UnendingBreathOnPartyTrigger::IsActive() +{ + return BuffOnPartyTrigger::IsActive() && AI_VALUE2(bool, "swimming", "self target"); +} + bool DemonicEmpowermentTrigger::IsActive() { Pet* pet = bot->GetPet(); diff --git a/src/Ai/Class/Warlock/Trigger/WarlockTriggers.h b/src/Ai/Class/Warlock/Trigger/WarlockTriggers.h index 8512549012..d66fe537e1 100644 --- a/src/Ai/Class/Warlock/Trigger/WarlockTriggers.h +++ b/src/Ai/Class/Warlock/Trigger/WarlockTriggers.h @@ -32,6 +32,20 @@ class SoulLinkTrigger : public BuffTrigger bool IsActive() override; }; +class UnendingBreathTrigger : public BuffTrigger +{ +public: + UnendingBreathTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "unending breath", 5 * 2000) {} + bool IsActive() override; +}; + +class UnendingBreathOnPartyTrigger : public BuffOnPartyTrigger +{ +public: + UnendingBreathOnPartyTrigger(PlayerbotAI* botAI) : BuffOnPartyTrigger(botAI, "unending breath on party", 2 * 2000) {} + bool IsActive() override; +}; + class OutOfSoulShardsTrigger : public Trigger { public: diff --git a/src/Ai/Class/Warlock/WarlockAiObjectContext.cpp b/src/Ai/Class/Warlock/WarlockAiObjectContext.cpp index a70327b6c6..27b9166c65 100644 --- a/src/Ai/Class/Warlock/WarlockAiObjectContext.cpp +++ b/src/Ai/Class/Warlock/WarlockAiObjectContext.cpp @@ -143,6 +143,8 @@ class WarlockTriggerFactoryInternal : public NamedObjectContext creators["shadow trance"] = &WarlockTriggerFactoryInternal::shadow_trance; creators["demon armor"] = &WarlockTriggerFactoryInternal::demon_armor; creators["soul link"] = &WarlockTriggerFactoryInternal::soul_link; + creators["unending breath"] = &WarlockTriggerFactoryInternal::unending_breath; + creators["unending breath on party"] = &WarlockTriggerFactoryInternal::unending_breath_on_party; creators["no soul shard"] = &WarlockTriggerFactoryInternal::no_soul_shard; creators["too many soul shards"] = &WarlockTriggerFactoryInternal::too_many_soul_shards; creators["no healthstone"] = &WarlockTriggerFactoryInternal::HasHealthstone; @@ -189,6 +191,8 @@ class WarlockTriggerFactoryInternal : public NamedObjectContext static Trigger* shadow_trance(PlayerbotAI* botAI) { return new ShadowTranceTrigger(botAI); } static Trigger* demon_armor(PlayerbotAI* botAI) { return new DemonArmorTrigger(botAI); } static Trigger* soul_link(PlayerbotAI* botAI) { return new SoulLinkTrigger(botAI); } + static Trigger* unending_breath(PlayerbotAI* botAI) { return new UnendingBreathTrigger(botAI); } + static Trigger* unending_breath_on_party(PlayerbotAI* botAI) { return new UnendingBreathOnPartyTrigger(botAI); } static Trigger* no_soul_shard(PlayerbotAI* botAI) { return new OutOfSoulShardsTrigger(botAI); } static Trigger* too_many_soul_shards(PlayerbotAI* botAI) { return new TooManySoulShardsTrigger(botAI); } static Trigger* HasHealthstone(PlayerbotAI* botAI) { return new HasHealthstoneTrigger(botAI); } @@ -240,6 +244,8 @@ class WarlockAiObjectContextInternal : public NamedObjectContext creators["demon armor"] = &WarlockAiObjectContextInternal::demon_armor; creators["demon skin"] = &WarlockAiObjectContextInternal::demon_skin; creators["soul link"] = &WarlockAiObjectContextInternal::soul_link; + creators["unending breath"] = &WarlockAiObjectContextInternal::unending_breath; + creators["unending breath on party"] = &WarlockAiObjectContextInternal::unending_breath_on_party; creators["create soul shard"] = &WarlockAiObjectContextInternal::create_soul_shard; creators["destroy soul shard"] = &WarlockAiObjectContextInternal::destroy_soul_shard; creators["create healthstone"] = &WarlockAiObjectContextInternal::create_healthstone; @@ -313,6 +319,8 @@ class WarlockAiObjectContextInternal : public NamedObjectContext static Action* demon_armor(PlayerbotAI* botAI) { return new CastDemonArmorAction(botAI); } static Action* demon_skin(PlayerbotAI* botAI) { return new CastDemonSkinAction(botAI); } static Action* soul_link(PlayerbotAI* botAI) { return new CastSoulLinkAction(botAI); } + static Action* unending_breath(PlayerbotAI* botAI) { return new CastUnendingBreathAction(botAI); } + static Action* unending_breath_on_party(PlayerbotAI* botAI) { return new CastUnendingBreathOnPartyAction(botAI); } static Action* create_soul_shard(PlayerbotAI* botAI) { return new CreateSoulShardAction(botAI); } static Action* destroy_soul_shard(PlayerbotAI* botAI) { return new DestroySoulShardAction(botAI); } static Action* create_healthstone(PlayerbotAI* botAI) { return new CastCreateHealthstoneAction(botAI); } From 610fdc16d7e24fccbc336509831879d4c08db600 Mon Sep 17 00:00:00 2001 From: privatecore Date: Sun, 8 Feb 2026 18:36:56 +0100 Subject: [PATCH 14/25] Fix bug with GetCreature + GetGameObject = use ObjectAccessor's methods instead (#2105) # Pull Request https://en.cppreference.com/w/cpp/algorithm/equal_range.html > second is an iterator to the first element of the range [first, last) ordered after value (or last if no such element is found). The original code uses `return bounds.second->second`, which causes the wrong creature/gameobject to be returned. Instead, both methods (`GetCreature` and `GetGameObject`) now utilize ObjectAccessor's methods to retrieve the correct entities. These built-in methods offer a safer way to access objects. Additionally, `GetUnit` no longer includes redundant creature processing before checks and now has the same logic as the `ObjectAccessor::GetUnit` method. Furthermore, `GuidPosition::isDead` method has been renamed to `GuidPosition::IsCreatureOrGOAccessible` and updated, as it is used only for creatures (NOT units) and gameobjects. --- ## 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. --- ## How to Test the Changes The behavior has not changed after all. ## 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: - [ ] Lightweight mode remains the default - [ ] More complex behavior is optional and thereby configurable --- ## AI Assistance - Was AI assistance (e.g. ChatGPT or similar tools) used while working on this change? - [x] No - [ ] Yes (**explain below**) If yes, please specify: - 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 - [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: bashermens <31279994+hermensbas@users.noreply.github.com> --- src/Ai/Base/Value/QuestValues.cpp | 6 +- src/Mgr/Travel/TravelMgr.cpp | 136 +++++++++++++----------------- src/Mgr/Travel/TravelMgr.h | 7 +- 3 files changed, 66 insertions(+), 83 deletions(-) diff --git a/src/Ai/Base/Value/QuestValues.cpp b/src/Ai/Base/Value/QuestValues.cpp index 1724778509..f556f443d3 100644 --- a/src/Ai/Base/Value/QuestValues.cpp +++ b/src/Ai/Base/Value/QuestValues.cpp @@ -173,7 +173,7 @@ std::vector ActiveQuestGiversValue::Calculate() continue; } - if (guidp.isDead()) + if (!guidp.IsCreatureOrGOAccessible()) continue; retQuestGivers.push_back(guidp); @@ -231,7 +231,7 @@ std::vector ActiveQuestTakersValue::Calculate() for (auto& guidp : entry.second) { - if (guidp.isDead()) + if (!guidp.IsCreatureOrGOAccessible()) continue; retQuestTakers.push_back(guidp); @@ -298,7 +298,7 @@ std::vector ActiveQuestObjectivesValue::Calculate() { for (auto& guidp : entry.second) { - if (guidp.isDead()) + if (!guidp.IsCreatureOrGOAccessible()) continue; retQuestObjectives.push_back(guidp); diff --git a/src/Mgr/Travel/TravelMgr.cpp b/src/Mgr/Travel/TravelMgr.cpp index 84eef9b99e..748f929aa7 100644 --- a/src/Mgr/Travel/TravelMgr.cpp +++ b/src/Mgr/Travel/TravelMgr.cpp @@ -881,39 +881,63 @@ std::vector WorldPosition::getGameObjectsNear(float radiu return worker.GetResult(); } -Creature* GuidPosition::GetCreature() +CreatureTemplate const* GuidPosition::GetCreatureTemplate() +{ + return IsCreature() ? sObjectMgr->GetCreatureTemplate(GetEntry()) : nullptr; +} + +GameObjectTemplate const* GuidPosition::GetGameObjectTemplate() +{ + return IsGameObject() ? sObjectMgr->GetGameObjectTemplate(GetEntry()) : nullptr; +} + +WorldObject* GuidPosition::GetWorldObject() { if (!*this) return nullptr; - if (loadedFromDB) + switch (GetHigh()) { - auto creatureBounds = getMap()->GetCreatureBySpawnIdStore().equal_range(GetCounter()); - if (creatureBounds.first != creatureBounds.second) - return creatureBounds.second->second; - - return nullptr; + case HighGuid::Player: + return GetPlayer(); + case HighGuid::Transport: + case HighGuid::Mo_Transport: + case HighGuid::GameObject: + return GetGameObject(); + case HighGuid::Vehicle: + case HighGuid::Unit: + return GetCreature(); + case HighGuid::Pet: + return getMap()->GetPet(*this); + case HighGuid::DynamicObject: + return getMap()->GetDynamicObject(*this); + case HighGuid::Corpse: + return getMap()->GetCorpse(*this); + default: + return nullptr; } - return getMap()->GetCreature(*this); + return nullptr; } -Unit* GuidPosition::GetUnit() +GameObject* GuidPosition::GetGameObject() { if (!*this) return nullptr; if (loadedFromDB) - { - auto creatureBounds = getMap()->GetCreatureBySpawnIdStore().equal_range(GetCounter()); - if (creatureBounds.first != creatureBounds.second) - return creatureBounds.second->second; + return ObjectAccessor::GetSpawnedGameObjectByDBGUID(GetMapId(), GetCounter()); + return getMap()->GetGameObject(*this); // fallback +} + +Unit* GuidPosition::GetUnit() +{ + if (!*this) return nullptr; - } if (IsPlayer()) - return ObjectAccessor::FindPlayer(*this); + return GetPlayer(); if (IsPet()) return getMap()->GetPet(*this); @@ -921,21 +945,15 @@ Unit* GuidPosition::GetUnit() return GetCreature(); } -GameObject* GuidPosition::GetGameObject() +Creature* GuidPosition::GetCreature() { if (!*this) return nullptr; if (loadedFromDB) - { - auto gameobjectBounds = getMap()->GetGameObjectBySpawnIdStore().equal_range(GetCounter()); - if (gameobjectBounds.first != gameobjectBounds.second) - return gameobjectBounds.second->second; + return ObjectAccessor::GetSpawnedCreatureByDBGUID(GetMapId(), GetCounter()); - return nullptr; - } - - return getMap()->GetGameObject(*this); + return getMap()->GetCreature(*this); // fallback } Player* GuidPosition::GetPlayer() @@ -949,21 +967,28 @@ Player* GuidPosition::GetPlayer() return nullptr; } -bool GuidPosition::isDead() -{ - if (!getMap()) - return false; - - if (!getMap()->IsGridLoaded(getX(), getY())) - return false; +bool GuidPosition::HasNpcFlag(NPCFlags flag) { return IsCreature() && GetCreatureTemplate()->npcflag & flag; } - if (IsUnit() && GetUnit() && GetUnit()->IsInWorld() && GetUnit()->IsAlive()) +bool GuidPosition::IsCreatureOrGOAccessible() +{ + Map* map = getMap(); + if (!map || !map->IsGridLoaded(GetPositionX(), GetPositionY())) return false; - if (IsGameObject() && GetGameObject() && GetGameObject()->IsInWorld()) - return false; + if (IsCreature()) + { + Creature* creature = GetCreature(); + if (creature && creature->IsInWorld() && creature->IsAlive()) + return true; + } + else if (IsGameObject()) + { + GameObject* go = GetGameObject(); + if (go && go->IsInWorld()) + return true; + } - return true; + return false; } GuidPosition::GuidPosition(WorldObject* wo) : ObjectGuid(wo->GetGUID()), WorldPosition(wo), loadedFromDB(false) {} @@ -982,47 +1007,6 @@ GuidPosition::GuidPosition(GameObjectData const& goData) loadedFromDB = true; } -CreatureTemplate const* GuidPosition::GetCreatureTemplate() -{ - return IsCreature() ? sObjectMgr->GetCreatureTemplate(GetEntry()) : nullptr; -} - -GameObjectTemplate const* GuidPosition::GetGameObjectTemplate() -{ - return IsGameObject() ? sObjectMgr->GetGameObjectTemplate(GetEntry()) : nullptr; -} - -WorldObject* GuidPosition::GetWorldObject() -{ - if (!*this) - return nullptr; - - switch (GetHigh()) - { - case HighGuid::Player: - return ObjectAccessor::FindPlayer(*this); - case HighGuid::Transport: - case HighGuid::Mo_Transport: - case HighGuid::GameObject: - return GetGameObject(); - case HighGuid::Vehicle: - case HighGuid::Unit: - return GetCreature(); - case HighGuid::Pet: - return getMap()->GetPet(*this); - case HighGuid::DynamicObject: - return getMap()->GetDynamicObject(*this); - case HighGuid::Corpse: - return getMap()->GetCorpse(*this); - default: - return nullptr; - } - - return nullptr; -} - -bool GuidPosition::HasNpcFlag(NPCFlags flag) { return IsCreature() && GetCreatureTemplate()->npcflag & flag; } - std::vector TravelDestination::getPoints(bool ignoreFull) { if (ignoreFull) diff --git a/src/Mgr/Travel/TravelMgr.h b/src/Mgr/Travel/TravelMgr.h index 3223444bfe..251313736a 100644 --- a/src/Mgr/Travel/TravelMgr.h +++ b/src/Mgr/Travel/TravelMgr.h @@ -416,14 +416,13 @@ class GuidPosition : public ObjectGuid, public WorldPosition GameObjectTemplate const* GetGameObjectTemplate(); WorldObject* GetWorldObject(); - Creature* GetCreature(); - Unit* GetUnit(); GameObject* GetGameObject(); + Unit* GetUnit(); + Creature* GetCreature(); Player* GetPlayer(); bool HasNpcFlag(NPCFlags flag); - - bool isDead(); // For loaded grids check if the unit/object is unloaded/dead. + bool IsCreatureOrGOAccessible(); // For loaded grids check if the creature/gameobject is in world + alive operator bool() const { return !IsEmpty(); } bool operator==(ObjectGuid const& guid) const { return GetRawValue() == guid.GetRawValue(); } From ee2a399ac8544bbb6cc9d56e88252afce8405fc1 Mon Sep 17 00:00:00 2001 From: Keleborn <22352763+Celandriel@users.noreply.github.com> Date: Fri, 13 Feb 2026 09:19:54 -0800 Subject: [PATCH 15/25] Refactor newrpginfo data union to std::variant (#2079) # Pull Request As I began modifying the newrpginfo to change the types of data it stored, or add new data I found myself with the issue of ending up either with garbage memory if the information wasnt properly stored on status change, or needing complicated destructor patterns for non trivial data sets. --- ## Design Philosophy Make rpginfo able to handle more complicated information in a strongly --- ## Feature Evaluation No Feature changes --- ## How to Test the Changes - Server should be stable for an extended period of time. - Bots should be able to complete quests, fly, etc as they did before. ## Complexity & Impact - Does this change add new decision branches? - [X ] No - [ ] Yes (**explain below**) - Does this change increase per-bot or per-tick processing? - [ ] No - [ X] Yes (**describe and justify impact**) Potentially as there can be more memory involved in the object. - 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: - [ ] 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 - [ X] Yes (**explain below**) If yes, please specify: - Gemini suggested the use of std::variant as an alternative data structure. I found additinal external references that correlated with the same suggestion of moving away from a union. - Implementation was performed manually with Co-pilot auto-complete --- ## Final Checklist In progress. - [ ] 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 Im not 100% sure if this is a good design choice. There are some things I didnt quite like by the end of this, specifically having to double check whenever accessing data whether exists or not even though an action has already been triggered. But I have a PR in the works where I want to store a full flight path vector, and the union was giving me issues. (It appears that state changes may be occuring in the same tick between RPG status update and the stated action, leading to incorrect data gathering. I ended up solving it by first checking a pointer to the object, and then getting the reference. ```c++ auto* dataPtr = std::get_if(&info.data); if (!dataPtr) return false; auto& data = *dataPtr; ``` --------- Co-authored-by: bashermens <31279994+hermensbas@users.noreply.github.com> --- src/Ai/Base/Value/GrindTargetValue.cpp | 2 +- src/Ai/World/Rpg/Action/NewRpgAction.cpp | 157 ++++++++++++--------- src/Ai/World/Rpg/Action/NewRpgAction.h | 4 +- src/Ai/World/Rpg/NewRpgInfo.cpp | 139 ++++++++++-------- src/Ai/World/Rpg/NewRpgInfo.h | 30 ++-- src/Ai/World/Rpg/Trigger/NewRpgTrigger.cpp | 2 +- src/Bot/RandomPlayerbotMgr.cpp | 2 +- src/PlayerbotAIConfig.h | 21 ++- 8 files changed, 196 insertions(+), 161 deletions(-) diff --git a/src/Ai/Base/Value/GrindTargetValue.cpp b/src/Ai/Base/Value/GrindTargetValue.cpp index ce021a2d9a..573de8fb06 100644 --- a/src/Ai/Base/Value/GrindTargetValue.cpp +++ b/src/Ai/Base/Value/GrindTargetValue.cpp @@ -107,7 +107,7 @@ Unit* GrindTargetValue::FindTargetForGrinding(uint32 assistCount) continue; } - bool inactiveGrindStatus = botAI->rpgInfo.status != RPG_WANDER_RANDOM && botAI->rpgInfo.status != RPG_IDLE; + bool inactiveGrindStatus = botAI->rpgInfo.GetStatus() != RPG_WANDER_RANDOM && botAI->rpgInfo.GetStatus() != RPG_IDLE; float aggroRange = 30.0f; if (unit->ToCreature()) diff --git a/src/Ai/World/Rpg/Action/NewRpgAction.cpp b/src/Ai/World/Rpg/Action/NewRpgAction.cpp index 41fec2291c..2c42741c88 100644 --- a/src/Ai/World/Rpg/Action/NewRpgAction.cpp +++ b/src/Ai/World/Rpg/Action/NewRpgAction.cpp @@ -64,17 +64,18 @@ bool StartRpgDoQuestAction::Execute(Event event) bool NewRpgStatusUpdateAction::Execute(Event event) { NewRpgInfo& info = botAI->rpgInfo; - switch (info.status) + NewRpgStatus status = info.GetStatus(); + switch (status) { case RPG_IDLE: - { return RandomChangeStatus({RPG_GO_CAMP, RPG_GO_GRIND, RPG_WANDER_RANDOM, RPG_WANDER_NPC, RPG_DO_QUEST, RPG_TRAVEL_FLIGHT, RPG_REST}); - } + case RPG_GO_GRIND: { - WorldPosition& originalPos = info.go_grind.pos; - assert(info.go_grind.pos != WorldPosition()); + auto& data = std::get(info.data); + WorldPosition& originalPos = data.pos; + assert(data.pos != WorldPosition()); // GO_GRIND -> WANDER_RANDOM if (bot->GetExactDist(originalPos) < 10.0f) { @@ -85,8 +86,9 @@ bool NewRpgStatusUpdateAction::Execute(Event event) } case RPG_GO_CAMP: { - WorldPosition& originalPos = info.go_camp.pos; - assert(info.go_camp.pos != WorldPosition()); + auto& data = std::get(info.data); + WorldPosition& originalPos = data.pos; + assert(data.pos != WorldPosition()); // GO_CAMP -> WANDER_NPC if (bot->GetExactDist(originalPos) < 10.0f) { @@ -126,7 +128,8 @@ bool NewRpgStatusUpdateAction::Execute(Event event) } case RPG_TRAVEL_FLIGHT: { - if (info.flight.inFlight && !bot->IsInFlight()) + auto& data = std::get(info.data); + if (data.inFlight && !bot->IsInFlight()) { // flight arrival info.ChangeToIdle(); @@ -154,8 +157,10 @@ bool NewRpgGoGrindAction::Execute(Event event) { if (SearchQuestGiverAndAcceptOrReward()) return true; + if (auto* data = std::get_if(&botAI->rpgInfo.data)) + return MoveFarTo(data->pos); - return MoveFarTo(botAI->rpgInfo.go_grind.pos); + return false; } bool NewRpgGoCampAction::Execute(Event event) @@ -163,7 +168,10 @@ bool NewRpgGoCampAction::Execute(Event event) if (SearchQuestGiverAndAcceptOrReward()) return true; - return MoveFarTo(botAI->rpgInfo.go_camp.pos); + if (auto* data = std::get_if(&botAI->rpgInfo.data)) + return MoveFarTo(data->pos); + + return false; } bool NewRpgWanderRandomAction::Execute(Event event) @@ -177,7 +185,11 @@ bool NewRpgWanderRandomAction::Execute(Event event) bool NewRpgWanderNpcAction::Execute(Event event) { NewRpgInfo& info = botAI->rpgInfo; - if (!info.wander_npc.npcOrGo) + auto* dataPtr = std::get_if(&info.data); + if (!dataPtr) + return false; + auto& data = *dataPtr; + if (!data.npcOrGo) { // No npc can be found, switch to IDLE ObjectGuid npcOrGo = ChooseNpcOrGameObjectToInteract(); @@ -186,33 +198,32 @@ bool NewRpgWanderNpcAction::Execute(Event event) info.ChangeToIdle(); return true; } - info.wander_npc.npcOrGo = npcOrGo; - info.wander_npc.lastReach = 0; + data.npcOrGo = npcOrGo; + data.lastReach = 0; return true; } - WorldObject* object = ObjectAccessor::GetWorldObject(*bot, info.wander_npc.npcOrGo); + WorldObject* object = ObjectAccessor::GetWorldObject(*bot, data.npcOrGo); if (object && IsWithinInteractionDist(object)) { - if (!info.wander_npc.lastReach) + if (!data.lastReach) { - info.wander_npc.lastReach = getMSTime(); + data.lastReach = getMSTime(); if (bot->CanInteractWithQuestGiver(object)) - InteractWithNpcOrGameObjectForQuest(info.wander_npc.npcOrGo); + InteractWithNpcOrGameObjectForQuest(data.npcOrGo); return true; } - if (info.wander_npc.lastReach && GetMSTimeDiffToNow(info.wander_npc.lastReach) < npcStayTime) + if (data.lastReach && GetMSTimeDiffToNow(data.lastReach) < npcStayTime) return false; // has reached the npc for more than `npcStayTime`, select the next target - info.wander_npc.npcOrGo = ObjectGuid(); - info.wander_npc.lastReach = 0; + data.npcOrGo = ObjectGuid(); + data.lastReach = 0; } else - { - return MoveWorldObjectTo(info.wander_npc.npcOrGo); - } + return MoveWorldObjectTo(data.npcOrGo); + return true; } @@ -222,29 +233,33 @@ bool NewRpgDoQuestAction::Execute(Event event) return true; NewRpgInfo& info = botAI->rpgInfo; - uint32 questId = RPG_INFO(quest, questId); - const Quest* quest = RPG_INFO(quest, quest); + auto* dataPtr = std::get_if(&info.data); + if (!dataPtr) + return false; + auto& data = *dataPtr; + uint32 questId = data.questId; + const Quest* quest = data.quest; uint8 questStatus = bot->GetQuestStatus(questId); switch (questStatus) { case QUEST_STATUS_INCOMPLETE: - return DoIncompleteQuest(); + return DoIncompleteQuest(data); case QUEST_STATUS_COMPLETE: - return DoCompletedQuest(); + return DoCompletedQuest(data); default: break; } - botAI->rpgInfo.ChangeToIdle(); + info.ChangeToIdle(); return true; } -bool NewRpgDoQuestAction::DoIncompleteQuest() +bool NewRpgDoQuestAction::DoIncompleteQuest(NewRpgInfo::DoQuest& data) { - uint32 questId = RPG_INFO(do_quest, questId); - if (botAI->rpgInfo.do_quest.pos != WorldPosition()) + uint32 questId = data.questId; + if (data.pos != WorldPosition()) { /// @TODO: extract to a new function - int32 currentObjective = botAI->rpgInfo.do_quest.objectiveIdx; + int32 currentObjective = data.objectiveIdx; // check if the objective has completed Quest const* quest = sObjectMgr->GetQuestTemplate(questId); const QuestStatusData& q_status = bot->getQuestStatusMap().at(questId); @@ -263,12 +278,12 @@ bool NewRpgDoQuestAction::DoIncompleteQuest() // the current objective is completed, clear and find a new objective later if (completed) { - botAI->rpgInfo.do_quest.lastReachPOI = 0; - botAI->rpgInfo.do_quest.pos = WorldPosition(); - botAI->rpgInfo.do_quest.objectiveIdx = 0; + data.lastReachPOI = 0; + data.pos = WorldPosition(); + data.objectiveIdx = 0; } } - if (botAI->rpgInfo.do_quest.pos == WorldPosition()) + if (data.pos == WorldPosition()) { std::vector poiInfo; if (!GetQuestPOIPosAndObjectiveIdx(questId, poiInfo)) @@ -291,28 +306,28 @@ bool NewRpgDoQuestAction::DoIncompleteQuest() return false; WorldPosition pos(bot->GetMapId(), dx, dy, dz); - botAI->rpgInfo.do_quest.lastReachPOI = 0; - botAI->rpgInfo.do_quest.pos = pos; - botAI->rpgInfo.do_quest.objectiveIdx = objectiveIdx; + data.lastReachPOI = 0; + data.pos = pos; + data.objectiveIdx = objectiveIdx; } - if (bot->GetDistance(botAI->rpgInfo.do_quest.pos) > 10.0f && !botAI->rpgInfo.do_quest.lastReachPOI) + if (bot->GetDistance(data.pos) > 10.0f && !data.lastReachPOI) { - return MoveFarTo(botAI->rpgInfo.do_quest.pos); + return MoveFarTo(data.pos); } // Now we are near the quest objective // kill mobs and looting quest should be done automatically by grind strategy - if (!botAI->rpgInfo.do_quest.lastReachPOI) + if (!data.lastReachPOI) { - botAI->rpgInfo.do_quest.lastReachPOI = getMSTime(); + data.lastReachPOI = getMSTime(); return true; } // stayed at this POI for more than 5 minutes - if (GetMSTimeDiffToNow(botAI->rpgInfo.do_quest.lastReachPOI) >= poiStayTime) + if (GetMSTimeDiffToNow(data.lastReachPOI) >= poiStayTime) { bool hasProgression = false; - int32 currentObjective = botAI->rpgInfo.do_quest.objectiveIdx; + int32 currentObjective = data.objectiveIdx; // check if the objective has progression Quest const* quest = sObjectMgr->GetQuestTemplate(questId); const QuestStatusData& q_status = bot->getQuestStatusMap().at(questId); @@ -339,21 +354,21 @@ bool NewRpgDoQuestAction::DoIncompleteQuest() return true; } // clear and select another poi later - botAI->rpgInfo.do_quest.lastReachPOI = 0; - botAI->rpgInfo.do_quest.pos = WorldPosition(); - botAI->rpgInfo.do_quest.objectiveIdx = 0; + data.lastReachPOI = 0; + data.pos = WorldPosition(); + data.objectiveIdx = 0; return true; } return MoveRandomNear(20.0f); } -bool NewRpgDoQuestAction::DoCompletedQuest() +bool NewRpgDoQuestAction::DoCompletedQuest(NewRpgInfo::DoQuest& data) { - uint32 questId = RPG_INFO(quest, questId); - const Quest* quest = RPG_INFO(quest, quest); + uint32 questId = data.questId; + const Quest* quest = data.quest; - if (RPG_INFO(quest, objectiveIdx) != -1) + if (data.objectiveIdx != -1) { // if quest is completed, back to poi with -1 idx to reward BroadcastHelper::BroadcastQuestUpdateComplete(botAI, bot, quest); @@ -376,26 +391,26 @@ bool NewRpgDoQuestAction::DoCompletedQuest() return false; WorldPosition pos(bot->GetMapId(), dx, dy, dz); - botAI->rpgInfo.do_quest.lastReachPOI = 0; - botAI->rpgInfo.do_quest.pos = pos; - botAI->rpgInfo.do_quest.objectiveIdx = -1; + data.lastReachPOI = 0; + data.pos = pos; + data.objectiveIdx = -1; } - if (botAI->rpgInfo.do_quest.pos == WorldPosition()) + if (data.pos == WorldPosition()) return false; - if (bot->GetDistance(botAI->rpgInfo.do_quest.pos) > 10.0f && !botAI->rpgInfo.do_quest.lastReachPOI) - return MoveFarTo(botAI->rpgInfo.do_quest.pos); + if (bot->GetDistance(data.pos) > 10.0f && !data.lastReachPOI) + return MoveFarTo(data.pos); // Now we are near the qoi of reward // the quest should be rewarded by SearchQuestGiverAndAcceptOrReward - if (!botAI->rpgInfo.do_quest.lastReachPOI) + if (!data.lastReachPOI) { - botAI->rpgInfo.do_quest.lastReachPOI = getMSTime(); + data.lastReachPOI = getMSTime(); return true; } // stayed at this POI for more than 5 minutes - if (GetMSTimeDiffToNow(botAI->rpgInfo.do_quest.lastReachPOI) >= poiStayTime) + if (GetMSTimeDiffToNow(data.lastReachPOI) >= poiStayTime) { // e.g. Can not reward quest to gameobjects /// @TODO: It may be better to make lowPriorityQuest a global set shared by all bots (or saved in db) @@ -410,29 +425,33 @@ bool NewRpgDoQuestAction::DoCompletedQuest() bool NewRpgTravelFlightAction::Execute(Event event) { + NewRpgInfo& info = botAI->rpgInfo; + auto* dataPtr = std::get_if(&info.data); + if (!dataPtr) + return false; + + auto& data = *dataPtr; if (bot->IsInFlight()) { - botAI->rpgInfo.flight.inFlight = true; + data.inFlight = true; return false; } - Creature* flightMaster = ObjectAccessor::GetCreature(*bot, botAI->rpgInfo.flight.fromFlightMaster); + Creature* flightMaster = ObjectAccessor::GetCreature(*bot, data.fromFlightMaster); if (!flightMaster || !flightMaster->IsAlive()) { botAI->rpgInfo.ChangeToIdle(); return true; } - const TaxiNodesEntry* entry = sTaxiNodesStore.LookupEntry(botAI->rpgInfo.flight.toNode); + const TaxiNodesEntry* entry = sTaxiNodesStore.LookupEntry(data.toNode); if (bot->GetDistance(flightMaster) > INTERACTION_DISTANCE) - { return MoveFarTo(flightMaster); - } - std::vector nodes = {botAI->rpgInfo.flight.fromNode, botAI->rpgInfo.flight.toNode}; + + std::vector nodes = {data.fromNode, data.toNode}; botAI->RemoveShapeshift(); if (bot->IsMounted()) - { bot->Dismount(); - } + if (!bot->ActivateTaxiPathTo(nodes, flightMaster, 0)) { LOG_DEBUG("playerbots", "[New RPG] {} active taxi path {} (from {} to {}) failed", bot->GetName(), diff --git a/src/Ai/World/Rpg/Action/NewRpgAction.h b/src/Ai/World/Rpg/Action/NewRpgAction.h index 0e621fc406..a8cb7a2bce 100644 --- a/src/Ai/World/Rpg/Action/NewRpgAction.h +++ b/src/Ai/World/Rpg/Action/NewRpgAction.h @@ -90,8 +90,8 @@ class NewRpgDoQuestAction : public NewRpgBaseAction bool Execute(Event event) override; protected: - bool DoIncompleteQuest(); - bool DoCompletedQuest(); + bool DoIncompleteQuest(NewRpgInfo::DoQuest& data); + bool DoCompletedQuest(NewRpgInfo::DoQuest& data); const uint32 poiStayTime = 5 * 60 * 1000; }; diff --git a/src/Ai/World/Rpg/NewRpgInfo.cpp b/src/Ai/World/Rpg/NewRpgInfo.cpp index 889fb1ff92..def0d472de 100644 --- a/src/Ai/World/Rpg/NewRpgInfo.cpp +++ b/src/Ai/World/Rpg/NewRpgInfo.cpp @@ -6,71 +6,65 @@ void NewRpgInfo::ChangeToGoGrind(WorldPosition pos) { - Reset(); - status = RPG_GO_GRIND; - go_grind = GoGrind(); - go_grind.pos = pos; + startT = getMSTime(); + data = GoGrind{pos}; } void NewRpgInfo::ChangeToGoCamp(WorldPosition pos) { - Reset(); - status = RPG_GO_CAMP; - go_camp = GoCamp(); - go_camp.pos = pos; + startT = getMSTime(); + data = GoCamp{pos}; } void NewRpgInfo::ChangeToWanderNpc() { - Reset(); - status = RPG_WANDER_NPC; - wander_npc = WanderNpc(); + startT = getMSTime(); + data = WanderNpc{}; } void NewRpgInfo::ChangeToWanderRandom() { - Reset(); - status = RPG_WANDER_RANDOM; - WANDER_RANDOM = WanderRandom(); + startT = getMSTime(); + data = WanderRandom{}; } void NewRpgInfo::ChangeToDoQuest(uint32 questId, const Quest* quest) { - Reset(); - status = RPG_DO_QUEST; - do_quest = DoQuest(); + startT = getMSTime(); + DoQuest do_quest; do_quest.questId = questId; do_quest.quest = quest; + data = do_quest; } void NewRpgInfo::ChangeToTravelFlight(ObjectGuid fromFlightMaster, uint32 fromNode, uint32 toNode) { - Reset(); - status = RPG_TRAVEL_FLIGHT; - flight = TravelFlight(); + startT = getMSTime(); + TravelFlight flight; flight.fromFlightMaster = fromFlightMaster; flight.fromNode = fromNode; flight.toNode = toNode; + flight.inFlight = false; + data = flight; } void NewRpgInfo::ChangeToRest() { - Reset(); - status = RPG_REST; - rest = Rest(); + startT = getMSTime(); + data = Rest{}; } void NewRpgInfo::ChangeToIdle() { - Reset(); - status = RPG_IDLE; + startT = getMSTime(); + data = Idle{}; } bool NewRpgInfo::CanChangeTo(NewRpgStatus status) { return true; } void NewRpgInfo::Reset() { - *this = NewRpgInfo(); + data = Idle{}; startT = getMSTime(); } @@ -82,58 +76,83 @@ void NewRpgInfo::SetMoveFarTo(WorldPosition pos) moveFarPos = pos; } +NewRpgStatus NewRpgInfo::GetStatus() +{ + return std::visit([](auto&& arg) -> NewRpgStatus { + using T = std::decay_t; + if constexpr (std::is_same_v) return RPG_IDLE; + if constexpr (std::is_same_v) return RPG_GO_GRIND; + if constexpr (std::is_same_v) return RPG_GO_CAMP; + if constexpr (std::is_same_v) return RPG_WANDER_NPC; + if constexpr (std::is_same_v) return RPG_WANDER_RANDOM; + if constexpr (std::is_same_v) return RPG_REST; + if constexpr (std::is_same_v) return RPG_DO_QUEST; + if constexpr (std::is_same_v) return RPG_TRAVEL_FLIGHT; + return RPG_IDLE; + }, data); +} + std::string NewRpgInfo::ToString() { std::stringstream out; out << "Status: "; - switch (status) + std::visit([&out, this](auto&& arg) { - case RPG_GO_GRIND: + using T = std::decay_t; + if constexpr (std::is_same_v) + { out << "GO_GRIND"; - out << "\nGrindPos: " << go_grind.pos.GetMapId() << " " << go_grind.pos.GetPositionX() << " " - << go_grind.pos.GetPositionY() << " " << go_grind.pos.GetPositionZ(); + out << "\nGrindPos: " << arg.pos.GetMapId() << " " << arg.pos.GetPositionX() << " " + << arg.pos.GetPositionY() << " " << arg.pos.GetPositionZ(); out << "\nlastGoGrind: " << startT; - break; - case RPG_GO_CAMP: + } + else if constexpr (std::is_same_v) + { out << "GO_CAMP"; - out << "\nCampPos: " << go_camp.pos.GetMapId() << " " << go_camp.pos.GetPositionX() << " " - << go_camp.pos.GetPositionY() << " " << go_camp.pos.GetPositionZ(); + out << "\nCampPos: " << arg.pos.GetMapId() << " " << arg.pos.GetPositionX() << " " + << arg.pos.GetPositionY() << " " << arg.pos.GetPositionZ(); out << "\nlastGoCamp: " << startT; - break; - case RPG_WANDER_NPC: + } + else if constexpr (std::is_same_v) + { out << "WANDER_NPC"; - out << "\nnpcOrGoEntry: " << wander_npc.npcOrGo.GetCounter(); + out << "\nnpcOrGoEntry: " << arg.npcOrGo.GetCounter(); out << "\nlastWanderNpc: " << startT; - out << "\nlastReachNpcOrGo: " << wander_npc.lastReach; - break; - case RPG_WANDER_RANDOM: + out << "\nlastReachNpcOrGo: " << arg.lastReach; + } + else if constexpr (std::is_same_v) + { out << "WANDER_RANDOM"; out << "\nlastWanderRandom: " << startT; - break; - case RPG_IDLE: + } + else if constexpr (std::is_same_v) + { out << "IDLE"; - break; - case RPG_REST: + } + else if constexpr (std::is_same_v) + { out << "REST"; out << "\nlastRest: " << startT; - break; - case RPG_DO_QUEST: + } + else if constexpr (std::is_same_v) + { out << "DO_QUEST"; - out << "\nquestId: " << do_quest.questId; - out << "\nobjectiveIdx: " << do_quest.objectiveIdx; - out << "\npoiPos: " << do_quest.pos.GetMapId() << " " << do_quest.pos.GetPositionX() << " " - << do_quest.pos.GetPositionY() << " " << do_quest.pos.GetPositionZ(); - out << "\nlastReachPOI: " << do_quest.lastReachPOI ? GetMSTimeDiffToNow(do_quest.lastReachPOI) : 0; - break; - case RPG_TRAVEL_FLIGHT: + out << "\nquestId: " << arg.questId; + out << "\nobjectiveIdx: " << arg.objectiveIdx; + out << "\npoiPos: " << arg.pos.GetMapId() << " " << arg.pos.GetPositionX() << " " + << arg.pos.GetPositionY() << " " << arg.pos.GetPositionZ(); + out << "\nlastReachPOI: " << (arg.lastReachPOI ? GetMSTimeDiffToNow(arg.lastReachPOI) : 0); + } + else if constexpr (std::is_same_v) + { out << "TRAVEL_FLIGHT"; - out << "\nfromFlightMaster: " << flight.fromFlightMaster.GetEntry(); - out << "\nfromNode: " << flight.fromNode; - out << "\ntoNode: " << flight.toNode; - out << "\ninFlight: " << flight.inFlight; - break; - default: + out << "\nfromFlightMaster: " << arg.fromFlightMaster.GetEntry(); + out << "\nfromNode: " << arg.fromNode; + out << "\ntoNode: " << arg.toNode; + out << "\ninFlight: " << arg.inFlight; + } + else out << "UNKNOWN"; - } + }, data); return out.str(); } diff --git a/src/Ai/World/Rpg/NewRpgInfo.h b/src/Ai/World/Rpg/NewRpgInfo.h index 20f801bf73..5b6ae3cb95 100644 --- a/src/Ai/World/Rpg/NewRpgInfo.h +++ b/src/Ai/World/Rpg/NewRpgInfo.h @@ -13,7 +13,8 @@ using NewRpgStatusTransitionProb = std::vector>; struct NewRpgInfo { - NewRpgInfo() {} + NewRpgInfo() : data(Idle{}) {} + ~NewRpgInfo() = default; // RPG_GO_GRIND struct GoGrind @@ -61,7 +62,6 @@ struct NewRpgInfo struct Idle { }; - NewRpgStatus status{RPG_IDLE}; uint32 startT{0}; // start timestamp of the current status @@ -72,18 +72,19 @@ struct NewRpgInfo WorldPosition moveFarPos; // END MOVE_FAR - union - { - GoGrind go_grind; - GoCamp go_camp; - WanderNpc wander_npc; - WanderRandom WANDER_RANDOM; - DoQuest do_quest; - Rest rest; - DoQuest quest; - TravelFlight flight; - }; + using RpgData = std::variant< + Idle, + GoGrind, + GoCamp, + WanderNpc, + WanderRandom, + DoQuest, + Rest, + TravelFlight + >; + RpgData data; + NewRpgStatus GetStatus(); bool HasStatusPersisted(uint32 maxDuration) { return GetMSTimeDiffToNow(startT) > maxDuration; } void ChangeToGoGrind(WorldPosition pos); void ChangeToGoCamp(WorldPosition pos); @@ -127,7 +128,4 @@ struct NewRpgStatistic } }; -// not sure is it necessary but keep it for now -#define RPG_INFO(x, y) botAI->rpgInfo.x.y - #endif diff --git a/src/Ai/World/Rpg/Trigger/NewRpgTrigger.cpp b/src/Ai/World/Rpg/Trigger/NewRpgTrigger.cpp index 0804320ad9..65e78eb4ca 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; } +bool NewRpgStatusTrigger::IsActive() { return status == botAI->rpgInfo.GetStatus(); } diff --git a/src/Bot/RandomPlayerbotMgr.cpp b/src/Bot/RandomPlayerbotMgr.cpp index 892368c66f..c2b3fae3c2 100644 --- a/src/Bot/RandomPlayerbotMgr.cpp +++ b/src/Bot/RandomPlayerbotMgr.cpp @@ -3252,7 +3252,7 @@ void RandomPlayerbotMgr::PrintStats() if (sPlayerbotAIConfig.enableNewRpgStrategy) { - rpgStatusCount[botAI->rpgInfo.status]++; + rpgStatusCount[botAI->rpgInfo.GetStatus()]++; rpgStasticTotal += botAI->rpgStatistic; botAI->rpgStatistic = NewRpgStatistic(); } diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index 729fc5be16..27177565cd 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -42,21 +42,20 @@ enum class HealingManaEfficiency : uint8 enum NewRpgStatus : int { - RPG_STATUS_START = 0, - // Going to far away place - RPG_GO_GRIND = 0, - RPG_GO_CAMP = 1, + //Initial Status + RPG_IDLE = 0, + RPG_GO_GRIND = 1, + RPG_GO_CAMP = 2, // Exploring nearby - RPG_WANDER_RANDOM = 2, - RPG_WANDER_NPC = 3, + RPG_WANDER_RANDOM = 3, + RPG_WANDER_NPC = 4, // Do Quest (based on quest status) - RPG_DO_QUEST = 4, + RPG_DO_QUEST = 5, // Travel - RPG_TRAVEL_FLIGHT = 5, + + RPG_TRAVEL_FLIGHT = 6, // Taking a break - RPG_REST = 6, - // Initial status - RPG_IDLE = 7, + RPG_REST = 7, RPG_STATUS_END = 8 }; From 80b3823f128adaed494ef5e2ac2ed6d8dda93cb8 Mon Sep 17 00:00:00 2001 From: Keleborn <22352763+Celandriel@users.noreply.github.com> Date: Fri, 13 Feb 2026 09:22:27 -0800 Subject: [PATCH 16/25] Warnings PR 3, remove std::move when not necessary. (#2108) # Pull Request std::move was being used in a few places to return a vector. Its not necessary. A direct return allows for some optimizations that moving wouldnt. ## How to Test the Changes -Bots should initialize correctly ## 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 - [ ] 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. --------- Co-authored-by: bashermens <31279994+hermensbas@users.noreply.github.com> --- src/Ai/Base/Actions/ChangeTalentsAction.cpp | 4 ++-- src/Bot/Factory/RandomPlayerbotFactory.cpp | 18 +++++++++--------- src/Bot/RandomPlayerbotMgr.cpp | 2 +- src/Mgr/Item/RandomItemMgr.cpp | 8 ++++---- src/Mgr/Talent/Talentspec.cpp | 4 ++-- src/Mgr/Travel/TravelMgr.cpp | 4 ++-- src/Mgr/Travel/TravelMgr.h | 4 ++-- src/Mgr/Travel/TravelNode.cpp | 4 ++-- 8 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/Ai/Base/Actions/ChangeTalentsAction.cpp b/src/Ai/Base/Actions/ChangeTalentsAction.cpp index 9b57a201a0..df09dadaca 100644 --- a/src/Ai/Base/Actions/ChangeTalentsAction.cpp +++ b/src/Ai/Base/Actions/ChangeTalentsAction.cpp @@ -184,7 +184,7 @@ std::string ChangeTalentsAction::SpecApply(std::string param) // // } // // } -// return std::move(ret); +// return ret; // } // std::vector ChangeTalentsAction::getPremadePaths(TalentSpec* oldSpec) @@ -201,7 +201,7 @@ std::string ChangeTalentsAction::SpecApply(std::string param) // // } // // } -// return std::move(ret); +// return ret; // } // TalentPath* ChangeTalentsAction::getPremadePath(uint32 id) diff --git a/src/Bot/Factory/RandomPlayerbotFactory.cpp b/src/Bot/Factory/RandomPlayerbotFactory.cpp index 0fd7e90e21..e2e6ffb11a 100644 --- a/src/Bot/Factory/RandomPlayerbotFactory.cpp +++ b/src/Bot/Factory/RandomPlayerbotFactory.cpp @@ -277,7 +277,7 @@ std::string const RandomPlayerbotFactory::CreateRandomBotName(NameRaceAndGender botName.clear(); continue; } - return std::move(botName); + return botName; } // TRUE RANDOM NAME GENERATION @@ -302,11 +302,11 @@ std::string const RandomPlayerbotFactory::CreateRandomBotName(NameRaceAndGender botName.clear(); continue; } - return std::move(botName); + return botName; } LOG_ERROR("playerbots", "Random name generation failed."); botName.clear(); - return std::move(botName); + return botName; } // Calculates the total number of required accounts, either using the specified randomBotAccountCount @@ -763,7 +763,7 @@ std::string const RandomPlayerbotFactory::CreateRandomGuildName() if (!result) { LOG_ERROR("playerbots", "No more names left for random guilds"); - return std::move(guildName); + return guildName; } Field* fields = result->Fetch(); @@ -777,13 +777,13 @@ std::string const RandomPlayerbotFactory::CreateRandomGuildName() if (!result) { LOG_ERROR("playerbots", "No more names left for random guilds"); - return std::move(guildName); + return guildName; } fields = result->Fetch(); guildName = fields[0].Get(); - return std::move(guildName); + return guildName; } void RandomPlayerbotFactory::CreateRandomArenaTeams(ArenaType type, uint32 count) @@ -905,7 +905,7 @@ std::string const RandomPlayerbotFactory::CreateRandomArenaTeamName() if (!result) { LOG_ERROR("playerbots", "No more names left for random arena teams"); - return std::move(arenaTeamName); + return arenaTeamName; } Field* fields = result->Fetch(); @@ -920,11 +920,11 @@ std::string const RandomPlayerbotFactory::CreateRandomArenaTeamName() if (!result) { LOG_ERROR("playerbots", "No more names left for random arena teams"); - return std::move(arenaTeamName); + return arenaTeamName; } fields = result->Fetch(); arenaTeamName = fields[0].Get(); - return std::move(arenaTeamName); + return arenaTeamName; } diff --git a/src/Bot/RandomPlayerbotMgr.cpp b/src/Bot/RandomPlayerbotMgr.cpp index c2b3fae3c2..fad757fa93 100644 --- a/src/Bot/RandomPlayerbotMgr.cpp +++ b/src/Bot/RandomPlayerbotMgr.cpp @@ -2680,7 +2680,7 @@ std::vector RandomPlayerbotMgr::GetBgBots(uint32 bracket) } while (result->NextRow()); } - return std::move(BgBots); + return BgBots; } CachedEvent* RandomPlayerbotMgr::FindEvent(uint32 bot, std::string const& event) diff --git a/src/Mgr/Item/RandomItemMgr.cpp b/src/Mgr/Item/RandomItemMgr.cpp index 1053e542e6..4abd2eaa9d 100644 --- a/src/Mgr/Item/RandomItemMgr.cpp +++ b/src/Mgr/Item/RandomItemMgr.cpp @@ -1700,7 +1700,7 @@ std::vector RandomItemMgr::GetQuestIdsForItem(uint32 itemId) } } - return std::move(questIds); + return questIds; } uint32 RandomItemMgr::GetUpgrade(Player* player, std::string spec, uint8 slot, uint32 quality, uint32 itemId) @@ -1827,7 +1827,7 @@ std::vector RandomItemMgr::GetUpgradeList(Player* player, std::string sp { std::vector listItems; if (!player) - return std::move(listItems); + return listItems; // get old item statWeight uint32 oldStatWeight = 0; @@ -1848,7 +1848,7 @@ std::vector RandomItemMgr::GetUpgradeList(Player* player, std::string sp } if (!specId) - return std::move(listItems); + return listItems; if (itemId && itemInfoCache.find(itemId) != itemInfoCache.end()) { @@ -1942,7 +1942,7 @@ std::vector RandomItemMgr::GetUpgradeList(Player* player, std::string sp LOG_INFO("playerbots", "New Items: {}, Old item:%d, New items max: {}", listItems.size(), oldStatWeight, closestUpgradeWeight); - return std::move(listItems); + return listItems; } bool RandomItemMgr::HasStatWeight(uint32 itemId) diff --git a/src/Mgr/Talent/Talentspec.cpp b/src/Mgr/Talent/Talentspec.cpp index 06e70b841c..df647baf10 100644 --- a/src/Mgr/Talent/Talentspec.cpp +++ b/src/Mgr/Talent/Talentspec.cpp @@ -317,7 +317,7 @@ std::vector TalentSpec::GetTalentTree(uint32 tabpag if (entry.tabPage() == tabpage) retList.push_back(entry); - return std::move(retList); + return retList; } uint32 TalentSpec::GetTalentPoints(int32 tabpage) { return GetTalentPoints(talents, tabpage); }; @@ -368,7 +368,7 @@ std::string const TalentSpec::GetTalentLink() if (treeLink[2] != "0") link = link + "-" + treeLink[2]; - return std::move(link); + return link; } uint32 TalentSpec::highestTree() diff --git a/src/Mgr/Travel/TravelMgr.cpp b/src/Mgr/Travel/TravelMgr.cpp index 748f929aa7..02454edc53 100644 --- a/src/Mgr/Travel/TravelMgr.cpp +++ b/src/Mgr/Travel/TravelMgr.cpp @@ -480,7 +480,7 @@ std::string const WorldPosition::getAreaName(bool fullName, bool zoneName) } } - return std::move(areaName); + return areaName; } std::set WorldPosition::getTransports(uint32 entry) @@ -4022,7 +4022,7 @@ std::vector TravelMgr::getRpgTravelDestinations(Player* bot, retTravelLocations.push_back(dest); } - return std::move(retTravelLocations); + return retTravelLocations; } std::vector TravelMgr::getExploreTravelDestinations(Player* bot, bool ignoreFull, diff --git a/src/Mgr/Travel/TravelMgr.h b/src/Mgr/Travel/TravelMgr.h index 251313736a..68542a7d07 100644 --- a/src/Mgr/Travel/TravelMgr.h +++ b/src/Mgr/Travel/TravelMgr.h @@ -440,7 +440,7 @@ std::vector> GetPosList(std::vector oList) for (auto& obj : oList) retList.push_back(std::make_pair(obj, WorldPosition(obj))); - return std::move(retList); + return retList; }; template @@ -450,7 +450,7 @@ std::vector> GetPosVector(std::vector oList) for (auto& obj : oList) retList.push_back(make_pair(obj, WorldPosition(obj))); - return std::move(retList); + return retList; }; class mapTransfer diff --git a/src/Mgr/Travel/TravelNode.cpp b/src/Mgr/Travel/TravelNode.cpp index 5d740075da..0cb88f9b11 100644 --- a/src/Mgr/Travel/TravelNode.cpp +++ b/src/Mgr/Travel/TravelNode.cpp @@ -364,7 +364,7 @@ std::vector TravelNode::getNodeMap(bool importantOnly, std::vector< } } - return std::move(closeList); + return closeList; } bool TravelNode::isUselessLink(TravelNode* farNode) @@ -1135,7 +1135,7 @@ std::vector TravelNodeMap::getNodes(WorldPosition pos, float range) [pos](TravelNode* i, TravelNode* j) { return i->getPosition()->distance(pos) < j->getPosition()->distance(pos); }); - return std::move(retVec); + return retVec; } TravelNode* TravelNodeMap::getNode(WorldPosition pos, [[maybe_unused]] std::vector& ppath, Unit* bot, From a0a50204eca3d8cd917d63828e32c3a3b82ec3ab Mon Sep 17 00:00:00 2001 From: privatecore Date: Fri, 13 Feb 2026 18:24:11 +0100 Subject: [PATCH 17/25] Fix action validation checks: isUseful -> isPossible + codestyle fixes and corrections (#2125) # Pull Request Fix the incorrect logic flaw when processing actions from different sources. It should be: `isUseful` -> `isPossible`. The original logic is based on the Mangosbot code and the impl presented inside `Engine::DoNextAction`. This should fix all wrong validation orders for triggers and direct/specific actions. Code style is based on the AzerothCore style guide + clang-format. --- ## 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? - - [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: - - [ ] Lightweight mode remains the default - - [ ] More complex behavior is optional and thereby configurable --- ## AI Assistance Was AI assistance (e.g. ChatGPT or similar tools) used while working on this change? - - [x] No - - [ ] Yes (**explain below**) If yes, please specify: - 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 - - [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. --- src/Ai/Base/Actions/CheckMountStateAction.cpp | 114 +++++++++--------- src/Ai/Base/Actions/ChooseTargetActions.cpp | 64 +++++----- src/Ai/Base/Actions/FishingAction.h | 17 ++- src/Ai/Base/Actions/GenericSpellActions.cpp | 55 +++++---- src/Ai/Base/Actions/GenericSpellActions.h | 2 +- src/Ai/Base/Actions/NonCombatActions.cpp | 29 ++--- src/Ai/Base/Actions/RpgAction.cpp | 2 +- src/Ai/Base/Actions/UseItemAction.cpp | 23 ++-- src/Ai/Base/Actions/UseItemAction.h | 6 +- .../Druid/Action/DruidShapeshiftActions.cpp | 26 ++-- .../Druid/Action/DruidShapeshiftActions.h | 7 +- src/Ai/Class/Priest/Action/PriestActions.cpp | 6 +- src/Ai/Class/Priest/Action/PriestActions.h | 23 +++- src/Ai/Class/Rogue/Action/RogueActions.h | 25 ++-- .../Class/Warrior/Action/WarriorActions.cpp | 32 ++--- src/Ai/Class/Warrior/Action/WarriorActions.h | 9 +- .../EyeOfEternity/Action/RaidEoEActions.h | 38 +++--- src/Bot/Engine/Action/Action.h | 21 +++- src/Bot/Engine/Engine.cpp | 8 +- 19 files changed, 266 insertions(+), 241 deletions(-) diff --git a/src/Ai/Base/Actions/CheckMountStateAction.cpp b/src/Ai/Base/Actions/CheckMountStateAction.cpp index 5ab7cc0f92..0d7fe43216 100644 --- a/src/Ai/Base/Actions/CheckMountStateAction.cpp +++ b/src/Ai/Base/Actions/CheckMountStateAction.cpp @@ -55,63 +55,6 @@ MountData CollectMountData(const Player* bot) return data; } -bool CheckMountStateAction::isUseful() -{ - // Not useful when: - if (botAI->IsInVehicle() || bot->isDead() || bot->HasUnitState(UNIT_STATE_IN_FLIGHT) || - !bot->IsOutdoors() || bot->InArena()) - return false; - - master = GetMaster(); - - // Get shapeshift states, only applicable when there's a master - if (master) - { - botInShapeshiftForm = bot->GetShapeshiftForm(); - masterInShapeshiftForm = master->GetShapeshiftForm(); - } - - // Not useful when in combat and not currently mounted / travel formed - if ((bot->IsInCombat() || botAI->GetState() == BOT_STATE_COMBAT) && - !bot->IsMounted() && botInShapeshiftForm != FORM_TRAVEL && botInShapeshiftForm != FORM_FLIGHT && botInShapeshiftForm != FORM_FLIGHT_EPIC) - return false; - - // In addition to checking IsOutdoors, also check whether bot is clipping below floor slightly because that will - // cause bot to falsly indicate they are outdoors. This fixes bug where bot tries to mount indoors (which seems - // to mostly be an issue in tunnels of WSG and AV) - float posZ = bot->GetPositionZ(); - float groundLevel = bot->GetMapWaterOrGroundLevel(bot->GetPositionX(), bot->GetPositionY(), posZ); - if (!bot->IsMounted() && !bot->HasWaterWalkAura() && posZ < groundLevel) - return false; - - // Not useful when bot does not have mount strat and is not currently mounted - if (!GET_PLAYERBOT_AI(bot)->HasStrategy("mount", BOT_STATE_NON_COMBAT) && !bot->IsMounted()) - return false; - - // Not useful when level lower than minimum required - if (bot->GetLevel() < sPlayerbotAIConfig.useGroundMountAtMinLevel) - return false; - - // Allow mounting while transformed only if the form allows it - if (bot->HasAuraType(SPELL_AURA_TRANSFORM) && bot->IsInDisallowedMountForm()) - return false; - - // BG Logic - if (bot->InBattleground()) - { - // Do not use when carrying BG Flags - if (bot->HasAura(BG_WS_SPELL_WARSONG_FLAG) || bot->HasAura(BG_WS_SPELL_SILVERWING_FLAG) || bot->HasAura(BG_EY_NETHERSTORM_FLAG_SPELL)) - return false; - - // Only mount if BG starts in less than 30 sec - if (Battleground* bg = bot->GetBattleground()) - if (bg->GetStatus() == STATUS_WAIT_JOIN && bg->GetStartDelayTime() > BG_START_DELAY_30S) - return false; - } - - return true; -} - bool CheckMountStateAction::Execute(Event /*event*/) { // Determine if there are no attackers @@ -182,6 +125,63 @@ bool CheckMountStateAction::Execute(Event /*event*/) return false; } +bool CheckMountStateAction::isUseful() +{ + // Not useful when: + if (botAI->IsInVehicle() || bot->isDead() || bot->HasUnitState(UNIT_STATE_IN_FLIGHT) || + !bot->IsOutdoors() || bot->InArena()) + return false; + + master = GetMaster(); + + // Get shapeshift states, only applicable when there's a master + if (master) + { + botInShapeshiftForm = bot->GetShapeshiftForm(); + masterInShapeshiftForm = master->GetShapeshiftForm(); + } + + // Not useful when in combat and not currently mounted / travel formed + if ((bot->IsInCombat() || botAI->GetState() == BOT_STATE_COMBAT) && + !bot->IsMounted() && botInShapeshiftForm != FORM_TRAVEL && botInShapeshiftForm != FORM_FLIGHT && botInShapeshiftForm != FORM_FLIGHT_EPIC) + return false; + + // In addition to checking IsOutdoors, also check whether bot is clipping below floor slightly because that will + // cause bot to falsly indicate they are outdoors. This fixes bug where bot tries to mount indoors (which seems + // to mostly be an issue in tunnels of WSG and AV) + float posZ = bot->GetPositionZ(); + float groundLevel = bot->GetMapWaterOrGroundLevel(bot->GetPositionX(), bot->GetPositionY(), posZ); + if (!bot->IsMounted() && !bot->HasWaterWalkAura() && posZ < groundLevel) + return false; + + // Not useful when bot does not have mount strat and is not currently mounted + if (!GET_PLAYERBOT_AI(bot)->HasStrategy("mount", BOT_STATE_NON_COMBAT) && !bot->IsMounted()) + return false; + + // Not useful when level lower than minimum required + if (bot->GetLevel() < sPlayerbotAIConfig.useGroundMountAtMinLevel) + return false; + + // Allow mounting while transformed only if the form allows it + if (bot->HasAuraType(SPELL_AURA_TRANSFORM) && bot->IsInDisallowedMountForm()) + return false; + + // BG Logic + if (bot->InBattleground()) + { + // Do not use when carrying BG Flags + if (bot->HasAura(BG_WS_SPELL_WARSONG_FLAG) || bot->HasAura(BG_WS_SPELL_SILVERWING_FLAG) || bot->HasAura(BG_EY_NETHERSTORM_FLAG_SPELL)) + return false; + + // Only mount if BG starts in less than 30 sec + if (Battleground* bg = bot->GetBattleground()) + if (bg->GetStatus() == STATUS_WAIT_JOIN && bg->GetStartDelayTime() > BG_START_DELAY_30S) + return false; + } + + return true; +} + bool CheckMountStateAction::Mount() { // Remove current Shapeshift if need be diff --git a/src/Ai/Base/Actions/ChooseTargetActions.cpp b/src/Ai/Base/Actions/ChooseTargetActions.cpp index 200094c900..3446c9b521 100644 --- a/src/Ai/Base/Actions/ChooseTargetActions.cpp +++ b/src/Ai/Base/Actions/ChooseTargetActions.cpp @@ -30,37 +30,6 @@ bool AttackEnemyFlagCarrierAction::isUseful() PlayerHasFlag::IsCapturingFlag(bot); } -bool AttackAnythingAction::isUseful() -{ - if (!bot || !botAI) // Prevents invalid accesses - return false; - - if (!botAI->AllowActivity(GRIND_ACTIVITY)) // Bot cannot be active - return false; - - if (botAI->HasStrategy("stay", BOT_STATE_NON_COMBAT)) - return false; - - if (bot->IsInCombat()) - return false; - - Unit* target = GetTarget(); - if (!target || !target->IsInWorld()) // Checks if the target is valid and in the world - return false; - - std::string const name = std::string(target->GetName()); - if (!name.empty() && - (name.find("Dummy") != std::string::npos || - name.find("Charge Target") != std::string::npos || - name.find("Melee Target") != std::string::npos || - name.find("Ranged Target") != std::string::npos)) - { - return false; - } - - return true; -} - bool DropTargetAction::Execute(Event event) { Unit* target = context->GetValue("current target")->Get(); @@ -127,7 +96,38 @@ bool AttackAnythingAction::Execute(Event event) return result; } -bool AttackAnythingAction::isPossible() { return AttackAction::isPossible() && GetTarget(); } +bool AttackAnythingAction::isUseful() +{ + if (!bot || !botAI) // Prevents invalid accesses + return false; + + if (!botAI->AllowActivity(GRIND_ACTIVITY)) // Bot cannot be active + return false; + + if (botAI->HasStrategy("stay", BOT_STATE_NON_COMBAT)) + return false; + + if (bot->IsInCombat()) + return false; + + Unit* target = GetTarget(); + if (!target || !target->IsInWorld()) // Checks if the target is valid and in the world + return false; + + std::string const name = std::string(target->GetName()); + if (!name.empty() && + (name.find("Dummy") != std::string::npos || + name.find("Charge Target") != std::string::npos || + name.find("Melee Target") != std::string::npos || + name.find("Ranged Target") != std::string::npos)) + { + return false; + } + + return true; +} + +bool AttackAnythingAction::isPossible() { return GetTarget() && AttackAction::isPossible(); } bool DpsAssistAction::isUseful() { diff --git a/src/Ai/Base/Actions/FishingAction.h b/src/Ai/Base/Actions/FishingAction.h index 407825ed05..35a7ab6fe6 100644 --- a/src/Ai/Base/Actions/FishingAction.h +++ b/src/Ai/Base/Actions/FishingAction.h @@ -7,22 +7,24 @@ #define _PLAYERBOT_FISHINGACTION_H #include "Action.h" -#include "MovementActions.h" #include "Event.h" +#include "MovementActions.h" #include "Playerbots.h" extern const uint32 FISHING_SPELL; extern const uint32 FISHING_POLE; extern const uint32 FISHING_BOBBER; -WorldPosition FindWaterRadial(Player* bot, float x, float y, float z, Map* map, uint32 phaseMask, float minDistance, float maxDistance, float increment, bool checkLOS=false, int numDirections = 16); +WorldPosition FindWaterRadial(Player* bot, float x, float y, float z, Map* map, uint32 phaseMask, float minDistance, + float maxDistance, float increment, bool checkLOS = false, int numDirections = 16); class PlayerbotAI; class FishingAction : public Action { public: - FishingAction(PlayerbotAI* botAI) : Action(botAI, "go fishing"){} + FishingAction(PlayerbotAI* botAI) : Action(botAI, "go fishing") {} + bool Execute(Event event) override; bool isUseful() override; }; @@ -31,8 +33,10 @@ class EquipFishingPoleAction : public Action { public: EquipFishingPoleAction(PlayerbotAI* botAI) : Action(botAI, "equip fishing pole") {} + bool Execute(Event event) override; bool isUseful() override; + private: Item* _pole = nullptr; }; @@ -40,7 +44,8 @@ class EquipFishingPoleAction : public Action class MoveNearWaterAction : public MovementAction { public: - MoveNearWaterAction(PlayerbotAI* botAI): MovementAction(botAI, "move near water") {} + MoveNearWaterAction(PlayerbotAI* botAI) : MovementAction(botAI, "move near water") {} + bool Execute(Event event) override; bool isUseful() override; bool isPossible() override; @@ -50,6 +55,7 @@ class UseBobberAction : public Action { public: UseBobberAction(PlayerbotAI* botAI) : Action(botAI, "use fishing bobber") {} + bool Execute(Event event) override; bool isUseful() override; }; @@ -58,6 +64,7 @@ class EndMasterFishingAction : public Action { public: EndMasterFishingAction(PlayerbotAI* botAI) : Action(botAI, "end master fishing") {} + bool Execute(Event event) override; bool isUseful() override; }; @@ -66,6 +73,8 @@ class RemoveBobberStrategyAction : public Action { public: RemoveBobberStrategyAction(PlayerbotAI* botAI) : Action(botAI, "remove bobber strategy") {} + bool Execute(Event event) override; }; + #endif diff --git a/src/Ai/Base/Actions/GenericSpellActions.cpp b/src/Ai/Base/Actions/GenericSpellActions.cpp index 819816f948..02d1decc69 100644 --- a/src/Ai/Base/Actions/GenericSpellActions.cpp +++ b/src/Ai/Base/Actions/GenericSpellActions.cpp @@ -78,62 +78,61 @@ bool CastSpellAction::Execute(Event event) return botAI->CastSpell(spell, GetTarget()); } -bool CastSpellAction::isPossible() +bool CastSpellAction::isUseful() { if (botAI->IsInVehicle() && !botAI->IsInVehicle(false, false, true)) - { - if (!sPlayerbotAIConfig.logInGroupOnly || (bot->GetGroup() && botAI->HasRealPlayerMaster())) - { - LOG_DEBUG("playerbots", "Can cast spell failed. Vehicle. - bot name: {}", bot->GetName()); - } return false; - } if (spell == "mount" && !bot->IsMounted() && !bot->IsInCombat()) return true; if (spell == "mount" && bot->IsInCombat()) { - if (!sPlayerbotAIConfig.logInGroupOnly || (bot->GetGroup() && botAI->HasRealPlayerMaster())) - { - LOG_DEBUG("playerbots", "Can cast spell failed. Mount. - bot name: {}", bot->GetName()); - } bot->Dismount(); return false; } - // Spell* currentSpell = bot->GetCurrentSpell(CURRENT_GENERIC_SPELL); //not used, line marked for removal. - return botAI->CanCastSpell(spell, GetTarget()); + Unit* spellTarget = GetTarget(); + if (!spellTarget) + return false; + + if (!spellTarget->IsInWorld() || spellTarget->GetMapId() != bot->GetMapId()) + return false; + + // float combatReach = bot->GetCombatReach() + target->GetCombatReach(); + // if (!botAI->IsRanged(bot)) + // combatReach += 4.0f / 3.0f; + + return AI_VALUE2(bool, "spell cast useful", spell); + // && ServerFacade::instance().GetDistance2d(bot, target) <= (range + combatReach); } -bool CastSpellAction::isUseful() +bool CastSpellAction::isPossible() { if (botAI->IsInVehicle() && !botAI->IsInVehicle(false, false, true)) + { + if (!sPlayerbotAIConfig.logInGroupOnly || (bot->GetGroup() && botAI->HasRealPlayerMaster())) + { + LOG_DEBUG("playerbots", "Can cast spell failed. Vehicle. - bot name: {}", bot->GetName()); + } return false; + } if (spell == "mount" && !bot->IsMounted() && !bot->IsInCombat()) return true; if (spell == "mount" && bot->IsInCombat()) { + if (!sPlayerbotAIConfig.logInGroupOnly || (bot->GetGroup() && botAI->HasRealPlayerMaster())) + { + LOG_DEBUG("playerbots", "Can cast spell failed. Mount. - bot name: {}", bot->GetName()); + } bot->Dismount(); return false; } - Unit* spellTarget = GetTarget(); - if (!spellTarget) - return false; - - if (!spellTarget->IsInWorld() || spellTarget->GetMapId() != bot->GetMapId()) - return false; - - // float combatReach = bot->GetCombatReach() + spellTarget->GetCombatReach(); - // if (!botAI->IsRanged(bot)) - // combatReach += 4.0f / 3.0f; - - return spellTarget && - AI_VALUE2(bool, "spell cast useful", - spell); // && ServerFacade::instance().GetDistance2d(bot, spellTarget) <= (range + combatReach); + // Spell* currentSpell = bot->GetCurrentSpell(CURRENT_GENERIC_SPELL); //not used, line marked for removal. + return botAI->CanCastSpell(spell, GetTarget()); } CastMeleeSpellAction::CastMeleeSpellAction(PlayerbotAI* botAI, std::string const spell) : CastSpellAction(botAI, spell) diff --git a/src/Ai/Base/Actions/GenericSpellActions.h b/src/Ai/Base/Actions/GenericSpellActions.h index b148b93ff6..9aa83f62d6 100644 --- a/src/Ai/Base/Actions/GenericSpellActions.h +++ b/src/Ai/Base/Actions/GenericSpellActions.h @@ -23,8 +23,8 @@ class CastSpellAction : public Action std::string const GetTargetName() override { return "current target"; }; bool Execute(Event event) override; - bool isPossible() override; bool isUseful() override; + bool isPossible() override; ActionThreatType getThreatType() override { return ActionThreatType::Single; } std::vector getPrerequisites() override diff --git a/src/Ai/Base/Actions/NonCombatActions.cpp b/src/Ai/Base/Actions/NonCombatActions.cpp index 492da8ae6b..9de1e0bae6 100644 --- a/src/Ai/Base/Actions/NonCombatActions.cpp +++ b/src/Ai/Base/Actions/NonCombatActions.cpp @@ -49,18 +49,16 @@ bool DrinkAction::Execute(Event event) bool DrinkAction::isUseful() { - return UseItemAction::isUseful() && - AI_VALUE2(bool, "has mana", "self target") && - AI_VALUE2(uint8, "mana", "self target") < 100; + return UseItemAction::isUseful() && AI_VALUE2(bool, "has mana", "self target") && + AI_VALUE2(uint8, "mana", "self target") < 100; } bool DrinkAction::isPossible() { - return !bot->IsInCombat() && - !bot->IsMounted() && - !botAI->HasAnyAuraOf(GetTarget(), "dire bear form", "bear form", "cat form", "travel form", - "aquatic form","flight form", "swift flight form", nullptr) && - (botAI->HasCheat(BotCheatMask::food) || UseItemAction::isPossible()); + return !bot->IsInCombat() && !bot->IsMounted() && + !botAI->HasAnyAuraOf(GetTarget(), "dire bear form", "bear form", "cat form", "travel form", "aquatic form", + "flight form", "swift flight form", nullptr) && + (botAI->HasCheat(BotCheatMask::food) || UseItemAction::isPossible()); } bool EatAction::Execute(Event event) @@ -102,17 +100,12 @@ bool EatAction::Execute(Event event) return UseItemAction::Execute(event); } -bool EatAction::isUseful() -{ - return UseItemAction::isUseful() && - AI_VALUE2(uint8, "health", "self target") < 100; -} +bool EatAction::isUseful() { return UseItemAction::isUseful() && AI_VALUE2(uint8, "health", "self target") < 100; } bool EatAction::isPossible() { - return !bot->IsInCombat() && - !bot->IsMounted() && - !botAI->HasAnyAuraOf(GetTarget(), "dire bear form", "bear form", "cat form", "travel form", - "aquatic form","flight form", "swift flight form", nullptr) && - (botAI->HasCheat(BotCheatMask::food) || UseItemAction::isPossible()); + return !bot->IsInCombat() && !bot->IsMounted() && + !botAI->HasAnyAuraOf(GetTarget(), "dire bear form", "bear form", "cat form", "travel form", "aquatic form", + "flight form", "swift flight form", nullptr) && + (botAI->HasCheat(BotCheatMask::food) || UseItemAction::isPossible()); } diff --git a/src/Ai/Base/Actions/RpgAction.cpp b/src/Ai/Base/Actions/RpgAction.cpp index 919e25c58e..1e461b068e 100644 --- a/src/Ai/Base/Actions/RpgAction.cpp +++ b/src/Ai/Base/Actions/RpgAction.cpp @@ -85,7 +85,7 @@ bool RpgAction::SetNextRpgAction() isChecked = true; Action* action = botAI->GetAiObjectContext()->GetAction(nextAction.getName()); - if (!dynamic_cast(action) || !action->isPossible() || !action->isUseful()) + if (!dynamic_cast(action) || !action->isUseful() || !action->isPossible()) continue; actions.push_back(action); diff --git a/src/Ai/Base/Actions/UseItemAction.cpp b/src/Ai/Base/Actions/UseItemAction.cpp index 473816e3ec..77c865dba8 100644 --- a/src/Ai/Base/Actions/UseItemAction.cpp +++ b/src/Ai/Base/Actions/UseItemAction.cpp @@ -7,9 +7,9 @@ #include "ChatHelper.h" #include "Event.h" +#include "ItemPackets.h" #include "ItemUsageValue.h" #include "Playerbots.h" -#include "ItemPackets.h" bool UseItemAction::Execute(Event event) { @@ -416,13 +416,6 @@ bool UseHearthStone::Execute(Event event) bool UseHearthStone::isUseful() { return !bot->InBattleground(); } -bool UseRandomRecipe::isUseful() -{ - return !bot->IsInCombat() && !botAI->HasActivePlayerMaster() && !bot->InBattleground(); -} - -bool UseRandomRecipe::isPossible() { return AI_VALUE2(uint32, "item count", "recipe") > 0; } - bool UseRandomRecipe::Execute(Event event) { std::vector recipes = AI_VALUE2(std::vector, "inventory items", "recipe"); @@ -445,12 +438,12 @@ bool UseRandomRecipe::Execute(Event event) return used; } -bool UseRandomQuestItem::isUseful() +bool UseRandomRecipe::isUseful() { - return !botAI->HasActivePlayerMaster() && !bot->InBattleground() && !bot->HasUnitState(UNIT_STATE_IN_FLIGHT); + return !bot->IsInCombat() && !botAI->HasActivePlayerMaster() && !bot->InBattleground(); } -bool UseRandomQuestItem::isPossible() { return AI_VALUE2(uint32, "item count", "quest") > 0; } +bool UseRandomRecipe::isPossible() { return AI_VALUE2(uint32, "item count", "recipe") > 0; } bool UseRandomQuestItem::Execute(Event event) { @@ -478,7 +471,6 @@ bool UseRandomQuestItem::Execute(Event event) break; } } - } if (!item) @@ -490,3 +482,10 @@ bool UseRandomQuestItem::Execute(Event event) return used; } + +bool UseRandomQuestItem::isUseful() +{ + return !botAI->HasActivePlayerMaster() && !bot->InBattleground() && !bot->HasUnitState(UNIT_STATE_IN_FLIGHT); +} + +bool UseRandomQuestItem::isPossible() { return AI_VALUE2(uint32, "item count", "quest") > 0; } diff --git a/src/Ai/Base/Actions/UseItemAction.h b/src/Ai/Base/Actions/UseItemAction.h index 2b0c7e1911..263bc29dc4 100644 --- a/src/Ai/Base/Actions/UseItemAction.h +++ b/src/Ai/Base/Actions/UseItemAction.h @@ -69,8 +69,8 @@ class UseHearthStone : public UseItemAction public: UseHearthStone(PlayerbotAI* botAI) : UseItemAction(botAI, "hearthstone", true) {} - bool isUseful() override; bool Execute(Event event) override; + bool isUseful() override; }; class UseRandomRecipe : public UseItemAction @@ -78,9 +78,9 @@ class UseRandomRecipe : public UseItemAction public: UseRandomRecipe(PlayerbotAI* botAI) : UseItemAction(botAI, "random recipe", true) {} + bool Execute(Event event) override; bool isUseful() override; bool isPossible() override; - bool Execute(Event event) override; }; class UseRandomQuestItem : public UseItemAction @@ -88,9 +88,9 @@ class UseRandomQuestItem : public UseItemAction public: UseRandomQuestItem(PlayerbotAI* botAI) : UseItemAction(botAI, "random quest item", true) {} + bool Execute(Event event) override; bool isUseful() override; bool isPossible() override; - bool Execute(Event event) override; }; #endif diff --git a/src/Ai/Class/Druid/Action/DruidShapeshiftActions.cpp b/src/Ai/Class/Druid/Action/DruidShapeshiftActions.cpp index 1f066dc349..42e639f93b 100644 --- a/src/Ai/Class/Druid/Action/DruidShapeshiftActions.cpp +++ b/src/Ai/Class/Druid/Action/DruidShapeshiftActions.cpp @@ -7,20 +7,19 @@ #include "Playerbots.h" -bool CastBearFormAction::isPossible() +bool CastBearFormAction::isUseful() { - return CastBuffSpellAction::isPossible() && !botAI->HasAura("dire bear form", GetTarget()); + return CastBuffSpellAction::isUseful() && !botAI->HasAura("dire bear form", GetTarget()); } -bool CastBearFormAction::isUseful() +bool CastBearFormAction::isPossible() { - return CastBuffSpellAction::isUseful() && !botAI->HasAura("dire bear form", GetTarget()); + return CastBuffSpellAction::isPossible() && !botAI->HasAura("dire bear form", GetTarget()); } std::vector CastDireBearFormAction::getAlternatives() { - return NextAction::merge({ NextAction("bear form") }, - CastSpellAction::getAlternatives()); + return NextAction::merge({NextAction("bear form")}, CastSpellAction::getAlternatives()); } bool CastTravelFormAction::isUseful() @@ -32,22 +31,17 @@ bool CastTravelFormAction::isUseful() !botAI->HasAura("dash", bot); } -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; -} - bool CastCasterFormAction::Execute(Event event) { botAI->RemoveShapeshift(); return true; } -bool CastCancelTreeFormAction::isUseful() +bool CastCasterFormAction::isUseful() { - return botAI->HasAura(33891, bot); + 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; } bool CastCancelTreeFormAction::Execute(Event event) @@ -56,6 +50,8 @@ bool CastCancelTreeFormAction::Execute(Event event) return true; } +bool CastCancelTreeFormAction::isUseful() { return botAI->HasAura(33891, bot); } + bool CastTreeFormAction::isUseful() { return GetTarget() && CastSpellAction::isUseful() && !botAI->HasAura(33891, bot); diff --git a/src/Ai/Class/Druid/Action/DruidShapeshiftActions.h b/src/Ai/Class/Druid/Action/DruidShapeshiftActions.h index 26e14c42ca..9d75f26820 100644 --- a/src/Ai/Class/Druid/Action/DruidShapeshiftActions.h +++ b/src/Ai/Class/Druid/Action/DruidShapeshiftActions.h @@ -15,8 +15,8 @@ class CastBearFormAction : public CastBuffSpellAction public: CastBearFormAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "bear form") {} - bool isPossible() override; bool isUseful() override; + bool isPossible() override; }; class CastDireBearFormAction : public CastBuffSpellAction @@ -37,6 +37,7 @@ class CastTreeFormAction : public CastBuffSpellAction { public: CastTreeFormAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "tree of life") {} + bool isUseful() override; }; @@ -65,9 +66,9 @@ class CastCasterFormAction : public CastBuffSpellAction public: CastCasterFormAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "caster form") {} + bool Execute(Event event) override; bool isUseful() override; bool isPossible() override { return true; } - bool Execute(Event event) override; }; class CastCancelTreeFormAction : public CastBuffSpellAction @@ -75,9 +76,9 @@ class CastCancelTreeFormAction : public CastBuffSpellAction public: CastCancelTreeFormAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "cancel tree form") {} + bool Execute(Event event) override; bool isUseful() override; bool isPossible() override { return true; } - bool Execute(Event event) override; }; #endif diff --git a/src/Ai/Class/Priest/Action/PriestActions.cpp b/src/Ai/Class/Priest/Action/PriestActions.cpp index ae55b104dc..bdc33ac340 100644 --- a/src/Ai/Class/Priest/Action/PriestActions.cpp +++ b/src/Ai/Class/Priest/Action/PriestActions.cpp @@ -8,16 +8,14 @@ #include "Event.h" #include "Playerbots.h" -bool CastRemoveShadowformAction::isUseful() { return botAI->HasAura("shadowform", AI_VALUE(Unit*, "self target")); } - -bool CastRemoveShadowformAction::isPossible() { return true; } - bool CastRemoveShadowformAction::Execute(Event event) { botAI->RemoveAura("shadowform"); return true; } +bool CastRemoveShadowformAction::isUseful() { return botAI->HasAura("shadowform", AI_VALUE(Unit*, "self target")); } + Unit* CastPowerWordShieldOnAlmostFullHealthBelowAction::GetTarget() { Group* group = bot->GetGroup(); diff --git a/src/Ai/Class/Priest/Action/PriestActions.h b/src/Ai/Class/Priest/Action/PriestActions.h index 1b09414d48..4e94f27cca 100644 --- a/src/Ai/Class/Priest/Action/PriestActions.h +++ b/src/Ai/Class/Priest/Action/PriestActions.h @@ -56,7 +56,10 @@ HEAL_PARTY_ACTION(CastRenewOnPartyAction, "renew", 15.0f, HealingManaEfficiency: class CastPrayerOfMendingAction : public HealPartyMemberAction { public: - CastPrayerOfMendingAction(PlayerbotAI* botAI) : HealPartyMemberAction(botAI, "prayer of mending", 10.0f, HealingManaEfficiency::HIGH, false) {} + CastPrayerOfMendingAction(PlayerbotAI* botAI) + : HealPartyMemberAction(botAI, "prayer of mending", 10.0f, HealingManaEfficiency::HIGH, false) + { + } }; HEAL_PARTY_ACTION(CastBindingHealAction, "binding heal", 15.0f, HealingManaEfficiency::MEDIUM); @@ -65,7 +68,8 @@ HEAL_PARTY_ACTION(CastPrayerOfHealingAction, "prayer of healing", 15.0f, Healing class CastCircleOfHealingAction : public HealPartyMemberAction { public: - CastCircleOfHealingAction(PlayerbotAI* ai) : HealPartyMemberAction(ai, "circle of healing", 15.0f, HealingManaEfficiency::HIGH) + CastCircleOfHealingAction(PlayerbotAI* ai) + : HealPartyMemberAction(ai, "circle of healing", 15.0f, HealingManaEfficiency::HIGH) { } }; @@ -134,15 +138,15 @@ class CastRemoveShadowformAction : public Action public: CastRemoveShadowformAction(PlayerbotAI* botAI) : Action(botAI, "remove shadowform") {} - bool isUseful() override; - bool isPossible() override; bool Execute(Event event) override; + bool isUseful() override; }; class CastDispersionAction : public CastSpellAction { public: CastDispersionAction(PlayerbotAI* ai) : CastSpellAction(ai, "dispersion") {} + virtual std::string const GetTargetName() { return "self target"; } }; @@ -158,6 +162,7 @@ class CastHymnOfHopeAction : public CastSpellAction { public: CastHymnOfHopeAction(PlayerbotAI* ai) : CastSpellAction(ai, "hymn of hope") {} + virtual std::string const GetTargetName() { return "self target"; } }; @@ -165,6 +170,7 @@ class CastDivineHymnAction : public CastSpellAction { public: CastDivineHymnAction(PlayerbotAI* ai) : CastSpellAction(ai, "divine hymn") {} + virtual std::string const GetTargetName() { return "self target"; } }; @@ -172,6 +178,7 @@ class CastShadowfiendAction : public CastSpellAction { public: CastShadowfiendAction(PlayerbotAI* ai) : CastSpellAction(ai, "shadowfiend") {} + virtual std::string const GetTargetName() { return "current target"; } }; @@ -182,6 +189,7 @@ class CastPowerWordShieldOnAlmostFullHealthBelowAction : public HealPartyMemberA : HealPartyMemberAction(ai, "power word: shield", 15.0f, HealingManaEfficiency::HIGH) { } + bool isUseful() override; Unit* GetTarget() override; }; @@ -193,6 +201,7 @@ class CastPowerWordShieldOnNotFullAction : public HealPartyMemberAction : HealPartyMemberAction(ai, "power word: shield", 5.0f, HealingManaEfficiency::HIGH) { } + bool isUseful() override; Unit* GetTarget() override; }; @@ -201,13 +210,17 @@ class CastMindSearAction : public CastSpellAction { public: CastMindSearAction(PlayerbotAI* ai) : CastSpellAction(ai, "mind sear") {} + ActionThreatType getThreatType() override { return ActionThreatType::Aoe; } }; class CastGuardianSpiritOnPartyAction : public HealPartyMemberAction { public: - CastGuardianSpiritOnPartyAction(PlayerbotAI* ai) : HealPartyMemberAction(ai, "guardian spirit", 40.0f, HealingManaEfficiency::MEDIUM) {} + CastGuardianSpiritOnPartyAction(PlayerbotAI* ai) + : HealPartyMemberAction(ai, "guardian spirit", 40.0f, HealingManaEfficiency::MEDIUM) + { + } }; #endif diff --git a/src/Ai/Class/Rogue/Action/RogueActions.h b/src/Ai/Class/Rogue/Action/RogueActions.h index c31dfbd7e6..dd0ad4735e 100644 --- a/src/Ai/Class/Rogue/Action/RogueActions.h +++ b/src/Ai/Class/Rogue/Action/RogueActions.h @@ -27,6 +27,7 @@ class CastHungerForBloodAction : public CastBuffSpellAction { public: CastHungerForBloodAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "hunger for blood") {} + std::string const GetTargetName() override { return "current target"; } }; @@ -43,9 +44,9 @@ class CastStealthAction : public CastBuffSpellAction public: CastStealthAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "stealth") {} - std::string const GetTargetName() override { return "self target"; } bool isUseful() override; bool isPossible() override; + std::string const GetTargetName() override { return "self target"; } }; class UnstealthAction : public Action @@ -61,8 +62,8 @@ class CheckStealthAction : public Action public: CheckStealthAction(PlayerbotAI* botAI) : Action(botAI, "check stealth") {} - bool isPossible() override { return true; } bool Execute(Event event) override; + bool isPossible() override { return true; } }; class CastKickAction : public CastSpellAction @@ -131,6 +132,7 @@ class CastEnvenomAction : public CastMeleeSpellAction { public: CastEnvenomAction(PlayerbotAI* ai) : CastMeleeSpellAction(ai, "envenom") {} + bool isUseful() override; bool isPossible() override; }; @@ -139,37 +141,42 @@ class CastTricksOfTheTradeOnMainTankAction : public BuffOnMainTankAction { public: CastTricksOfTheTradeOnMainTankAction(PlayerbotAI* ai) : BuffOnMainTankAction(ai, "tricks of the trade", true) {} - virtual bool isUseful() override; + + bool isUseful() override; }; class UseDeadlyPoisonAction : public UseItemAction { public: UseDeadlyPoisonAction(PlayerbotAI* ai) : UseItemAction(ai, "Deadly Poison") {} - virtual bool Execute(Event event) override; - virtual bool isPossible() override; + + bool Execute(Event event) override; + bool isPossible() override; }; class UseInstantPoisonAction : public UseItemAction { public: UseInstantPoisonAction(PlayerbotAI* ai) : UseItemAction(ai, "Instant Poison") {} - virtual bool Execute(Event event) override; - virtual bool isPossible() override; + + bool Execute(Event event) override; + bool isPossible() override; }; class UseInstantPoisonOffHandAction : public UseItemAction { public: UseInstantPoisonOffHandAction(PlayerbotAI* ai) : UseItemAction(ai, "Instant Poison Off Hand") {} - virtual bool Execute(Event event) override; - virtual bool isPossible() override; + + bool Execute(Event event) override; + bool isPossible() override; }; class FanOfKnivesAction : public CastMeleeSpellAction { public: FanOfKnivesAction(PlayerbotAI* ai) : CastMeleeSpellAction(ai, "fan of knives") {} + ActionThreatType getThreatType() override { return ActionThreatType::Aoe; } }; diff --git a/src/Ai/Class/Warrior/Action/WarriorActions.cpp b/src/Ai/Class/Warrior/Action/WarriorActions.cpp index 9733226a95..0bde24cd94 100644 --- a/src/Ai/Class/Warrior/Action/WarriorActions.cpp +++ b/src/Ai/Class/Warrior/Action/WarriorActions.cpp @@ -176,20 +176,19 @@ Unit* CastShatteringThrowAction::GetTarget() return nullptr; // No valid target } -bool CastShatteringThrowAction::isUseful() +bool CastShatteringThrowAction::Execute(Event event) { - - // Spell cooldown check - if (!bot->HasSpell(64382)) - { + Unit* target = GetTarget(); + if (!target) return false; - } - // Spell cooldown check - if (bot->HasSpellCooldown(64382)) - { + return botAI->CastSpell("shattering throw", target); +} + +bool CastShatteringThrowAction::isUseful() +{ + if (!bot->HasSpell(64382) || bot->HasSpellCooldown(64382)) return false; - } GuidVector enemies = AI_VALUE(GuidVector, "possible targets"); @@ -220,25 +219,12 @@ bool CastShatteringThrowAction::isPossible() // Range check: Shattering Throw is 30 yards if (!bot->IsWithinDistInMap(target, 30.0f)) - { return false; - } // Check line of sight if (!bot->IsWithinLOSInMap(target)) - { return false; - } // If the minimal checks above pass, simply return true. return true; } - -bool CastShatteringThrowAction::Execute(Event event) -{ - Unit* target = GetTarget(); - if (!target) - return false; - - return botAI->CastSpell("shattering throw", target); -} diff --git a/src/Ai/Class/Warrior/Action/WarriorActions.h b/src/Ai/Class/Warrior/Action/WarriorActions.h index ea72fb269a..7910fc0d8e 100644 --- a/src/Ai/Class/Warrior/Action/WarriorActions.h +++ b/src/Ai/Class/Warrior/Action/WarriorActions.h @@ -25,8 +25,7 @@ MELEE_ACTION_U(CastBattleShoutTauntAction, "battle shout", CastSpellAction::isUs class CastDemoralizingShoutAction : public CastMeleeDebuffSpellAction { public: - CastDemoralizingShoutAction(PlayerbotAI* botAI) - : CastMeleeDebuffSpellAction(botAI, "demoralizing shout") {} + CastDemoralizingShoutAction(PlayerbotAI* botAI) : CastMeleeDebuffSpellAction(botAI, "demoralizing shout") {} }; class CastDemoralizingShoutWithoutLifeTimeCheckAction : public CastMeleeDebuffSpellAction @@ -140,8 +139,8 @@ class CastVigilanceAction : public BuffOnPartyAction public: CastVigilanceAction(PlayerbotAI* botAI) : BuffOnPartyAction(botAI, "vigilance") {} - Unit* GetTarget() override; bool Execute(Event event) override; + Unit* GetTarget() override; }; class CastRetaliationAction : public CastBuffSpellAction @@ -157,10 +156,10 @@ class CastShatteringThrowAction : public CastSpellAction public: CastShatteringThrowAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "shattering throw") {} - Unit* GetTarget() override; + bool Execute(Event event) override; bool isUseful() override; bool isPossible() override; - bool Execute(Event event) override; + Unit* GetTarget() override; }; #endif diff --git a/src/Ai/Raid/EyeOfEternity/Action/RaidEoEActions.h b/src/Ai/Raid/EyeOfEternity/Action/RaidEoEActions.h index c6fe064c05..0d5b6fcc36 100644 --- a/src/Ai/Raid/EyeOfEternity/Action/RaidEoEActions.h +++ b/src/Ai/Raid/EyeOfEternity/Action/RaidEoEActions.h @@ -1,9 +1,9 @@ #ifndef _PLAYERBOT_RAIDEOEACTIONS_H #define _PLAYERBOT_RAIDEOEACTIONS_H -#include "MovementActions.h" #include "AttackAction.h" #include "GenericSpellActions.h" +#include "MovementActions.h" #include "PlayerbotAI.h" #include "Playerbots.h" @@ -13,34 +13,38 @@ const std::pair MALYGOS_STACK_POSITION = {755.0f, 1301.0f}; class MalygosPositionAction : public MovementAction { public: - MalygosPositionAction(PlayerbotAI* botAI, std::string const name = "malygos position") - : MovementAction(botAI, name) {} + MalygosPositionAction(PlayerbotAI* botAI, std::string const name = "malygos position") : MovementAction(botAI, name) + { + } + bool Execute(Event event) override; }; class MalygosTargetAction : public AttackAction { public: - MalygosTargetAction(PlayerbotAI* botAI, std::string const name = "malygos target") - : AttackAction(botAI, name) {} - bool Execute(Event event) override; -}; + MalygosTargetAction(PlayerbotAI* botAI, std::string const name = "malygos target") : AttackAction(botAI, name) {} -class PullPowerSparkAction : public CastSpellAction -{ -public: - PullPowerSparkAction(PlayerbotAI* botAI, std::string const name = "pull power spark") - : CastSpellAction(botAI, name) {} bool Execute(Event event) override; - bool isPossible() override; - bool isUseful() override; }; +//class PullPowerSparkAction : public CastSpellAction +//{ +//public: +// PullPowerSparkAction(PlayerbotAI* botAI, std::string const name = "pull power spark") : CastSpellAction(botAI, name) +// { +// } + +// bool Execute(Event event) override; +// bool isUseful() override; +// bool isPossible() override; +//}; + class KillPowerSparkAction : public AttackAction { public: - KillPowerSparkAction(PlayerbotAI* botAI, std::string const name = "kill power spark") - : AttackAction(botAI, name) {} + KillPowerSparkAction(PlayerbotAI* botAI, std::string const name = "kill power spark") : AttackAction(botAI, name) {} + bool Execute(Event event) override; }; @@ -48,6 +52,7 @@ class EoEFlyDrakeAction : public MovementAction { public: EoEFlyDrakeAction(PlayerbotAI* ai) : MovementAction(ai, "eoe fly drake") {} + bool Execute(Event event) override; bool isPossible() override; }; @@ -56,6 +61,7 @@ class EoEDrakeAttackAction : public Action { public: EoEDrakeAttackAction(PlayerbotAI* botAI) : Action(botAI, "eoe drake attack") {} + bool Execute(Event event) override; bool isPossible() override; diff --git a/src/Bot/Engine/Action/Action.h b/src/Bot/Engine/Action/Action.h index 2395c5ea87..6c54d24b97 100644 --- a/src/Bot/Engine/Action/Action.h +++ b/src/Bot/Engine/Action/Action.h @@ -60,8 +60,27 @@ class Action : public AiNamedObject virtual ~Action(void) {} virtual bool Execute([[maybe_unused]] Event event) { return true; } - virtual bool isPossible() { return true; } + + /** + * @brief First validation check - determines if this action is contextually useful + * + * Performs lightweight checks to evaluate whether the action makes sense + * in the current situation. Called before isPossible() during action selection. + * + * @return true if the action is useful, false otherwise + */ virtual bool isUseful() { return true; } + + /** + * @brief Second validation check - determines if this action can be executed + * + * Performs hard pre-execution validation against the event and game state. + * Called after isUseful() passes, before Execute(). + * + * @return true if the action is possible, false otherwise + */ + virtual bool isPossible() { return true; } + virtual std::vector getPrerequisites() { return {}; } virtual std::vector getAlternatives() { return {}; } virtual std::vector getContinuers() { return {}; } diff --git a/src/Bot/Engine/Engine.cpp b/src/Bot/Engine/Engine.cpp index bc24baf560..bb4f2eb351 100644 --- a/src/Bot/Engine/Engine.cpp +++ b/src/Bot/Engine/Engine.cpp @@ -323,16 +323,16 @@ ActionResult Engine::ExecuteAction(std::string const name, Event event, std::str q->Qualify(qualifier); } - if (!action->isPossible()) + if (!action->isUseful()) { delete actionNode; - return ACTION_RESULT_IMPOSSIBLE; + return ACTION_RESULT_USELESS; } - if (!action->isUseful()) + if (!action->isPossible()) { delete actionNode; - return ACTION_RESULT_USELESS; + return ACTION_RESULT_IMPOSSIBLE; } action->MakeVerbose(); From 17b8d7f68b4e7fe9626eebcd3ea319419b4e0f8e Mon Sep 17 00:00:00 2001 From: Alex Dcnh <140754794+Wishmaster117@users.noreply.github.com> Date: Fri, 13 Feb 2026 18:24:42 +0100 Subject: [PATCH 18/25] Stage1 refactor world position method names (#2126) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Pull Request This change replaces the non‑standard WorldPosition::getX/getY/getZ/getO/getMapId wrappers with the core getters (GetPositionX/Y/Z, GetOrientation, GetMapId) and removes the redundant wrappers. Goal: align the module with AzerothCore conventions, reduce local adapters, and improve long‑term maintainability. --- ## Design Philosophy This is a structural cleanup only (coordinate access) and does not alter any AI behavior or decision logic. It follows the stability/performance-first philosophy and does not add branches or extra runtime work. Before submitting: yes, this change aligns with the principles of stability, performance, and predictability. 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: - Minimum logic required: use core getters (GetPositionX/Y/Z, GetMapId, GetOrientation) wherever coordinates are needed. - Cheapest implementation: direct call replacement and removal of redundant wrappers. - Runtime cost: negligible (same data access, no additional logic). --- ## How to Test the Changes - No functional testing required (behavior‑neutral refactor). - Recommended: compile the module and run a normal server startup as validation. ## Complexity & Impact Does this change add new decision branches? - - [x] No - - [x] 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 - - [x] 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 - - [x] Yes (**explain below**) If yes, please specify: - AI tool or model used: Copilot - Purpose of usage: Translate this PR text from french to English --- ## 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 This is a core-friendly cleanup only, with no behavioral change. No additional logic or CPU cost is introduced. --- src/Ai/Base/Actions/ChooseRpgTargetAction.cpp | 4 +- src/Ai/Base/Actions/DebugAction.cpp | 100 +++++++------- src/Ai/Base/Actions/MovementActions.cpp | 48 +++---- .../Base/Actions/ReviveFromCorpseAction.cpp | 14 +- src/Ai/Base/Actions/RpgSubActions.cpp | 4 +- src/Ai/Base/Actions/RtscAction.cpp | 10 +- src/Ai/Base/Actions/SeeSpellAction.cpp | 12 +- src/Ai/Base/Trigger/RpgTriggers.cpp | 6 +- src/Ai/Base/Trigger/StuckTriggers.cpp | 2 +- .../PitOfSaron/Action/PitOfSaronActions.cpp | 28 ++-- .../GruulsLair/Util/RaidGruulsLairHelpers.cpp | 16 +-- .../Raid/Icecrown/Action/RaidIccActions.cpp | 6 +- src/Ai/World/Rpg/Action/NewRpgBaseAction.cpp | 4 +- src/Bot/PlayerbotAI.cpp | 5 +- src/Bot/RandomPlayerbotMgr.cpp | 4 +- src/Mgr/Move/FleeManager.cpp | 6 +- src/Mgr/Travel/TravelMgr.cpp | 128 +++++++++--------- src/Mgr/Travel/TravelMgr.h | 66 ++++----- src/Mgr/Travel/TravelNode.cpp | 49 +++---- src/Mgr/Travel/TravelNode.h | 10 +- 20 files changed, 269 insertions(+), 253 deletions(-) diff --git a/src/Ai/Base/Actions/ChooseRpgTargetAction.cpp b/src/Ai/Base/Actions/ChooseRpgTargetAction.cpp index 7d83c2b2bc..ce3d63562e 100644 --- a/src/Ai/Base/Actions/ChooseRpgTargetAction.cpp +++ b/src/Ai/Base/Actions/ChooseRpgTargetAction.cpp @@ -320,7 +320,7 @@ bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldPosition pos) inDungeon = true; if (realMaster && realMaster->IsInWorld() && realMaster->GetMap()->IsDungeon() && - (realMaster->GetMapId() != pos.getMapId())) + (realMaster->GetMapId() != pos.GetMapId())) return false; } @@ -334,7 +334,7 @@ bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldPosition pos) return false; Formation* formation = AI_VALUE(Formation*, "formation"); - float distance = groupLeader->GetDistance2d(pos.getX(), pos.getY()); + float distance = groupLeader->GetDistance2d(pos.GetPositionX(), pos.GetPositionY()); if (!botAI->HasActivePlayerMaster() && distance < 50.0f) { diff --git a/src/Ai/Base/Actions/DebugAction.cpp b/src/Ai/Base/Actions/DebugAction.cpp index 578ece17c4..d7fe0f8b75 100644 --- a/src/Ai/Base/Actions/DebugAction.cpp +++ b/src/Ai/Base/Actions/DebugAction.cpp @@ -28,8 +28,8 @@ bool DebugAction::Execute(Event event) uint32 areaId = 0; uint32 zoneId = 0; - sMapMgr->GetZoneAndAreaId(PHASEMASK_NORMAL, zoneId, areaId, pos.getMapId(), pos.getX(), pos.getY(), - pos.getZ()); + sMapMgr->GetZoneAndAreaId(PHASEMASK_NORMAL, zoneId, areaId, pos.GetMapId(), pos.GetPositionX(), pos.GetPositionY(), + pos.GetPositionZ()); std::ostringstream out; out << zoneId << "," << areaId << "," << (pos.getAreaName().empty() ? "none" : pos.getAreaName()) << ","; @@ -298,7 +298,7 @@ bool DebugAction::Execute(Event event) for (auto p : ppath) { Creature* wpCreature = - bot->SummonCreature(1, p.getX(), p.getY(), p.getZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 20000.0f); + bot->SummonCreature(1, p.GetPositionX(), p.GetPositionY(), p.GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 20000.0f); // addAura(246, wpCreature); units.push_back(wpCreature->GetGUID()); @@ -325,11 +325,11 @@ bool DebugAction::Execute(Event event) WorldPosition botPos(bot); WorldPosition botPos1 = botPos; - botPos.setX(botPos.getX() + cos(ang) * dist); - botPos.setY(botPos.getY() + sin(ang) * dist); + botPos.setX(botPos.GetPositionX() + cos(ang) * dist); + botPos.setY(botPos.GetPositionY() + sin(ang) * dist); botPos.setZ(botPos.getHeight() + 2); - Creature* wpCreature = bot->SummonCreature(2334, botPos.getX(), botPos.getY(), botPos.getZ(), 0, + Creature* wpCreature = bot->SummonCreature(2334, botPos.GetPositionX(), botPos.GetPositionY(), botPos.GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 10000.0f); FakeSpell(spellEffect, wpCreature, wpCreature, prev->GetGUID(), {}, {}, botPos, botPos); @@ -352,11 +352,11 @@ bool DebugAction::Execute(Event event) WorldPosition botPos(bot); WorldPosition botPos1 = botPos; - botPos.setX(botPos.getX() + cos(ang) * dist); - botPos.setY(botPos.getY() + sin(ang) * dist); + botPos.setX(botPos.GetPositionX() + cos(ang) * dist); + botPos.setY(botPos.GetPositionY() + sin(ang) * dist); botPos.setZ(botPos.getHeight() + 2); - Creature* wpCreature = bot->SummonCreature(2334, botPos.getX(), botPos.getY(), botPos.getZ(), 0, + Creature* wpCreature = bot->SummonCreature(2334, botPos.GetPositionX(), botPos.GetPositionY(), botPos.GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 10000.0f); if (wpCreature) @@ -383,11 +383,11 @@ bool DebugAction::Execute(Event event) WorldPosition botPos(bot); WorldPosition botPos1 = botPos; - botPos.setX(botPos.getX() + cos(ang) * dist); - botPos.setY(botPos.getY() + sin(ang) * dist); + botPos.setX(botPos.GetPositionX() + cos(ang) * dist); + botPos.setY(botPos.GetPositionY() + sin(ang) * dist); botPos.setZ(botPos.getHeight() + 2); - Creature* wpCreature = bot->SummonCreature(2334, botPos.getX(), botPos.getY(), botPos.getZ(), 0, + Creature* wpCreature = bot->SummonCreature(2334, botPos.GetPositionX(), botPos.GetPositionY(), botPos.GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 5000.0f + i * 100.0f); wpCreature->SetObjectScale(0.5f); @@ -411,11 +411,11 @@ bool DebugAction::Execute(Event event) WorldPosition botPos(bot); - botPos.setX(botPos.getX() + cos(ang) * dist); - botPos.setY(botPos.getY() + sin(ang) * dist); + botPos.setX(botPos.GetPositionX() + cos(ang) * dist); + botPos.setY(botPos.GetPositionY() + sin(ang) * dist); botPos.setZ(botPos.getHeight() + 2); - Creature* wpCreature = bot->SummonCreature(2334, botPos.getX(), botPos.getY(), botPos.getZ(), 0, + Creature* wpCreature = bot->SummonCreature(2334, botPos.GetPositionX(), botPos.GetPositionY(), botPos.GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 10000.0f); units.push_back(wpCreature->GetGUID()); @@ -480,13 +480,13 @@ bool DebugAction::Execute(Event event) uint32 effect = dx + dy * 10 + spellEffect * 100; WorldPosition botPos(bot); - botPos.setX(botPos.getX() + (dx - 5) * 5); - botPos.setY(botPos.getY() + (dy - 5) * 5); + botPos.setX(botPos.GetPositionX() + (dx - 5) * 5); + botPos.setY(botPos.GetPositionY() + (dy - 5) * 5); botPos.setZ(botPos.getHeight()); Creature* wpCreature = - bot->SummonCreature((dy == 0 && (dx == 0 || dx == 2)) ? 6 : 2, botPos.getX(), botPos.getY(), - botPos.getZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 10000.0f); + bot->SummonCreature((dy == 0 && (dx == 0 || dx == 2)) ? 6 : 2, botPos.GetPositionX(), botPos.GetPositionY(), + botPos.GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 10000.0f); if (wpCreature) { @@ -512,11 +512,11 @@ bool DebugAction::Execute(Event event) uint32 effect = dx + dy * 10 + spellEffect * 100; WorldPosition botPos(bot); - botPos.setX(botPos.getX() + (dx - 5) * 5); - botPos.setY(botPos.getY() + (dy - 5) * 5); + botPos.setX(botPos.GetPositionX() + (dx - 5) * 5); + botPos.setY(botPos.GetPositionY() + (dy - 5) * 5); botPos.setZ(botPos.getHeight()); - Creature* wpCreature = bot->SummonCreature(effect, botPos.getX(), botPos.getY(), botPos.getZ(), 0, + Creature* wpCreature = bot->SummonCreature(effect, botPos.GetPositionX(), botPos.GetPositionY(), botPos.GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 10000.0f); } } @@ -532,8 +532,8 @@ bool DebugAction::Execute(Event event) uint32 effect = dx + dy * 10 + spellEffect * 100; WorldPosition botPos(bot); - botPos.setX(botPos.getX() + (dx - 5) * 5); - botPos.setY(botPos.getY() + (dy - 5) * 5); + botPos.setX(botPos.GetPositionX() + (dx - 5) * 5); + botPos.setY(botPos.GetPositionY() + (dy - 5) * 5); botPos.setZ(botPos.getHeight()); FakeSpell(effect, bot, nullptr, ObjectGuid::Empty, {}, {}, botPos, botPos, true); @@ -552,11 +552,11 @@ bool DebugAction::Execute(Event event) uint32 effect = dx + dy * 10 + spellEffect * 100; WorldPosition botPos(bot); - botPos.setX(botPos.getX() + (dx - 5) * 5); - botPos.setY(botPos.getY() + (dy - 5) * 5); + botPos.setX(botPos.GetPositionX() + (dx - 5) * 5); + botPos.setY(botPos.GetPositionY() + (dy - 5) * 5); botPos.setZ(botPos.getHeight()); - Creature* wpCreature = bot->SummonCreature(2334, botPos.getX(), botPos.getY(), botPos.getZ(), 0, + Creature* wpCreature = bot->SummonCreature(2334, botPos.GetPositionX(), botPos.GetPositionY(), botPos.GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 10000.0f); if (wpCreature) @@ -568,7 +568,7 @@ bool DebugAction::Execute(Event event) // wpCreature->SendMessageToSet(&data, true); datMap.push_back(data); - // wpCreature->MonsterMoveWithSpeed(botPos.getX(), botPos.getY() + 80, botPos.getZ(), 8.0f, true, + // wpCreature->MonsterMoveWithSpeed(botPos.GetPositionX(), botPos.GetPositionY() + 80, botPos.GetPositionZ(), 8.0f, true, // true); } } @@ -600,13 +600,13 @@ bool DebugAction::Execute(Event event) uint32 effect = dx + dy * 10 + spellEffect * 100; WorldPosition botPos(bot); - botPos.setX(botPos.getX() + (dx - 5) * 5); - botPos.setY(botPos.getY() + (dy - 5) * 5); + botPos.setX(botPos.GetPositionX() + (dx - 5) * 5); + botPos.setY(botPos.GetPositionY() + (dy - 5) * 5); botPos.setZ(botPos.getHeight()); Creature* wpCreature = - bot->SummonCreature((dy == 0 && (dx == 0 || dx == 2)) ? 6 : 2, botPos.getX(), botPos.getY(), - botPos.getZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 10000.0f); + bot->SummonCreature((dy == 0 && (dx == 0 || dx == 2)) ? 6 : 2, botPos.GetPositionX(), botPos.GetPositionY(), + botPos.GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 10000.0f); if (wpCreature) { @@ -646,12 +646,12 @@ bool DebugAction::Execute(Event event) uint32 effect = dx + dy * 10 + spellEffect * 100; WorldPosition botPos(bot); - botPos.setX(botPos.getX() + (dx - 5) * 5); - botPos.setY(botPos.getY() + (dy - 5) * 5); + botPos.setX(botPos.GetPositionX() + (dx - 5) * 5); + botPos.setY(botPos.GetPositionY() + (dy - 5) * 5); botPos.setZ(botPos.getHeight()); - wpCreature = bot->SummonCreature((dy == 0 && (dx == 0 || dx == 2)) ? 6 : 2, botPos.getX(), - botPos.getY(), botPos.getZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 10000.0f); + wpCreature = bot->SummonCreature((dy == 0 && (dx == 0 || dx == 2)) ? 6 : 2, botPos.GetPositionX(), + botPos.GetPositionY(), botPos.GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 10000.0f); if (wpCreature && lCreature) { @@ -675,11 +675,11 @@ bool DebugAction::Execute(Event event) uint32 effect = dx + dy * 10 + spellEffect * 100; WorldPosition botPos(bot); - botPos.setX(botPos.getX() + (dx - 5) * 5); - botPos.setY(botPos.getY() + (dy - 5) * 5); + botPos.setX(botPos.GetPositionX() + (dx - 5) * 5); + botPos.setY(botPos.GetPositionY() + (dy - 5) * 5); botPos.setZ(botPos.getHeight()); - wpCreature = bot->SummonCreature(2334, botPos.getX(), botPos.getY(), botPos.getZ(), 0, + wpCreature = bot->SummonCreature(2334, botPos.GetPositionX(), botPos.GetPositionY(), botPos.GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 10000.0f); if (wpCreature) @@ -708,11 +708,11 @@ bool DebugAction::Execute(Event event) { WorldPosition botPos(bot); - botPos.setX(botPos.getX() + (dx - 5) * 5); - botPos.setY(botPos.getY() + (dy - 5) * 5); + botPos.setX(botPos.GetPositionX() + (dx - 5) * 5); + botPos.setY(botPos.GetPositionY() + (dy - 5) * 5); botPos.setZ(botPos.getHeight()); - Creature* wpCreature = bot->SummonCreature(2334, botPos.getX(), botPos.getY(), botPos.getZ(), 0, + Creature* wpCreature = bot->SummonCreature(2334, botPos.GetPositionX(), botPos.GetPositionY(), botPos.GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 10000.0f); all_targets.push_back(wpCreature->GetGUID()); @@ -788,11 +788,11 @@ bool DebugAction::Execute(Event event) { WorldPosition botPos(bot); - botPos.setX(botPos.getX() + (dx - 5) * 5); - botPos.setY(botPos.getY() + (dy - 5) * 5); + botPos.setX(botPos.GetPositionX() + (dx - 5) * 5); + botPos.setY(botPos.GetPositionY() + (dy - 5) * 5); botPos.setZ(botPos.getHeight()); - Creature* wpCreature = bot->SummonCreature(2334, botPos.getX(), botPos.getY(), botPos.getZ(), 0, + Creature* wpCreature = bot->SummonCreature(2334, botPos.GetPositionX(), botPos.GetPositionY(), botPos.GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 10000.0f); all_targets.push_back(wpCreature->GetGUID()); @@ -868,13 +868,13 @@ bool DebugAction::Execute(Event event) uint32 effect = dx + dy * 10 + soundEffects * 100; WorldPosition botPos(bot); - botPos.setX(botPos.getX() + (dx - 5) * 5); - botPos.setY(botPos.getY() + (dy - 5) * 5); + botPos.setX(botPos.GetPositionX() + (dx - 5) * 5); + botPos.setY(botPos.GetPositionY() + (dy - 5) * 5); botPos.setZ(botPos.getHeight()); Creature* wpCreature = - bot->SummonCreature((dy == 0 && (dx == 0 || dx == 2)) ? 6 : 2, botPos.getX(), botPos.getY(), - botPos.getZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 10000.0f); + bot->SummonCreature((dy == 0 && (dx == 0 || dx == 2)) ? 6 : 2, botPos.GetPositionX(), botPos.GetPositionY(), + botPos.GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 10000.0f); wpCreature->PlayDistanceSound(effect); } @@ -964,7 +964,7 @@ void DebugAction::FakeSpell(uint32 spellId, Unit* truecaster, Unit* caster, Obje m_targets.SetDst(dest); if ((spellInfo && spellInfo->Targets & TARGET_FLAG_SOURCE_LOCATION) || forceDest) - m_targets.SetSrc(source.getX(), source.getY(), source.getZ()); + m_targets.SetSrc(source.GetPositionX(), source.GetPositionY(), source.GetPositionZ()); if (!forceDest && target) if (!spellInfo || diff --git a/src/Ai/Base/Actions/MovementActions.cpp b/src/Ai/Base/Actions/MovementActions.cpp index c595dff6e4..d4090c0ef2 100644 --- a/src/Ai/Base/Actions/MovementActions.cpp +++ b/src/Ai/Base/Actions/MovementActions.cpp @@ -370,7 +370,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // { // movePosition = endPosition; - // if (startPosition.getMapId() != endPosition.getMapId() || totalDistance > maxDist) + // if (startPosition.GetMapId() != endPosition.GetMapId() || totalDistance > maxDist) // { // if (!TravelNodeMap::instance().getNodes().empty() && !bot->InBattleground()) // { @@ -421,7 +421,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // { // //Use standard PathGenerator to find a route. // PathGenerator path(mover); - // path.CalculatePath(movePosition.getX(), movePosition.getY(), movePosition.getZ(), false); + // path.CalculatePath(movePosition.GetPositionX(), movePosition.GetPositionY(), movePosition.GetPositionZ(), false); // PathType type = path.GetPathType(); // Movement::PointsArray const& points = path.GetPath(); // movePath.addPath(startPosition.fromPointsArray(points)); @@ -485,8 +485,8 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // else // { // LOG_DEBUG("playerbots", "!entry"); - // return bot->TeleportTo(movePosition.getMapId(), movePosition.getX(), movePosition.getY(), - // movePosition.getZ(), movePosition.getO(), 0); + // return bot->TeleportTo(movePosition.GetMapId(), movePosition.GetPositionX(), movePosition.GetPositionY(), + // movePosition.GetPositionZ(), movePosition.GetOrientation(), 0); // } // } @@ -563,14 +563,14 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // AI_VALUE(LastMovement&, "last movement").setPath(movePath); - // if (!movePosition || movePosition.getMapId() != bot->GetMapId()) + // if (!movePosition || movePosition.GetMapId() != bot->GetMapId()) // { // movePath.clear(); // AI_VALUE(LastMovement&, "last movement").setPath(movePath); // if (botAI->HasStrategy("debug move", BOT_STATE_NON_COMBAT)) // botAI->TellMasterNoFacing("No point. Rebuilding."); - // LOG_DEBUG("playerbots", "!movePosition || movePosition.getMapId() != bot->GetMapId()"); + // LOG_DEBUG("playerbots", "!movePosition || movePosition.GetMapId() != bot->GetMapId()"); // return false; // } @@ -609,15 +609,15 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // float cz = z; // for (auto i : movePath.getPath()) // { - // CreateWp(bot, i.point.getX(), i.point.getY(), i.point.getZ(), 0.f, 2334); + // CreateWp(bot, i.point.GetPositionX(), i.point.GetPositionY(), i.point.GetPositionZ(), 0.f, 2334); - // cx = i.point.getX(); - // cy = i.point.getY(); - // cz = i.point.getZ(); + // cx = i.point.GetPositionX(); + // cy = i.point.GetPositionY(); + // cz = i.point.GetPositionZ(); // } // } // else - // CreateWp(bot, movePosition.getX(), movePosition.getY(), movePosition.getZ(), 0, 2334, true); + // CreateWp(bot, movePosition.GetPositionX(), movePosition.GetPositionY(), movePosition.GetPositionZ(), 0, 2334, true); // } // //Log bot movement @@ -634,8 +634,8 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // sPlayerbotAIConfig.log("bot_movement.csv", out.str().c_str()); // } - // // LOG_DEBUG("playerbots", "({}, {}) -> ({}, {})", startPosition.getX(), startPosition.getY(), - // movePosition.getX(), movePosition.getY()); if (!react) + // // LOG_DEBUG("playerbots", "({}, {}) -> ({}, {})", startPosition.GetPositionX(), startPosition.GetPositionY(), + // movePosition.GetPositionX(), movePosition.GetPositionY()); if (!react) // if (totalDistance > maxDist) // WaitForReach(startPosition.distance(movePosition) - 10.0f); // else @@ -671,7 +671,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // // else // // { // // mover->GetMotionMaster()->GetDestination(x, y, z); - // // if (movePosition.distance(WorldPosition(movePosition.getMapId(), x, y, z, 0)) > minDist) + // // if (movePosition.distance(WorldPosition(movePosition.GetMapId(), x, y, z, 0)) > minDist) // // { // // mover->StopMoving(); // // mover->GetMotionMaster()->Clear(); @@ -685,8 +685,8 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // AI_VALUE(LastMovement&, "last movement").nextTeleport = now + // (time_t)MoveDelay(startPosition.distance(movePosition)); LOG_DEBUG("playerbots", "totalDistance > maxDist && - // !detailedMove && !botAI->HasPlayerNearby(&movePosition)"); return bot->TeleportTo(movePosition.getMapId(), - // movePosition.getX(), movePosition.getY(), movePosition.getZ(), startPosition.getAngleTo(movePosition)); + // !detailedMove && !botAI->HasPlayerNearby(&movePosition)"); return bot->TeleportTo(movePosition.GetMapId(), + // movePosition.GetPositionX(), movePosition.GetPositionY(), movePosition.GetPositionZ(), startPosition.getAngleTo(movePosition)); // } // // walk if master walks and is close @@ -708,9 +708,9 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // if (!bot->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED) && !bot->HasAuraType(SPELL_AURA_FLY)) // { // bot->SetWalk(masterWalking); - // bot->GetMotionMaster()->MovePoint(movePosition.getMapId(), movePosition.getX(), movePosition.getY(), - // movePosition.getZ(), generatePath); WaitForReach(startPosition.distance(movePosition)); - // // LOG_DEBUG("playerbots", "Movepoint to ({}, {})", movePosition.getX(), movePosition.getY()); + // bot->GetMotionMaster()->MovePoint(movePosition.GetMapId(), movePosition.GetPositionX(), movePosition.GetPositionY(), + // movePosition.GetPositionZ(), generatePath); WaitForReach(startPosition.distance(movePosition)); + // // LOG_DEBUG("playerbots", "Movepoint to ({}, {})", movePosition.GetPositionX(), movePosition.GetPositionY()); // } // else // { @@ -756,9 +756,9 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // } // } - // bot->GetMotionMaster()->MovePoint(movePosition.getMapId(), Position(movePosition.getX(), movePosition.getY(), - // movePosition.getZ(), 0.f)); WaitForReach(startPosition.distance(movePosition)); LOG_DEBUG("playerbots", - // "Movepoint to ({}, {})", movePosition.getX(), movePosition.getY()); + // bot->GetMotionMaster()->MovePoint(movePosition.GetMapId(), Position(movePosition.GetPositionX(), movePosition.GetPositionY(), + // movePosition.GetPositionZ(), 0.f)); WaitForReach(startPosition.distance(movePosition)); LOG_DEBUG("playerbots", + // "Movepoint to ({}, {})", movePosition.GetPositionX(), movePosition.GetPositionY()); // } // AI_VALUE(LastMovement&, "last movement").setShort(movePosition); @@ -1190,7 +1190,7 @@ bool MovementAction::Follow(Unit* target, float distance, float angle) WorldPosition cPos(corpse); if (botPos.fDist(cPos) > sPlayerbotAIConfig.spellDistance) - return MoveTo(cPos.getMapId(), cPos.getX(), cPos.getY(), cPos.getZ()); + return MoveTo(cPos.GetMapId(), cPos.GetPositionX(), cPos.GetPositionY(), cPos.GetPositionZ()); } } @@ -1217,7 +1217,7 @@ bool MovementAction::Follow(Unit* target, float distance, float angle) if ((lDist * 1.5 < tDist && ang < static_cast(M_PI) / 2) || target->HasUnitState(UNIT_STATE_IN_FLIGHT)) { - return MoveTo(longMove.getMapId(), longMove.getX(), longMove.getY(), longMove.getZ()); + return MoveTo(longMove.GetMapId(), longMove.GetPositionX(), longMove.GetPositionY(), longMove.GetPositionZ()); } } } diff --git a/src/Ai/Base/Actions/ReviveFromCorpseAction.cpp b/src/Ai/Base/Actions/ReviveFromCorpseAction.cpp index 4af96fc698..970d378f47 100644 --- a/src/Ai/Base/Actions/ReviveFromCorpseAction.cpp +++ b/src/Ai/Base/Actions/ReviveFromCorpseAction.cpp @@ -150,7 +150,7 @@ bool FindCorpseAction::Execute(Event event) { float rx, ry, rz; if (manager.CalculateDestination(&rx, &ry, &rz)) - moveToPos = WorldPosition(moveToPos.getMapId(), rx, ry, rz, 0.0); + moveToPos = WorldPosition(moveToPos.GetMapId(), rx, ry, rz, 0.0); else if (!moveToPos.GetReachableRandomPointOnGround(bot, reclaimDist, urand(0, 1))) moveToPos = corpsePos; } @@ -170,7 +170,7 @@ bool FindCorpseAction::Execute(Event event) { bot->GetMotionMaster()->Clear(); bot->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP); - bot->TeleportTo(moveToPos.getMapId(), moveToPos.getX(), moveToPos.getY(), moveToPos.getZ(), 0); + bot->TeleportTo(moveToPos.GetMapId(), moveToPos.GetPositionX(), moveToPos.GetPositionY(), moveToPos.GetPositionZ(), 0); } moved = true; @@ -184,7 +184,7 @@ bool FindCorpseAction::Execute(Event event) if (deadTime < 10 * MINUTE && dCount < 5) // Look for corpse up to 30 minutes. { moved = - MoveTo(moveToPos.getMapId(), moveToPos.getX(), moveToPos.getY(), moveToPos.getZ(), false, false); + MoveTo(moveToPos.GetMapId(), moveToPos.GetPositionX(), moveToPos.GetPositionY(), moveToPos.GetPositionZ(), false, false); } if (!moved) @@ -237,10 +237,10 @@ GraveyardStruct const* SpiritHealerAction::GetGrave(bool startZone) { uint32 areaId = 0; uint32 zoneId = 0; - sMapMgr->GetZoneAndAreaId(bot->GetPhaseMask(), zoneId, areaId, travelPos.getMapId(), travelPos.getX(), - travelPos.getY(), travelPos.getZ()); - ClosestGrave = sGraveyard->GetClosestGraveyard(travelPos.getMapId(), travelPos.getX(), travelPos.getY(), - travelPos.getZ(), bot->GetTeamId(), areaId, zoneId, + sMapMgr->GetZoneAndAreaId(bot->GetPhaseMask(), zoneId, areaId, travelPos.GetMapId(), travelPos.GetPositionX(), + travelPos.GetPositionY(), travelPos.GetPositionZ()); + ClosestGrave = sGraveyard->GetClosestGraveyard(travelPos.GetMapId(), travelPos.GetPositionX(), travelPos.GetPositionY(), + travelPos.GetPositionZ(), bot->GetTeamId(), areaId, zoneId, bot->getClass() == CLASS_DEATH_KNIGHT); if (ClosestGrave) diff --git a/src/Ai/Base/Actions/RpgSubActions.cpp b/src/Ai/Base/Actions/RpgSubActions.cpp index 43bff6ab83..3f0947ac83 100644 --- a/src/Ai/Base/Actions/RpgSubActions.cpp +++ b/src/Ai/Base/Actions/RpgSubActions.cpp @@ -150,7 +150,7 @@ bool RpgTaxiAction::Execute(Event event) bot->GetSession()->HandleCancelMountAuraOpcode(emptyPacket); uint32 node = - sObjectMgr->GetNearestTaxiNode(guidP.getX(), guidP.getY(), guidP.getZ(), guidP.getMapId(), bot->GetTeamId()); + sObjectMgr->GetNearestTaxiNode(guidP.GetPositionX(), guidP.GetPositionY(), guidP.GetPositionZ(), guidP.GetMapId(), bot->GetTeamId()); std::vector nodes; for (uint32 i = 0; i < sTaxiPathStore.GetNumRows(); ++i) @@ -208,7 +208,7 @@ bool RpgDiscoverAction::Execute(Event event) GuidPosition guidP = rpg->guidP(); uint32 node = - sObjectMgr->GetNearestTaxiNode(guidP.getX(), guidP.getY(), guidP.getZ(), guidP.getMapId(), bot->GetTeamId()); + sObjectMgr->GetNearestTaxiNode(guidP.GetPositionX(), guidP.GetPositionY(), guidP.GetPositionZ(), guidP.GetMapId(), bot->GetTeamId()); if (!node) return false; diff --git a/src/Ai/Base/Actions/RtscAction.cpp b/src/Ai/Base/Actions/RtscAction.cpp index 4ed91d29c0..8309a4f09f 100644 --- a/src/Ai/Base/Actions/RtscAction.cpp +++ b/src/Ai/Base/Actions/RtscAction.cpp @@ -80,8 +80,9 @@ bool RTSCAction::Execute(Event event) SET_AI_VALUE2(WorldPosition, "RTSC saved location", locationName, spellPosition); Creature* wpCreature = - bot->SummonCreature(15631, spellPosition.getX(), spellPosition.getY(), spellPosition.getZ(), - spellPosition.getO(), TEMPSUMMON_TIMED_DESPAWN, 2000.0f); + bot->SummonCreature(15631, spellPosition.GetPositionX(), spellPosition.GetPositionY(), + spellPosition.GetPositionZ(), spellPosition.GetOrientation(), TEMPSUMMON_TIMED_DESPAWN, + 2000.0f); wpCreature->SetObjectScale(0.5f); return true; @@ -110,8 +111,9 @@ bool RTSCAction::Execute(Event event) if (spellPosition) { Creature* wpCreature = - bot->SummonCreature(15631, spellPosition.getX(), spellPosition.getY(), spellPosition.getZ(), - spellPosition.getO(), TEMPSUMMON_TIMED_DESPAWN, 2000.0f); + bot->SummonCreature(15631, spellPosition.GetPositionX(), spellPosition.GetPositionY(), + spellPosition.GetPositionZ(), spellPosition.GetOrientation(), + TEMPSUMMON_TIMED_DESPAWN, 2000.0f); wpCreature->SetObjectScale(0.5f); } diff --git a/src/Ai/Base/Actions/SeeSpellAction.cpp b/src/Ai/Base/Actions/SeeSpellAction.cpp index bec9577cf6..03117586e4 100644 --- a/src/Ai/Base/Actions/SeeSpellAction.cpp +++ b/src/Ai/Base/Actions/SeeSpellAction.cpp @@ -134,8 +134,8 @@ bool SeeSpellAction::Execute(Event event) SET_AI_VALUE2(WorldPosition, "RTSC saved location", locationName, spellPosition); Creature* wpCreature = - bot->SummonCreature(15631, spellPosition.getX(), spellPosition.getY(), spellPosition.getZ(), - spellPosition.getO(), TEMPSUMMON_TIMED_DESPAWN, 2000.0f); + bot->SummonCreature(15631, spellPosition.GetPositionX(), spellPosition.GetPositionY(), spellPosition.GetPositionZ(), + spellPosition.GetOrientation(), TEMPSUMMON_TIMED_DESPAWN, 2000.0f); wpCreature->SetObjectScale(0.5f); RESET_AI_VALUE(std::string, "RTSC next spell action"); @@ -167,14 +167,14 @@ bool SeeSpellAction::MoveToSpell(WorldPosition& spellPosition, bool inFormation) PositionMap& posMap = AI_VALUE(PositionMap&, "position"); PositionInfo stayPosition = posMap["stay"]; - stayPosition.Set(spellPosition.getX(), spellPosition.getY(), spellPosition.getZ(), spellPosition.getMapId()); + stayPosition.Set(spellPosition.GetPositionX(), spellPosition.GetPositionY(), spellPosition.GetPositionZ(), spellPosition.GetMapId()); posMap["stay"] = stayPosition; } - if (bot->IsWithinLOS(spellPosition.getX(), spellPosition.getY(), spellPosition.getZ())) - return MoveNear(spellPosition.getMapId(), spellPosition.getX(), spellPosition.getY(), spellPosition.getZ(), 0); + if (bot->IsWithinLOS(spellPosition.GetPositionX(), spellPosition.GetPositionY(), spellPosition.GetPositionZ())) + return MoveNear(spellPosition.GetMapId(), spellPosition.GetPositionX(), spellPosition.GetPositionY(), spellPosition.GetPositionZ(), 0); - return MoveTo(spellPosition.getMapId(), spellPosition.getX(), spellPosition.getY(), spellPosition.getZ(), false, + return MoveTo(spellPosition.GetMapId(), spellPosition.GetPositionX(), spellPosition.GetPositionY(), spellPosition.GetPositionZ(), false, false); } diff --git a/src/Ai/Base/Trigger/RpgTriggers.cpp b/src/Ai/Base/Trigger/RpgTriggers.cpp index f390359be3..0151c6c5e7 100644 --- a/src/Ai/Base/Trigger/RpgTriggers.cpp +++ b/src/Ai/Base/Trigger/RpgTriggers.cpp @@ -46,7 +46,8 @@ bool RpgTaxiTrigger::IsActive() return false; uint32 node = - sObjectMgr->GetNearestTaxiNode(guidP.getX(), guidP.getY(), guidP.getZ(), guidP.getMapId(), bot->GetTeamId()); + sObjectMgr->GetNearestTaxiNode(guidP.GetPositionX(), guidP.GetPositionY(), guidP.GetPositionZ(), + guidP.GetMapId(), bot->GetTeamId()); if (!node) return false; @@ -68,7 +69,8 @@ bool RpgDiscoverTrigger::IsActive() return false; uint32 node = - sObjectMgr->GetNearestTaxiNode(guidP.getX(), guidP.getY(), guidP.getZ(), guidP.getMapId(), bot->GetTeamId()); + sObjectMgr->GetNearestTaxiNode(guidP.GetPositionX(), guidP.GetPositionY(), guidP.GetPositionZ(), + guidP.GetMapId(), bot->GetTeamId()); if (bot->m_taxi.IsTaximaskNodeKnown(node)) return false; diff --git a/src/Ai/Base/Trigger/StuckTriggers.cpp b/src/Ai/Base/Trigger/StuckTriggers.cpp index f0a54345a1..b6ade75d86 100644 --- a/src/Ai/Base/Trigger/StuckTriggers.cpp +++ b/src/Ai/Base/Trigger/StuckTriggers.cpp @@ -90,7 +90,7 @@ bool MoveLongStuckTrigger::IsActive() } if (cell.GridX() > 0 && cell.GridY() > 0 && - !MMAP::MMapFactory::createOrGetMMapMgr()->loadMap(botPos.getMapId(), cell.GridX(), cell.GridY())) + !MMAP::MMapFactory::createOrGetMMapMgr()->loadMap(botPos.GetMapId(), cell.GridX(), cell.GridY())) { // LOG_INFO("playerbots", "Bot {} {}:{} <{}> was in unloaded grid {},{} on map {}", // bot->GetGUID().ToString().c_str(), bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(), diff --git a/src/Ai/Dungeon/PitOfSaron/Action/PitOfSaronActions.cpp b/src/Ai/Dungeon/PitOfSaron/Action/PitOfSaronActions.cpp index f34b98462e..d435a208d9 100644 --- a/src/Ai/Dungeon/PitOfSaron/Action/PitOfSaronActions.cpp +++ b/src/Ai/Dungeon/PitOfSaron/Action/PitOfSaronActions.cpp @@ -193,17 +193,18 @@ bool IckAndKrickAction::ExplosiveBarrage(bool explosiveBarrage, Unit* boss) { float angle = i * ANGLE_INCREMENT; Position potentialPos; - potentialPos.m_positionX = bot->GetPositionX() + SAFE_DISTANCE * cos(angle); - potentialPos.m_positionY = bot->GetPositionY() + SAFE_DISTANCE * sin(angle); - potentialPos.m_positionZ = bot->GetPositionZ(); + potentialPos.Relocate(bot->GetPositionX() + SAFE_DISTANCE * cos(angle), + bot->GetPositionY() + SAFE_DISTANCE * sin(angle), + bot->GetPositionZ()); // Check if position is valid (not in a wall) - if (!bot->IsWithinLOS(potentialPos.m_positionX, potentialPos.m_positionY, potentialPos.m_positionZ)) + if (!bot->IsWithinLOS(potentialPos.GetPositionX(), potentialPos.GetPositionY(), + potentialPos.GetPositionZ())) continue; // Check if position is within maximum allowed distance from boss - if (boss && sqrt(pow(potentialPos.m_positionX - boss->GetPositionX(), 2) + - pow(potentialPos.m_positionY - boss->GetPositionY(), 2)) > MAX_BOSS_DISTANCE) + if (boss && sqrt(pow(potentialPos.GetPositionX() - boss->GetPositionX(), 2) + + pow(potentialPos.GetPositionY() - boss->GetPositionY(), 2)) > MAX_BOSS_DISTANCE) continue; // Score this position based on: @@ -216,8 +217,8 @@ bool IckAndKrickAction::ExplosiveBarrage(bool explosiveBarrage, Unit* boss) float minOrbDist = std::numeric_limits::max(); for (Unit* orb : orbs) { - float orbDist = sqrt(pow(potentialPos.m_positionX - orb->GetPositionX(), 2) + - pow(potentialPos.m_positionY - orb->GetPositionY(), 2)); + float orbDist = sqrt(pow(potentialPos.GetPositionX() - orb->GetPositionX(), 2) + + pow(potentialPos.GetPositionY() - orb->GetPositionY(), 2)); minOrbDist = std::min(minOrbDist, orbDist); } score += minOrbDist * 2.0f; // Weight orb distance more heavily @@ -225,16 +226,16 @@ bool IckAndKrickAction::ExplosiveBarrage(bool explosiveBarrage, Unit* boss) // Check distance from other players for (const Position& playerPos : playerPositions) { - float playerDist = sqrt(pow(potentialPos.m_positionX - playerPos.m_positionX, 2) + - pow(potentialPos.m_positionY - playerPos.m_positionY, 2)); + float playerDist = sqrt(pow(potentialPos.GetPositionX() - playerPos.GetPositionX(), 2) + + pow(potentialPos.GetPositionY() - playerPos.GetPositionY(), 2)); score += std::min(playerDist, 10.0f); // Cap player distance contribution } // Factor in proximity to boss (closer is better, as long as we're safe from orbs) if (boss) { - float bossDist = sqrt(pow(potentialPos.m_positionX - boss->GetPositionX(), 2) + - pow(potentialPos.m_positionY - boss->GetPositionY(), 2)); + float bossDist = sqrt(pow(potentialPos.GetPositionX() - boss->GetPositionX(), 2) + + pow(potentialPos.GetPositionY() - boss->GetPositionY(), 2)); // Add points for being closer to boss (inverse relationship) // but only if we're safely away from orbs if (minOrbDist > SAFE_DISTANCE) @@ -257,7 +258,8 @@ bool IckAndKrickAction::ExplosiveBarrage(bool explosiveBarrage, Unit* boss) { botAI->Reset(); // Use MoveTo instead of FleePosition since we already calculated the exact position - return MoveTo(bot->GetMapId(), bestPos.m_positionX, bestPos.m_positionY, bot->GetPositionZ(), false, false, + return MoveTo(bot->GetMapId(), bestPos.GetPositionX(), bestPos.GetPositionY(), bot->GetPositionZ(), false, + false, false, true, MovementPriority::MOVEMENT_COMBAT); } else diff --git a/src/Ai/Raid/GruulsLair/Util/RaidGruulsLairHelpers.cpp b/src/Ai/Raid/GruulsLair/Util/RaidGruulsLairHelpers.cpp index 27c703aecc..7ed82f19e6 100644 --- a/src/Ai/Raid/GruulsLair/Util/RaidGruulsLairHelpers.cpp +++ b/src/Ai/Raid/GruulsLair/Util/RaidGruulsLairHelpers.cpp @@ -164,21 +164,21 @@ namespace GruulsLairHelpers { float angle = 2 * M_PI * i / NUM_POSITIONS; Position candidatePos; - candidatePos.m_positionX = bot->GetPositionX() + SEARCH_RADIUS * cos(angle); - candidatePos.m_positionY = bot->GetPositionY() + SEARCH_RADIUS * sin(angle); - candidatePos.m_positionZ = bot->GetPositionZ(); + candidatePos.Relocate(bot->GetPositionX() + SEARCH_RADIUS * cos(angle), + bot->GetPositionY() + SEARCH_RADIUS * sin(angle), + bot->GetPositionZ()); - float destX = candidatePos.m_positionX, destY = candidatePos.m_positionY, destZ = candidatePos.m_positionZ; + float destX = candidatePos.GetPositionX(); + float destY = candidatePos.GetPositionY(); + float destZ = candidatePos.GetPositionZ(); if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), destX, destY, destZ, true)) continue; - if (destX != candidatePos.m_positionX || destY != candidatePos.m_positionY) + if (destX != candidatePos.GetPositionX() || destY != candidatePos.GetPositionY()) continue; - candidatePos.m_positionX = destX; - candidatePos.m_positionY = destY; - candidatePos.m_positionZ = destZ; + candidatePos.Relocate(destX, destY, destZ); if (IsPositionSafe(botAI, bot, candidatePos)) { diff --git a/src/Ai/Raid/Icecrown/Action/RaidIccActions.cpp b/src/Ai/Raid/Icecrown/Action/RaidIccActions.cpp index d6a91d0477..c3e3513e2a 100644 --- a/src/Ai/Raid/Icecrown/Action/RaidIccActions.cpp +++ b/src/Ai/Raid/Icecrown/Action/RaidIccActions.cpp @@ -1551,8 +1551,10 @@ Position IccFestergutSporeAction::CalculateSpreadPosition() float angle = (bot->GetGUID().GetCounter() % 16) * (M_PI / 8); Position spreadRangedPos = ICC_FESTERGUT_RANGED_SPORE; - spreadRangedPos.m_positionX += cos(angle) * SPREAD_RADIUS; - spreadRangedPos.m_positionY += sin(angle) * SPREAD_RADIUS; + spreadRangedPos.Relocate(spreadRangedPos.GetPositionX() + cos(angle) * SPREAD_RADIUS, + spreadRangedPos.GetPositionY() + sin(angle) * SPREAD_RADIUS, + spreadRangedPos.GetPositionZ(), + spreadRangedPos.GetOrientation()); return spreadRangedPos; } diff --git a/src/Ai/World/Rpg/Action/NewRpgBaseAction.cpp b/src/Ai/World/Rpg/Action/NewRpgBaseAction.cpp index 0204a21331..bd27bece5d 100644 --- a/src/Ai/World/Rpg/Action/NewRpgBaseAction.cpp +++ b/src/Ai/World/Rpg/Action/NewRpgBaseAction.cpp @@ -66,7 +66,7 @@ bool NewRpgBaseAction::MoveFarTo(WorldPosition dest) "playerbots", "[New RPG] Teleport {} from ({},{},{},{}) to ({},{},{},{}) as it stuck when moving far - Zone: {} ({})", bot->GetName(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), bot->GetMapId(), - dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(), dest.getMapId(), bot->GetZoneId(), + dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(), dest.GetMapId(), bot->GetZoneId(), zone_name); bot->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP); return bot->TeleportTo(dest); @@ -75,7 +75,7 @@ bool NewRpgBaseAction::MoveFarTo(WorldPosition dest) float dis = bot->GetExactDist(dest); if (dis < pathFinderDis) { - return MoveTo(dest.getMapId(), dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(), false, false, + return MoveTo(dest.GetMapId(), dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(), false, false, false, true); } diff --git a/src/Bot/PlayerbotAI.cpp b/src/Bot/PlayerbotAI.cpp index 96019402d3..c8fc4cb303 100644 --- a/src/Bot/PlayerbotAI.cpp +++ b/src/Bot/PlayerbotAI.cpp @@ -5192,8 +5192,9 @@ std::string const PlayerbotAI::HandleRemoteCommand(std::string const command) { LastMovement& data = *GetAiObjectContext()->GetValue("last movement"); std::ostringstream out; - out << data.lastMoveShort.getX() << " " << data.lastMoveShort.getY() << " " << data.lastMoveShort.getZ() << " " - << data.lastMoveShort.getMapId() << " " << data.lastMoveShort.getO(); + out << data.lastMoveShort.GetPositionX() << " " << data.lastMoveShort.GetPositionY() << " " + << data.lastMoveShort.GetPositionZ() << " " << data.lastMoveShort.GetMapId() << " " + << data.lastMoveShort.GetOrientation(); return out.str(); } else if (command == "target") diff --git a/src/Bot/RandomPlayerbotMgr.cpp b/src/Bot/RandomPlayerbotMgr.cpp index fad757fa93..6cdf1fcaeb 100644 --- a/src/Bot/RandomPlayerbotMgr.cpp +++ b/src/Bot/RandomPlayerbotMgr.cpp @@ -1667,7 +1667,7 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector& { std::vector::iterator i = find(sPlayerbotAIConfig.randomBotMaps.begin(), - sPlayerbotAIConfig.randomBotMaps.end(), l.getMapId()); + sPlayerbotAIConfig.randomBotMaps.end(), l.GetMapId()); return i == sPlayerbotAIConfig.randomBotMaps.end(); }), tlocs.end()); @@ -3073,7 +3073,7 @@ void RandomPlayerbotMgr::OnPlayerLogin(Player* player) FactionTemplateEntry const* factionEntry = sFactionTemplateStore.LookupEntry(cInfo->faction); ReputationRank reaction = Unit::GetFactionReactionTo(player->GetFactionTemplateEntry(), factionEntry); - if (reaction > REP_NEUTRAL && dest->nearestPoint(&botPos)->m_mapId == player->GetMapId()) + if (reaction > REP_NEUTRAL && dest->nearestPoint(&botPos)->GetMapId() == player->GetMapId()) { botPos = *dest->nearestPoint(&botPos); break; diff --git a/src/Mgr/Move/FleeManager.cpp b/src/Mgr/Move/FleeManager.cpp index d3d1a8bb02..e82f01daf0 100644 --- a/src/Mgr/Move/FleeManager.cpp +++ b/src/Mgr/Move/FleeManager.cpp @@ -62,9 +62,9 @@ void FleeManager::calculatePossibleDestinations(std::vector& points) } Unit* target = *botAI->GetAiObjectContext()->GetValue("current target"); - float botPosX = startPosition.getX(); - float botPosY = startPosition.getY(); - float botPosZ = startPosition.getZ(); + float botPosX = startPosition.GetPositionX(); + float botPosY = startPosition.GetPositionY(); + float botPosZ = startPosition.GetPositionZ(); FleePoint start(botAI, botPosX, botPosY, botPosZ); calculateDistanceToCreatures(&start); diff --git a/src/Mgr/Travel/TravelMgr.cpp b/src/Mgr/Travel/TravelMgr.cpp index 02454edc53..1a390d5041 100644 --- a/src/Mgr/Travel/TravelMgr.cpp +++ b/src/Mgr/Travel/TravelMgr.cpp @@ -71,13 +71,13 @@ WorldPosition::WorldPosition(std::vector list, WorldPositionCons set(*list[urand(0, size - 1)]); else if (conType == WP_CENTROID) { - set(std::accumulate(list.begin(), list.end(), WorldLocation(list[0]->getMapId(), 0, 0, 0, 0), + set(std::accumulate(list.begin(), list.end(), WorldLocation(list[0]->GetMapId(), 0, 0, 0, 0), [size](WorldLocation i, WorldPosition* j) { - i.m_positionX += j->getX() / size; - i.m_positionY += j->getY() / size; - i.m_positionZ += j->getZ() / size; - i.NormalizeOrientation(i.m_orientation += j->getO() / size); + i.m_positionX += j->GetPositionX() / size; + i.m_positionY += j->GetPositionY() / size; + i.m_positionZ += j->GetPositionZ() / size; + i.NormalizeOrientation(i.m_orientation += j->GetOrientation() / size); return i; })); } @@ -100,13 +100,13 @@ WorldPosition::WorldPosition(std::vector list, WorldPositionConst set(list[urand(0, size - 1)]); else if (conType == WP_CENTROID) { - set(std::accumulate(list.begin(), list.end(), WorldLocation(list[0].getMapId(), 0, 0, 0, 0), + set(std::accumulate(list.begin(), list.end(), WorldLocation(list[0].GetMapId(), 0, 0, 0, 0), [size](WorldLocation i, WorldPosition& j) { - i.m_positionX += j.getX() / size; - i.m_positionY += j.getY() / size; - i.m_positionZ += j.getZ() / size; - i.NormalizeOrientation(i.m_orientation += j.getO() / size); + i.m_positionX += j.GetPositionX() / size; + i.m_positionY += j.GetPositionY() / size; + i.m_positionZ += j.GetPositionZ() / size; + i.NormalizeOrientation(i.m_orientation += j.GetOrientation() / size); return i; })); } @@ -190,16 +190,6 @@ WorldPosition& WorldPosition::operator-=(WorldPosition const& p1) return *this; } -uint32 WorldPosition::getMapId() { return GetMapId(); } - -float WorldPosition::getX() { return GetPositionX(); } - -float WorldPosition::getY() { return GetPositionY(); } - -float WorldPosition::getZ() { return GetPositionZ(); } - -float WorldPosition::getO() { return GetOrientation(); } - bool WorldPosition::isOverworld() { return GetMapId() == 0 || GetMapId() == 1 || GetMapId() == 530 || GetMapId() == 571; @@ -243,7 +233,7 @@ float WorldPosition::size() float WorldPosition::distance(WorldPosition* center) { - if (GetMapId() == center->getMapId()) + if (GetMapId() == center->GetMapId()) return relPoint(center).size(); // this -> mapTransfer | mapTransfer -> center @@ -252,7 +242,7 @@ float WorldPosition::distance(WorldPosition* center) float WorldPosition::fDist(WorldPosition* center) { - if (GetMapId() == center->getMapId()) + if (GetMapId() == center->GetMapId()) return sqrt(sqDistance2d(center)); // this -> mapTransfer | mapTransfer -> center @@ -328,7 +318,7 @@ WorldPosition WorldPosition::firstOutRange(std::vector list, floa // Returns true if (on the x-y plane) the position is inside the three points. bool WorldPosition::isInside(WorldPosition* p1, WorldPosition* p2, WorldPosition* p3) { - if (getMapId() != p1->getMapId() != p2->getMapId() != p3->getMapId()) + if (GetMapId() != p1->GetMapId() != p2->GetMapId() != p3->GetMapId()) return false; float d1, d2, d3; @@ -348,7 +338,7 @@ MapEntry const* WorldPosition::getMapEntry() { return sMapStore.LookupEntry(GetM uint32 WorldPosition::getInstanceId() { - if (Map* map = sMapMgr->FindBaseMap(getMapId())) + if (Map* map = sMapMgr->FindBaseMap(GetMapId())) return map->GetInstanceId(); return 0; @@ -361,7 +351,7 @@ Map* WorldPosition::getMap() float WorldPosition::getHeight() // remove const - whipowill { - return getMap()->GetHeight(getX(), getY(), getZ()); + return getMap()->GetHeight(GetPositionX(), GetPositionY(), GetPositionZ()); } G3D::Vector3 WorldPosition::getVector3() { return G3D::Vector3(GetPositionX(), GetPositionY(), GetPositionZ()); } @@ -381,11 +371,11 @@ std::string const WorldPosition::print() std::string const WorldPosition::to_string() { std::stringstream out; - out << m_mapId << '|'; - out << m_positionX << '|'; - out << m_positionY << '|'; - out << m_positionZ << '|'; - out << m_orientation; + out << GetMapId() << '|'; + out << GetPositionX() << '|'; + out << GetPositionY() << '|'; + out << GetPositionZ() << '|'; + out << GetOrientation(); return out.str(); } @@ -429,11 +419,14 @@ void WorldPosition::printWKT(std::vector points, std::ostringstre WorldPosition WorldPosition::getDisplayLocation() { - WorldPosition pos = TravelNodeMap::instance().getMapOffset(getMapId()); + WorldPosition pos = TravelNodeMap::instance().getMapOffset(GetMapId()); return offset(const_cast(&pos)); } -uint16 WorldPosition::getAreaId() { return sMapMgr->GetAreaId(PHASEMASK_NORMAL, getMapId(), getX(), getY(), getZ()); } +uint16 WorldPosition::getAreaId() +{ + return sMapMgr->GetAreaId(PHASEMASK_NORMAL, GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ()); +} AreaTableEntry const* WorldPosition::getArea() { @@ -448,7 +441,7 @@ std::string const WorldPosition::getAreaName(bool fullName, bool zoneName) { if (!isOverworld()) { - MapEntry const* map = sMapStore.LookupEntry(getMapId()); + MapEntry const* map = sMapStore.LookupEntry(GetMapId()); if (map) return map->name[0]; } @@ -545,7 +538,7 @@ std::vector WorldPosition::fromGridCoord(GridCoord gridCoord) if (d == 2 || d == 3) g.inc_y(1); - retVec.push_back(WorldPosition(getMapId(), g)); + retVec.push_back(WorldPosition(GetMapId(), g)); } return retVec; @@ -566,7 +559,7 @@ std::vector WorldPosition::fromCellCoord(CellCoord cellcoord) if (d == 2 || d == 3) p.inc_y(1); - retVec.push_back(WorldPosition(getMapId(), p)); + retVec.push_back(WorldPosition(GetMapId(), p)); } return retVec; } @@ -618,7 +611,7 @@ std::vector WorldPosition::frommGridCoord(mGridCoord GridCoord) if (d == 2 || d == 3) g.first++; - retVec.push_back(WorldPosition(getMapId(), g)); + retVec.push_back(WorldPosition(GetMapId(), g)); } return retVec; @@ -706,7 +699,7 @@ void WorldPosition::loadMapAndVMaps(WorldPosition secondPos) { for (auto& grid : getmGridCoords(secondPos)) { - loadMapAndVMap(getMapId(), grid.first, grid.second); + loadMapAndVMap(GetMapId(), grid.first, grid.second); } } @@ -714,7 +707,7 @@ std::vector WorldPosition::fromPointsArray(std::vector retVec; for (auto p : path) - retVec.push_back(WorldPosition(getMapId(), p.x, p.y, p.z, getO())); + retVec.push_back(WorldPosition(GetMapId(), p.x, p.y, p.z, GetOrientation())); return retVec; } @@ -729,7 +722,7 @@ std::vector WorldPosition::getPathStepFrom(WorldPosition startPos loadMapAndVMaps(startPos); PathGenerator path(bot); - path.CalculatePath(startPos.getX(), startPos.getY(), startPos.getZ()); + path.CalculatePath(startPos.GetPositionX(), startPos.GetPositionY(), startPos.GetPositionZ()); Movement::PointsArray points = path.GetPath(); PathType type = path.GetPathType(); @@ -785,7 +778,7 @@ std::vector WorldPosition::getPathFromPath(std::vector subPath, fullPath = startPath; @@ -818,10 +811,18 @@ bool WorldPosition::GetReachableRandomPointOnGround(Player* bot, float radius, b { radius *= randomRange ? rand_norm() : 1.f; float angle = rand_norm() * static_cast(2 * M_PI); - m_positionX += radius * cosf(angle); - m_positionY += radius * sinf(angle); - - return getMap()->CanReachPositionAndGetValidCoords(bot, m_positionX, m_positionY, m_positionZ); + setX(GetPositionX() + radius * cosf(angle)); + setY(GetPositionY() + radius * sinf(angle)); + + float x = GetPositionX(); + float y = GetPositionY(); + float z = GetPositionZ(); + bool canReach = getMap()->CanReachPositionAndGetValidCoords(bot, x, y, z); + setX(x); + setY(y); + setZ(z); + + return canReach; } uint32 WorldPosition::getUnitsAggro(GuidVector& units, Player* bot) @@ -844,7 +845,7 @@ uint32 WorldPosition::getUnitsAggro(GuidVector& units, Player* bot) void FindPointCreatureData::operator()(CreatureData const& creatureData) { if (!entry || creatureData.id1 == entry) - if ((!point || creatureData.mapid == point.getMapId()) && + if ((!point || creatureData.mapid == point.GetMapId()) && (!radius || point.sqDistance(WorldPosition(creatureData.mapid, creatureData.posX, creatureData.posY, creatureData.posZ)) < radius * radius)) { @@ -855,7 +856,7 @@ void FindPointCreatureData::operator()(CreatureData const& creatureData) void FindPointGameObjectData::operator()(GameObjectData const& gameobjectData) { if (!entry || gameobjectData.id == entry) - if ((!point || gameobjectData.mapid == point.getMapId()) && + if ((!point || gameobjectData.mapid == point.GetMapId()) && (!radius || point.sqDistance(WorldPosition(gameobjectData.mapid, gameobjectData.posX, gameobjectData.posY, gameobjectData.posZ)) < radius * radius)) { @@ -3223,7 +3224,8 @@ void TravelMgr::LoadQuestTravelTable() if (loc.second.empty()) continue; - if (!TravelNodeMap::instance().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; @@ -3235,7 +3237,7 @@ void TravelMgr::LoadQuestTravelTable() out << "\"center\"" << ","; - out << points.begin()->getMapId() << ","; + out << points.begin()->GetMapId() << ","; out << points.begin()->getAreaName() << ","; out << points.begin()->getAreaName(true, true) << ","; @@ -3245,7 +3247,7 @@ void TravelMgr::LoadQuestTravelTable() out << "\"area\"" << ","; - out << points.begin()->getMapId() << ","; + out << points.begin()->GetMapId() << ","; out << points.begin()->getAreaName() << ","; out << points.begin()->getAreaName(true, true) << ","; @@ -3613,17 +3615,18 @@ void TravelMgr::LoadQuestTravelTable() if (!pos->getMap()) continue; - float nx = pos->getX() + (x*5)-5000.0f; - float ny = pos->getY() + (y*5)-5000.0f; - float nz = pos->getZ() + 100.0f; + float nx = pos->GetPositionX() + (x * 5) - 5000.0f; + float ny = pos->GetPositionY() + (y * 5) - 5000.0f; + float nz = pos->GetPositionZ() + 100.0f; //pos->getMap()->GetHitPosition(nx, ny, nz + 200.0f, nx, ny, nz, -0.5f); if (!pos->getMap()->GetHeightInRange(nx, ny, nz, 5000.0f)) // GetHeight can fail continue; - WorldPosition npos = WorldPosition(pos->getMapId(), nx, ny, nz, 0.0); - uint32 area = path.getArea(npos.getMapId(), npos.getX(), npos.getY(), npos.getZ()); + WorldPosition npos = WorldPosition(pos->GetMapId(), nx, ny, nz, 0.0); + uint32 area = path.getArea(npos.GetMapId(), npos.GetPositionX(), npos.GetPositionY(), + npos.GetPositionZ()); std::ostringstream out; out << std::fixed << area << "," << npos.getDisplayX() << "," << npos.getDisplayY(); @@ -3647,7 +3650,8 @@ 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->GetPositionX() << "," << j->GetPositionY() << "," << j->GetPositionZ(); + sPlayerbotAIConfig.log(5, out.str().c_str()); } } @@ -4087,8 +4091,8 @@ void TravelMgr::setNullTravelTarget(Player* player) void TravelMgr::addMapTransfer(WorldPosition start, WorldPosition end, float portalDistance, bool makeShortcuts) { - uint32 sMap = start.getMapId(); - uint32 eMap = end.getMapId(); + uint32 sMap = start.GetMapId(); + uint32 eMap = end.GetMapId(); if (sMap == eMap) return; @@ -4121,7 +4125,7 @@ void TravelMgr::addMapTransfer(WorldPosition start, WorldPosition end, float por } // Add actual transfer. - auto mapTransfers = mapTransfersMap.find(std::make_pair(start.getMapId(), end.getMapId())); + auto mapTransfers = mapTransfersMap.find(std::make_pair(start.GetMapId(), end.GetMapId())); if (mapTransfers == mapTransfersMap.end()) mapTransfersMap.insert({{sMap, eMap}, {mapTransfer(start, end, portalDistance)}}); @@ -4142,8 +4146,8 @@ void TravelMgr::loadMapTransfers() float TravelMgr::mapTransDistance(WorldPosition start, WorldPosition end) { - uint32 sMap = start.getMapId(); - uint32 eMap = end.getMapId(); + uint32 sMap = start.GetMapId(); + uint32 eMap = end.GetMapId(); if (sMap == eMap) return start.distance(end); @@ -4167,8 +4171,8 @@ float TravelMgr::mapTransDistance(WorldPosition start, WorldPosition end) float TravelMgr::fastMapTransDistance(WorldPosition start, WorldPosition end) { - uint32 sMap = start.getMapId(); - uint32 eMap = end.getMapId(); + uint32 sMap = start.GetMapId(); + uint32 eMap = end.GetMapId(); if (sMap == eMap) return start.fDist(end); diff --git a/src/Mgr/Travel/TravelMgr.h b/src/Mgr/Travel/TravelMgr.h index 68542a7d07..7e2a40b160 100644 --- a/src/Mgr/Travel/TravelMgr.h +++ b/src/Mgr/Travel/TravelMgr.h @@ -120,12 +120,6 @@ class WorldPosition : public WorldLocation WorldPosition& operator+=(WorldPosition const& p1); WorldPosition& operator-=(WorldPosition const& p1); - uint32 getMapId(); - float getX(); - float getY(); - float getZ(); - float getO(); - G3D::Vector3 getVector3(); std::string const print(); @@ -185,29 +179,29 @@ class WorldPosition : public WorldLocation // Quick square distance in 2d plane. float sqDistance2d(WorldPosition center) { - return (getX() - center.getX()) * (getX() - center.getX()) + - (getY() - center.getY()) * (getY() - center.getY()); + return (GetPositionX() - center.GetPositionX()) * (GetPositionX() - center.GetPositionX()) + + (GetPositionY() - center.GetPositionY()) * (GetPositionY() - center.GetPositionY()); } // Quick square distance calculation without map check. Used for getting the minimum distant points. float sqDistance(WorldPosition center) { - return (getX() - center.getX()) * (getX() - center.getX()) + - (getY() - center.getY()) * (getY() - center.getY()) + - (getZ() - center.getZ()) * (getZ() - center.getZ()); + return (GetPositionX() - center.GetPositionX()) * (GetPositionX() - center.GetPositionX()) + + (GetPositionY() - center.GetPositionY()) * (GetPositionY() - center.GetPositionY()) + + (GetPositionZ() - center.GetPositionZ()) * (GetPositionZ() - center.GetPositionZ()); } float sqDistance2d(WorldPosition* center) { - return (getX() - center->getX()) * (getX() - center->getX()) + - (getY() - center->getY()) * (getY() - center->getY()); + return (GetPositionX() - center->GetPositionX()) * (GetPositionX() - center->GetPositionX()) + + (GetPositionY() - center->GetPositionY()) * (GetPositionY() - center->GetPositionY()); } float sqDistance(WorldPosition* center) { - return (getX() - center->getX()) * (getX() - center->getX()) + - (getY() - center->getY()) * (getY() - center->getY()) + - (getZ() - center->getZ()) * (getZ() - center->getZ()); + return (GetPositionX() - center->GetPositionX()) * (GetPositionX() - center->GetPositionX()) + + (GetPositionY() - center->GetPositionY()) * (GetPositionY() - center->GetPositionY()) + + (GetPositionZ() - center->GetPositionZ()) * (GetPositionZ() - center->GetPositionZ()); } // Returns the closest point of the list. Fast but only works for the same map. @@ -227,7 +221,7 @@ class WorldPosition : public WorldLocation float getAngleTo(WorldPosition endPos) { - float ang = atan2(endPos.getY() - getY(), endPos.getX() - getX()); + float ang = atan2(endPos.GetPositionY() - GetPositionY(), endPos.GetPositionX() - GetPositionX()); return (ang >= 0) ? ang : 2 * static_cast(M_PI) + ang; } @@ -238,7 +232,8 @@ class WorldPosition : public WorldLocation float mSign(WorldPosition* p1, WorldPosition* p2) { - return (getX() - p2->getX()) * (p1->getY() - p2->getY()) - (p1->getX() - p2->getX()) * (getY() - p2->getY()); + return (GetPositionX() - p2->GetPositionX()) * (p1->GetPositionY() - p2->GetPositionY()) - + (p1->GetPositionX() - p2->GetPositionX()) * (GetPositionY() - p2->GetPositionY()); } bool isInside(WorldPosition* p1, WorldPosition* p2, WorldPosition* p3); @@ -251,18 +246,23 @@ class WorldPosition : public WorldLocation std::set getTransports(uint32 entry = 0); - GridCoord getGridCoord() { return Acore::ComputeGridCoord(getX(), getY()); }; + CellCoord getCellCoord() { return Acore::ComputeCellCoord(GetPositionX(), GetPositionY()); } + GridCoord getGridCoord() + { + CellCoord cellCoord = getCellCoord(); + Cell cell(cellCoord); + return GridCoord(cell.GridX(), cell.GridY()); + } std::vector getGridCoord(WorldPosition secondPos); std::vector fromGridCoord(GridCoord GridCoord); - CellCoord getCellCoord() { return Acore::ComputeCellCoord(getX(), getY()); } std::vector fromCellCoord(CellCoord cellCoord); std::vector gridFromCellCoord(CellCoord cellCoord); mGridCoord getmGridCoord() { - return std::make_pair((int32)(CENTER_GRID_ID - getX() / SIZE_OF_GRIDS), - (int32)(CENTER_GRID_ID - getY() / SIZE_OF_GRIDS)); + return std::make_pair((int32)(CENTER_GRID_ID - GetPositionX() / SIZE_OF_GRIDS), + (int32)(CENTER_GRID_ID - GetPositionY() / SIZE_OF_GRIDS)); } std::vector getmGridCoords(WorldPosition secondPos); @@ -270,15 +270,15 @@ class WorldPosition : public WorldLocation void loadMapAndVMap(uint32 mapId, uint8 x, uint8 y); - void loadMapAndVMap() { loadMapAndVMap(getMapId(), getmGridCoord().first, getmGridCoord().second); } + void loadMapAndVMap() { loadMapAndVMap(GetMapId(), getmGridCoord().first, getmGridCoord().second); } void loadMapAndVMaps(WorldPosition secondPos); // Display functions WorldPosition getDisplayLocation(); - float getDisplayX() { return getDisplayLocation().getY() * -1.0; } + float getDisplayX() { return getDisplayLocation().GetPositionY() * -1.0; } - float getDisplayY() { return getDisplayLocation().getX(); } + float getDisplayY() { return getDisplayLocation().GetPositionX(); } uint16 getAreaId(); AreaTableEntry const* getArea(); @@ -334,11 +334,11 @@ class WorldPosition : public WorldLocation inline ByteBuffer& operator<<(ByteBuffer& b, WorldPosition& guidP) { - b << guidP.getMapId(); - b << guidP.getX(); - b << guidP.getY(); - b << guidP.getZ(); - b << guidP.getO(); + b << guidP.GetMapId(); + b << guidP.GetPositionX(); + b << guidP.GetPositionY(); + b << guidP.GetPositionZ(); + b << guidP.GetOrientation(); b << guidP.getVisitors(); return b; } @@ -461,9 +461,9 @@ class mapTransfer { } - bool isFrom(WorldPosition point) { return point.getMapId() == pointFrom.getMapId(); } + bool isFrom(WorldPosition point) { return point.GetMapId() == pointFrom.GetMapId(); } - bool isTo(WorldPosition point) { return point.getMapId() == pointTo.getMapId(); } + bool isTo(WorldPosition point) { return point.GetMapId() == pointTo.GetMapId(); } WorldPosition* getPointFrom() { return &pointFrom; } @@ -543,7 +543,7 @@ class TravelDestination WorldPosition* nearestPoint(WorldPosition* pos); float distanceTo(WorldPosition* pos) { return nearestPoint(pos)->distance(pos); } - bool onMap(WorldPosition* pos) { return nearestPoint(pos)->getMapId() == pos->getMapId(); } + bool onMap(WorldPosition* pos) { return nearestPoint(pos)->GetMapId() == pos->GetMapId(); } virtual bool isIn(WorldPosition* pos, float radius = 0.f) { return onMap(pos) && distanceTo(pos) <= (radius ? radius : radiusMin); diff --git a/src/Mgr/Travel/TravelNode.cpp b/src/Mgr/Travel/TravelNode.cpp index 0cb88f9b11..bdf9cc9525 100644 --- a/src/Mgr/Travel/TravelNode.cpp +++ b/src/Mgr/Travel/TravelNode.cpp @@ -81,7 +81,7 @@ void TravelNodePath::calculateCost(bool distanceOnly) } } - if (lastPoint && point.getMapId() == lastPoint.getMapId()) + if (lastPoint && point.GetMapId() == lastPoint.GetMapId()) { if (!distanceOnly && (point.isInWater() || lastPoint.isInWater())) swimDistance += point.distance(lastPoint); @@ -687,7 +687,7 @@ bool TravelPath::makeShortCut(WorldPosition startPos, float maxDist) // if (p.point.getMapId() != startPos.getMapId()) // continue; - if (p.point.getMapId() == startPos.getMapId()) + if (p.point.GetMapId() == startPos.GetMapId()) { float curDist = p.point.sqDistance(startPos); @@ -722,7 +722,7 @@ bool TravelPath::makeShortCut(WorldPosition startPos, float maxDist) newPath.push_back(p); } - if (newPath.empty() || minDist > maxDistSq || newPath.front().point.getMapId() != startPos.getMapId()) + if (newPath.empty() || minDist > maxDistSq || newPath.front().point.GetMapId() != startPos.GetMapId()) { clear(); return false; @@ -800,7 +800,7 @@ bool TravelPath::shouldMoveToNextPoint(WorldPosition startPos, std::vectorpoint.distance(nextP->point); - if (p->point.getMapId() != startPos.getMapId() || + if (p->point.GetMapId() != startPos.GetMapId() || ((moveDist + nextMove > maxDist || startPos.distance(nextP->point) > maxDist) && moveDist > 0)) { return false; @@ -827,7 +827,7 @@ WorldPosition TravelPath::getNextPoint(WorldPosition startPos, float maxDist, Tr // Get the closest point on the path to start from. for (auto p = startP; p != ed; p++) { - if (p->point.getMapId() != startPos.getMapId()) + if (p->point.GetMapId() != startPos.GetMapId()) continue; float curDist = p->point.distance(startPos); @@ -1126,7 +1126,7 @@ std::vector TravelNodeMap::getNodes(WorldPosition pos, float range) for (auto& node : m_nodes) { - if (node->getMapId() == pos.getMapId()) + if (node->getMapId() == pos.GetMapId()) if (range == -1 || node->getDistance(pos) <= range) retVec.push_back(node); } @@ -1787,8 +1787,9 @@ void TravelNodeMap::generateTransportNodes() float dy = -1 * p.second->Y; WorldPosition pos = - WorldPosition(basePos.getMapId(), basePos.getX() + dx, basePos.getY() + dy, - basePos.getZ() + p.second->Z, basePos.getO()); + WorldPosition(basePos.GetMapId(), basePos.GetPositionX() + dx, + basePos.GetPositionY() + dy, basePos.GetPositionZ() + p.second->Z, + basePos.GetOrientation()); if (prevNode) { @@ -1830,8 +1831,9 @@ void TravelNodeMap::generateTransportNodes() float dx = -1 * p.second->X; float dy = -1 * p.second->Y; WorldPosition pos = - WorldPosition(basePos.getMapId(), basePos.getX() + dx, basePos.getY() + dy, - basePos.getZ() + p.second->Z, basePos.getO()); + WorldPosition(basePos.GetMapId(), basePos.GetPositionX() + dx, + basePos.GetPositionY() + dy, basePos.GetPositionZ() + p.second->Z, + basePos.GetOrientation()); ppath.push_back(pos); @@ -2356,10 +2358,10 @@ void TravelNodeMap::saveNodeStore() stmt->SetData(0, i); stmt->SetData(1, saveNodes.find(link.first)->second); stmt->SetData(2, j); - stmt->SetData(3, point.getMapId()); - stmt->SetData(4, point.getX()); - stmt->SetData(5, point.getY()); - stmt->SetData(6, point.getZ()); + stmt->SetData(3, point.GetMapId()); + stmt->SetData(4, point.GetPositionX()); + stmt->SetData(5, point.GetPositionY()); + stmt->SetData(6, point.GetPositionZ()); trans->Append(stmt); points++; @@ -2516,10 +2518,10 @@ void TravelNodeMap::calcMapOffset() } else { - min.back().setX(std::min(min.back().getX(), node->getX())); - min.back().setY(std::min(min.back().getY(), node->getY())); - max.back().setX(std::max(max.back().getX(), node->getX())); - max.back().setY(std::max(max.back().getY(), node->getY())); + min.back().setX(std::min(min.back().GetPositionX(), node->getX())); + min.back().setY(std::min(min.back().GetPositionY(), node->getY())); + max.back().setX(std::max(max.back().GetPositionX(), node->getX())); + max.back().setY(std::max(max.back().GetPositionY(), node->getY())); } } } @@ -2533,14 +2535,15 @@ void TravelNodeMap::calcMapOffset() for (auto& mapId : mapIds) { mapOffsets.push_back(std::make_pair( - mapId, WorldPosition(mapId, curPos.getX() - min[i].getX(), curPos.getY() - max[i].getY(), 0, 0))); + mapId, WorldPosition(mapId, curPos.GetPositionX() - min[i].GetPositionX(), + curPos.GetPositionY() - max[i].GetPositionY(), 0, 0))); - maxY = std::max(maxY, (max[i].getY() - min[i].getY() + 500)); - curPos.setX(curPos.getX() + (max[i].getX() - min[i].getX() + 500)); + maxY = std::max(maxY, (max[i].GetPositionY() - min[i].GetPositionY() + 500)); + curPos.setX(curPos.GetPositionX() + (max[i].GetPositionX() - min[i].GetPositionX() + 500)); - if (curPos.getX() > endPos.getX()) + if (curPos.GetPositionX() > endPos.GetPositionX()) { - curPos.setY(curPos.getY() - maxY); + curPos.setY(curPos.GetPositionY() - maxY); curPos.setX(-13000); } diff --git a/src/Mgr/Travel/TravelNode.h b/src/Mgr/Travel/TravelNode.h index 41419f8a97..4dc2357216 100644 --- a/src/Mgr/Travel/TravelNode.h +++ b/src/Mgr/Travel/TravelNode.h @@ -251,11 +251,11 @@ class TravelNode } // WorldLocation shortcuts - uint32 getMapId() { return point.getMapId(); } - float getX() { return point.getX(); } - float getY() { return point.getY(); } - float getZ() { return point.getZ(); } - float getO() { return point.getO(); } + uint32 getMapId() { return point.GetMapId(); } + float getX() { return point.GetPositionX(); } + float getY() { return point.GetPositionY(); } + float getZ() { return point.GetPositionZ(); } + float getO() { return point.GetOrientation(); } float getDistance(WorldPosition pos) { return point.distance(pos); } float getDistance(TravelNode* node) { return point.distance(node->getPosition()); } float fDist(TravelNode* node) { return point.fDist(node->getPosition()); } From 9748e36ad67df1ddb4d15518b0229ae37b07c85d Mon Sep 17 00:00:00 2001 From: Crow Date: Fri, 13 Feb 2026 11:25:12 -0600 Subject: [PATCH 19/25] Fix potential failure of Magtheridon cube clickers to engage in combat (#2129) # Pull Request I noticed a problem that has always existed with the Magtheridon strategy but just never came up for me due to chance. Cube clicker logic is based on a timer that resets after every Blast Nova. If the timer is not reset, the cubes will still be clicked, but the clickers will do nothing but wait to click on the cubes instead of resuming combat between Blast Novas. Because tracking of the Blast Nova state happens during the cube clicking sequence, if a cube clicker is assigned the singular role to track Blast Nova state (which is done simply by returning the first DPS bot found), then the timer will not be reset. This whole strategy needs a refactor, but the simple fix for this problem for now is just to remove the role check for tracking the Blast Nova state. I tested the fix, and it works. --- ## 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? - - [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: - - [ ] Lightweight mode remains the default - - [ ] More complex behavior is optional and thereby configurable --- ## AI Assistance Was AI assistance (e.g. ChatGPT or similar tools) used while working on this change? - - [X] No - - [ ] Yes (**explain below**) If yes, please specify: - 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. --- src/Ai/Raid/Magtheridon/Action/RaidMagtheridonActions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ai/Raid/Magtheridon/Action/RaidMagtheridonActions.cpp b/src/Ai/Raid/Magtheridon/Action/RaidMagtheridonActions.cpp index dab7efab97..7f1ad5b859 100644 --- a/src/Ai/Raid/Magtheridon/Action/RaidMagtheridonActions.cpp +++ b/src/Ai/Raid/Magtheridon/Action/RaidMagtheridonActions.cpp @@ -640,7 +640,7 @@ bool MagtheridonManageTimersAndAssignmentsAction::Execute(Event event) magtheridon->FindCurrentSpellBySpellId(SPELL_BLAST_NOVA); bool lastBlastNova = lastBlastNovaState[instanceId]; - if (lastBlastNova && !blastNovaActive && IsMechanicTrackerBot(botAI, bot, MAGTHERIDON_MAP_ID, nullptr)) + if (lastBlastNova && !blastNovaActive) blastNovaTimer[instanceId] = now; lastBlastNovaState[instanceId] = blastNovaActive; From 25800f54e8c0b40eee1933636d6aaf1d904da57d Mon Sep 17 00:00:00 2001 From: NoxMax <50133316+NoxMax@users.noreply.github.com> Date: Fri, 13 Feb 2026 10:31:55 -0700 Subject: [PATCH 20/25] Fix/Feat: PVP with master and PVP probablity system (thread-safe remake) (#2008) This is a remake of #1914 that had to be reverted. Original PR had a thread-safe issue where a crash happens if multiple threads access the cache at the same time. Unfortunately this problem was not caught in earlier testing. I don't know if because I was testing on a month old branch, if my settings had only ~2000, or if I needed test runs longer than an hour to find out. Regardless, this has all been addressed. Test have been run on the latest commits from today (2026/1/11), with all 7500 of my bots active, with a test run that lasted 15 hours. All stable and bots are following the probability system without issue. ~~The new edit uses mutex locking, preventing simultaneous access of the cache by multiple threads.~~ The new edit uses deterministic hashing, thereby not having issues with cache thread safety to begin with. Thank you @hermensbas for catching and reverting the original problem PR. Apologies for not catching the issue myself. --- Original PR description: There are two related PVP components in this PR. First is the simple yet fundamental change to bot behaviour when they are in party. Right now bots with a master will go into PVP when there's a nearby PVP target, even if master is not in PVP. This absolutely should not happen. Bots should not consider PVP at all if master is not in PVP. The fix is only 3 lines in EnemyPlayerValue The second component is introducing PVP probabilities, to make decisions more realistic. Right now even a level 1 bot will 100% go into PVP if it sees a level 80 PVP target. They can't help themselves. So the change here addresses that insanity. Several thresholds (subject to community review) are introduced: 1. Bots will not fight a target 5 or more levels higher than them 2. Bots have a 25% chance starting a fight with a target +/- 4 levels from them. 3. Bots have a 50% chance starting a fight with a target +/- 3 levels from them. 4. Bots have a 75% chance starting a fight with a target +/- 2 levels from them. 5. Bots have a 100% chance starting a fight with a target +/- 1 level from them. 6. Bots have a 25% chance starting a fight with a target 5 or more levels below them (ganking. thought it would be funny, and technically realistic of player behaviour) Exception of course exist for BG/Arena/Duel, and in capitals where bots will always PVP. Also bots will always defend themselves if attacked. Few notes: 1. The if/ else if logic can be further simplified, but only if we use thresholds that are different by one. So current logic allows for flexibility of using values like 10/7/5/3 instead of 5/4/3/2. 2. The caching system is per-bot basis. So for some target X, if some bot decides to attack it, another bot will make its own decision. At first I used a simplified global system (thinking there might be performance concerns) where if one bot decides to attack a target then they all do, but when I switched to the more realistic per-bot basis, I didn't see an effect on performance. 3. Variables are obviously not configurable right now. I'm starting to see Bash's POV that maybe we have too many configs :grimacing: Still, they can be easily exposed in the future, and if someone is reading this then, remember to change constexpr to const. --------- Co-authored-by: bashermens <31279994+hermensbas@users.noreply.github.com> --- src/Ai/Base/Value/EnemyPlayerValue.cpp | 11 ++ src/Ai/Base/Value/PossibleTargetsValue.cpp | 117 ++++++++++++++++++++- 2 files changed, 123 insertions(+), 5 deletions(-) diff --git a/src/Ai/Base/Value/EnemyPlayerValue.cpp b/src/Ai/Base/Value/EnemyPlayerValue.cpp index 7de0cd670a..c2f6e056a2 100644 --- a/src/Ai/Base/Value/EnemyPlayerValue.cpp +++ b/src/Ai/Base/Value/EnemyPlayerValue.cpp @@ -11,6 +11,10 @@ bool NearestEnemyPlayersValue::AcceptUnit(Unit* unit) { + // Apply parent's filtering first (includes level difference checks) + if (!PossibleTargetsValue::AcceptUnit(unit)) + return false; + bool inCannon = botAI->IsInVehicle(false, true); Player* enemy = dynamic_cast(unit); if (enemy && botAI->IsOpposing(enemy) && enemy->IsPvP() && @@ -19,7 +23,14 @@ bool NearestEnemyPlayersValue::AcceptUnit(Unit* unit) ((inCannon || !enemy->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE))) && /*!enemy->HasStealthAura() && !enemy->HasInvisibilityAura()*/ enemy->CanSeeOrDetect(bot) && !(enemy->HasSpiritOfRedemptionAura())) + { + // If with master, only attack if master is PvP flagged + Player* master = botAI->GetMaster(); + if (master && !master->IsPvP() && !master->IsFFAPvP()) + return false; + return true; + } return false; } diff --git a/src/Ai/Base/Value/PossibleTargetsValue.cpp b/src/Ai/Base/Value/PossibleTargetsValue.cpp index 654abd44c4..7db1dd39ba 100644 --- a/src/Ai/Base/Value/PossibleTargetsValue.cpp +++ b/src/Ai/Base/Value/PossibleTargetsValue.cpp @@ -16,6 +16,20 @@ #include "SpellAuraEffects.h" #include "SpellMgr.h" #include "Unit.h" +#include "AreaDefines.h" + +// Level difference thresholds for attack probability +constexpr int32 EXTREME_LEVEL_DIFF = 5; // Don't attack if enemy is this much higher +constexpr int32 HIGH_LEVEL_DIFF = 4; // 25% chance at +/- this difference +constexpr int32 MID_LEVEL_DIFF = 3; // 50% chance at +/- this difference +constexpr int32 LOW_LEVEL_DIFF = 2; // 75% chance at +/- this difference + +// Time window for deterministic attack decisions +constexpr uint32 ATTACK_DECISION_TIME_WINDOW = 2 * MINUTE; + +// 64 bit FNV-1a hash constants +constexpr uint64_t FNV_OFFSET_BASIS = 14695981039346656037ULL; +constexpr uint64_t FNV_PRIME = 1099511628211ULL; void PossibleTargetsValue::FindUnits(std::list& targets) { @@ -24,7 +38,103 @@ void PossibleTargetsValue::FindUnits(std::list& targets) Cell::VisitObjects(bot, searcher, range); } -bool PossibleTargetsValue::AcceptUnit(Unit* unit) { return AttackersValue::IsPossibleTarget(unit, bot, range); } +bool PossibleTargetsValue::AcceptUnit(Unit* unit) +{ + if (!AttackersValue::IsPossibleTarget(unit, bot, range)) + return false; + + // Level-based PvP restrictions + if (unit->IsPlayer()) + { + // Self-defense - always allow fighting back + if (bot->IsInCombat() && bot->GetVictim() == unit) + return true; // Already fighting + + Unit* botAttacker = bot->getAttackerForHelper(); + if (botAttacker) + { + if (botAttacker == unit) + return true; // Enemy attacking + + if (botAttacker->IsPet()) + { + Unit* petOwner = botAttacker->GetOwner(); + if (petOwner && petOwner == unit) + return true; // Enemy's pet attacking + } + } + + // Skip restrictions in BG/Arena + if (bot->InBattleground() || bot->InArena()) + return true; + + // Skip restrictions if in duel with this player + if (bot->duel && bot->duel->Opponent == unit) + return true; + + // Capital cities - no restrictions + uint32 zoneId = bot->GetZoneId(); + bool inCapitalCity = (zoneId == AREA_STORMWIND_CITY || + zoneId == AREA_IRONFORGE || + zoneId == AREA_DARNASSUS || + zoneId == AREA_THE_EXODAR || + zoneId == AREA_ORGRIMMAR || + zoneId == AREA_THUNDER_BLUFF || + zoneId == AREA_UNDERCITY || + zoneId == AREA_SILVERMOON_CITY); + + if (inCapitalCity) + return true; + + // Level difference check + int32 levelDifference = unit->GetLevel() - bot->GetLevel(); + int32 absLevelDifference = std::abs(levelDifference); + + // Extreme difference - do not attack + if (levelDifference >= EXTREME_LEVEL_DIFF) + return false; + + // Calculate attack chance based on level difference + uint32 attackChance = 100; // Default 100%: Bot and target's levels are very close + + // There's a chance a bot might gank on an extremly low target + if ((absLevelDifference < EXTREME_LEVEL_DIFF && absLevelDifference >= HIGH_LEVEL_DIFF) || + levelDifference <= -EXTREME_LEVEL_DIFF) + attackChance = 25; + + else if (absLevelDifference < HIGH_LEVEL_DIFF && absLevelDifference >= MID_LEVEL_DIFF) + attackChance = 50; + + else if (absLevelDifference < MID_LEVEL_DIFF && absLevelDifference >= LOW_LEVEL_DIFF) + attackChance = 75; + + // If probability check needed, use deterministic hash-based decision + if (attackChance < 100) + { + // Decisions remain stable for ATTACK_DECISION_TIME_WINDOW. + time_t timeWindow = time(nullptr) / ATTACK_DECISION_TIME_WINDOW; + + // FNV-1a hash used to deterministically convert botGUID, targetGUID, and timeWindow + // into a consistent percentage chance without needing to cache previous decisions. + // See: http://www.isthe.com/chongo/tech/comp/fnv/ + uint64_t hash = FNV_OFFSET_BASIS; + + // Diffuse bot GUID, target GUID, and time window into the hash + hash ^= bot->GetGUID().GetRawValue(); + hash *= FNV_PRIME; + hash ^= unit->GetGUID().GetRawValue(); + hash *= FNV_PRIME; + hash ^= static_cast(timeWindow); + hash *= FNV_PRIME; + + // Convert hash to 0-99 range and compare against attack chance percentage. + // Ex: attackChance=75: hash 0-74 = attack (75%), hash 75-99 = don't attack (25%) + return (hash % 100) < attackChance; + } + } + + return true; +} void PossibleTriggersValue::FindUnits(std::list& targets) { @@ -36,9 +146,8 @@ void PossibleTriggersValue::FindUnits(std::list& targets) bool PossibleTriggersValue::AcceptUnit(Unit* unit) { if (!unit->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE)) - { return false; - } + Unit::AuraEffectList const& aurasPeriodicTriggerSpell = unit->GetAuraEffectsByType(SPELL_AURA_PERIODIC_TRIGGER_SPELL); Unit::AuraEffectList const& aurasPeriodicTriggerWithValueSpell = @@ -58,9 +167,7 @@ bool PossibleTriggersValue::AcceptUnit(Unit* unit) for (int j = 0; j < MAX_SPELL_EFFECTS; j++) { if (triggerSpellInfo->Effects[j].Effect == SPELL_EFFECT_SCHOOL_DAMAGE) - { return true; - } } } } From 80aeeda0e84b708c8bdb433e1965a3704187a508 Mon Sep 17 00:00:00 2001 From: bashermens <31279994+hermensbas@users.noreply.github.com> Date: Sat, 14 Feb 2026 20:31:45 +0100 Subject: [PATCH 21/25] Flying, waterwalking, swimming movement optimizations and transition fixes. (#2134) # Pull Request **Fixes and optimizations for flying, water walking, swimming**: * optimized triggers * ensuring movement flag updates only happen between actual transitions states * fly bug fix; fly with bots following with stay command midair, fly down and dismount yourself, follow command and now the bots fall instead of lingering around in the air) * updated z-axes correction for water walking and bots (for real players this is handled client-side) * added lift off movement for more stabile transition from ground(level) to flying **Tested**: * Test all transitions; water walk, swimming, swimming, walking, mounting while water walking etc. * Flying with bots and fly master routes * Movement flag updates only occur during transitions **Known issues**: transition between water walking, swimming and back again, in most cases the bots will stay under the waterline instead of jumping on the z axes on water level. (will fix that another time) --- ## 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 Apply water walking effect on your bots, shaman or dk, and test all possible transitions and follow actions of the bots. water walking, swim, walk on land, swimming and walk without water walking effect/aura, fly mount from water, from ground, etc. ## 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 - - [x] More complex behavior is optional and thereby configurable --- ## AI Assistance Was AI assistance (e.g. ChatGPT or similar tools) used while working on this change? - - [x] No - - [ ] Yes (**explain below**) If yes, please specify: - 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 - - [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. --- src/Ai/Base/Actions/MovementActions.cpp | 78 +++++++++++++------------ 1 file changed, 40 insertions(+), 38 deletions(-) diff --git a/src/Ai/Base/Actions/MovementActions.cpp b/src/Ai/Base/Actions/MovementActions.cpp index d4090c0ef2..4f04d17995 100644 --- a/src/Ai/Base/Actions/MovementActions.cpp +++ b/src/Ai/Base/Actions/MovementActions.cpp @@ -953,68 +953,70 @@ bool MovementAction::Follow(Unit* target, float distance) { return Follow(target void MovementAction::UpdateMovementState() { - const bool isCurrentlyRestricted = // see if the bot is currently slowed, rooted, or otherwise unable to move - bot->HasUnitState(UNIT_STATE_LOST_CONTROL) || - bot->IsRooted() || - bot->isFrozen() || - bot->IsPolymorphed(); + const bool isCurrentlyRestricted = // see if the bot is currently slowed, rooted, or otherwise unable to move + bot->HasUnitState(UNIT_STATE_LOST_CONTROL) || bot->IsRooted() || bot->isFrozen() || bot->IsPolymorphed(); // no update movement flags while movement is current restricted. if (!isCurrentlyRestricted && bot->IsAlive()) { // state flags - const auto master = botAI ? botAI->GetMaster() : nullptr; // real player or not - const bool masterIsFlying = master ? master->HasUnitMovementFlag(MOVEMENTFLAG_FLYING) : true; - const bool masterIsSwimming = master ? master->HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING) : true; - const auto liquidState = bot->GetLiquidData().Status; // default LIQUID_MAP_NO_WATER + const auto master = botAI ? botAI->GetMaster() : nullptr; + const auto liquidState = bot->GetLiquidData().Status; const float gZ = bot->GetMapWaterOrGroundLevel(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ()); - const bool wantsToFly = bot->HasIncreaseMountedFlightSpeedAura() || bot->HasFlyAura(); + const bool onGroundZ = bot->GetPositionZ() < gZ + 1.f; + const bool canSwim = liquidState == LIQUID_MAP_IN_WATER || liquidState == LIQUID_MAP_UNDER_WATER; + const bool canFly = bot->HasIncreaseMountedFlightSpeedAura() || bot->HasFlyAura(); + const bool canWaterWalk = bot->HasWaterWalkAura(); + const bool isMasterFlying = master ? master->HasUnitMovementFlag(MOVEMENTFLAG_FLYING) : true; + const bool isMasterSwimming = master ? master->HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING) : true; const bool isFlying = bot->HasUnitMovementFlag(MOVEMENTFLAG_FLYING); - const bool isWaterArea = liquidState != LIQUID_MAP_NO_WATER; - const bool isUnderWater = liquidState == LIQUID_MAP_UNDER_WATER; - const bool isInWater = liquidState == LIQUID_MAP_IN_WATER; - const bool isWaterWalking = bot->HasUnitMovementFlag(MOVEMENTFLAG_WATERWALKING); const bool isSwimming = bot->HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING); - const bool wantsToWaterWalk = bot->HasWaterWalkAura(); - const bool wantsToSwim = isInWater || isUnderWater; - const bool onGroundZ = (bot->GetPositionZ() < gZ + 1.f) && !isWaterArea; + const bool isWaterWalking = bot->HasUnitMovementFlag(MOVEMENTFLAG_WATERWALKING); bool movementFlagsUpdated = false; - // handle water state - if (isWaterArea && !isFlying) + // handle water (fragile logic do not alter without testing every detail, animation and transition) + if (liquidState != LIQUID_MAP_NO_WATER && !isFlying) { - // water walking - if (wantsToWaterWalk && !isWaterWalking && !masterIsSwimming) + if (canWaterWalk && !isMasterSwimming && !isWaterWalking) { - bot->RemoveUnitMovementFlag(MOVEMENTFLAG_SWIMMING); + bot->SetSwim(false); bot->AddUnitMovementFlag(MOVEMENTFLAG_WATERWALKING); movementFlagsUpdated = true; } - // swimming - else if (wantsToSwim && !isSwimming && masterIsSwimming) + else if ((!canWaterWalk || isMasterSwimming) && isWaterWalking) { bot->RemoveUnitMovementFlag(MOVEMENTFLAG_WATERWALKING); - bot->AddUnitMovementFlag(MOVEMENTFLAG_SWIMMING); + if (canSwim) + bot->SetSwim(true); + movementFlagsUpdated = true; + } + else if (!canSwim && isSwimming) + { + bot->SetSwim(false); movementFlagsUpdated = true; } } - else if (isSwimming || isWaterWalking) + + // reset when not around water while swimming or water walking + if (liquidState == LIQUID_MAP_NO_WATER && (isSwimming || isWaterWalking)) { - // reset water flags - bot->RemoveUnitMovementFlag(MOVEMENTFLAG_SWIMMING); + bot->SetSwim(false); bot->RemoveUnitMovementFlag(MOVEMENTFLAG_WATERWALKING); movementFlagsUpdated = true; } - // handle flying state - if (wantsToFly && !isFlying && masterIsFlying) + // handle flying + if ((canFly && !isFlying) && isMasterFlying) { bot->AddUnitMovementFlag(MOVEMENTFLAG_CAN_FLY); bot->AddUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY); bot->AddUnitMovementFlag(MOVEMENTFLAG_FLYING); - movementFlagsUpdated = true; + + // required for transition and state monitoring. + if (MotionMaster* mm = bot->GetMotionMaster()) + mm->MoveTakeoff(0, {bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ() + 1.F}, 0.F, true); } - else if ((!wantsToFly || onGroundZ) && isFlying) + else if ((!canFly && !isWaterWalking && isFlying) || (!isMasterFlying && isFlying && onGroundZ)) { bot->RemoveUnitMovementFlag(MOVEMENTFLAG_CAN_FLY); bot->RemoveUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY); @@ -1022,15 +1024,16 @@ void MovementAction::UpdateMovementState() movementFlagsUpdated = true; } - // detect if movement restrictions have been lifted, CC just ended. + // detect if movement/CC restrictions have been ended, refresh movement state for animations. if (wasMovementRestricted) - movementFlagsUpdated = true; // refresh movement state to ensure animations play correctly + movementFlagsUpdated = true; + // movement flags should only be updated between state changes, if not it will break certain effects. if (movementFlagsUpdated) bot->SendMovementFlagUpdate(); } - // Save current state for the next check + // Save current state for the next check wasMovementRestricted = isCurrentlyRestricted; // Temporary speed increase in group @@ -1813,12 +1816,11 @@ void MovementAction::DoMovePoint(Unit* unit, float x, float y, float z, bool gen if (!mm) return; - // enable water walking - if (unit->HasUnitMovementFlag(MOVEMENTFLAG_WATERWALKING)) + // bot water collision correction + if (unit->HasUnitMovementFlag(MOVEMENTFLAG_WATERWALKING) && unit->HasWaterWalkAura()) { float gZ = unit->GetMapWaterOrGroundLevel(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ()); unit->UpdatePosition(unit->GetPositionX(), unit->GetPositionY(), gZ, false); - // z = gZ; no overwrite Z axe otherwise you cant steer the bots into swimming when water walking. } mm->Clear(); From 441f9f75525d1fe5b8f9a3110821a4a89cad87a2 Mon Sep 17 00:00:00 2001 From: Keleborn <22352763+Celandriel@users.noreply.github.com> Date: Sat, 14 Feb 2026 11:55:10 -0800 Subject: [PATCH 22/25] Warnings PR 1: Event warnings and headers (#2106) # Pull Request This is the first in a series of PRs intended to eliminate warnings in the module. The design intent is to eliminate the calling event when not needed in the body of the function. Based off of SmashingQuasars work. --- ## 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? - [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: - [ ] 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 --- ## 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> --- .../AcceptBattlegroundInvitationAction.cpp | 6 +- src/Ai/Base/Actions/AddLootAction.cpp | 2 +- src/Ai/Base/Actions/AreaTriggerAction.cpp | 2 +- .../AutoMaintenanceOnLevelupAction.cpp | 7 +- .../Base/Actions/BattleGroundJoinAction.cpp | 9 +- src/Ai/Base/Actions/BattleGroundTactics.cpp | 4 +- src/Ai/Base/Actions/BossAuraActions.cpp | 8 +- src/Ai/Base/Actions/CancelChannelAction.cpp | 4 +- src/Ai/Base/Actions/CastCustomSpellAction.cpp | 2 +- src/Ai/Base/Actions/ChangeTalentsAction.cpp | 6 +- src/Ai/Base/Actions/ChatShortcutActions.cpp | 18 +-- src/Ai/Base/Actions/CheckMailAction.cpp | 7 +- src/Ai/Base/Actions/CheckValuesAction.cpp | 7 +- src/Ai/Base/Actions/ChooseRpgTargetAction.cpp | 4 +- src/Ai/Base/Actions/ChooseTargetActions.cpp | 4 +- .../Base/Actions/ChooseTravelTargetAction.cpp | 2 +- src/Ai/Base/Actions/DebugAction.cpp | 6 +- src/Ai/Base/Actions/DelayAction.cpp | 12 +- src/Ai/Base/Actions/DestroyItemAction.cpp | 2 +- src/Ai/Base/Actions/EmoteAction.cpp | 3 +- src/Ai/Base/Actions/EquipAction.cpp | 2 +- src/Ai/Base/Actions/FishingAction.cpp | 10 +- src/Ai/Base/Actions/FollowActions.cpp | 7 +- src/Ai/Base/Actions/GenericActions.cpp | 6 +- src/Ai/Base/Actions/GenericSpellActions.cpp | 12 +- src/Ai/Base/Actions/GiveItemAction.cpp | 2 +- src/Ai/Base/Actions/GreetAction.cpp | 2 +- src/Ai/Base/Actions/GuildBankAction.cpp | 3 +- src/Ai/Base/Actions/GuildCreateActions.cpp | 10 +- .../Base/Actions/GuildManagementActions.cpp | 3 +- src/Ai/Base/Actions/HelpAction.cpp | 4 +- src/Ai/Base/Actions/HireAction.cpp | 11 +- src/Ai/Base/Actions/ImbueAction.cpp | 8 +- src/Ai/Base/Actions/InventoryAction.cpp | 2 - src/Ai/Base/Actions/InviteToGroupAction.cpp | 21 ++- src/Ai/Base/Actions/LeaveGroupAction.cpp | 2 +- src/Ai/Base/Actions/LfgActions.cpp | 20 +-- src/Ai/Base/Actions/LootRollAction.cpp | 2 +- src/Ai/Base/Actions/MoveToRpgTargetAction.cpp | 8 +- .../Base/Actions/MoveToTravelTargetAction.cpp | 3 +- src/Ai/Base/Actions/MovementActions.cpp | 64 +++----- src/Ai/Base/Actions/OpenItemAction.cpp | 2 +- .../Actions/PassLeadershipToMasterAction.cpp | 3 +- src/Ai/Base/Actions/PetsAction.h | 2 - src/Ai/Base/Actions/PositionAction.cpp | 4 +- src/Ai/Base/Actions/QueryQuestAction.cpp | 2 +- src/Ai/Base/Actions/QuestAction.cpp | 6 +- src/Ai/Base/Actions/RandomBotUpdateAction.cpp | 2 +- src/Ai/Base/Actions/ReachTargetActions.cpp | 2 +- src/Ai/Base/Actions/ReleaseSpiritAction.cpp | 6 +- src/Ai/Base/Actions/RememberTaxiAction.cpp | 2 +- src/Ai/Base/Actions/RepairAllAction.cpp | 2 +- src/Ai/Base/Actions/ResetInstancesAction.cpp | 5 +- .../Actions/RevealGatheringItemAction.cpp | 4 +- .../Base/Actions/ReviveFromCorpseAction.cpp | 5 +- src/Ai/Base/Actions/RpgAction.cpp | 3 +- src/Ai/Base/Actions/RpgSubActions.cpp | 34 ++-- src/Ai/Base/Actions/RtiAction.cpp | 2 +- src/Ai/Base/Actions/SayAction.cpp | 5 +- src/Ai/Base/Actions/SecurityCheckAction.cpp | 2 +- src/Ai/Base/Actions/SeeSpellAction.cpp | 1 - src/Ai/Base/Actions/SetCraftAction.cpp | 9 +- src/Ai/Base/Actions/SetHomeAction.cpp | 2 +- src/Ai/Base/Actions/StatsAction.cpp | 4 +- src/Ai/Base/Actions/StayActions.cpp | 6 +- src/Ai/Base/Actions/SuggestWhatToDoAction.cpp | 14 +- src/Ai/Base/Actions/TameAction.cpp | 2 - src/Ai/Base/Actions/TeleportAction.cpp | 9 +- src/Ai/Base/Actions/TellLosAction.cpp | 6 +- src/Ai/Base/Actions/TellMasterAction.cpp | 4 +- src/Ai/Base/Actions/TellReputationAction.cpp | 4 +- src/Ai/Base/Actions/TellTargetAction.cpp | 7 +- src/Ai/Base/Actions/TradeAction.cpp | 4 +- src/Ai/Base/Actions/TradeStatusAction.cpp | 11 +- src/Ai/Base/Actions/TrainerAction.cpp | 6 +- src/Ai/Base/Actions/TravelAction.cpp | 10 +- src/Ai/Base/Actions/UnlockItemAction.cpp | 7 +- .../Base/Actions/UnlockTradedItemAction.cpp | 6 +- src/Ai/Base/Actions/UseItemAction.cpp | 4 +- src/Ai/Base/Actions/UseMeetingStoneAction.cpp | 4 +- src/Ai/Base/Actions/VehicleActions.cpp | 2 +- .../Strategy/ChatCommandHandlerStrategy.cpp | 2 - src/Ai/Base/Strategy/CombatStrategy.cpp | 1 - src/Ai/Base/Strategy/DuelStrategy.cpp | 2 - src/Ai/Base/Strategy/FollowMasterStrategy.cpp | 2 - src/Ai/Base/Strategy/GuardStrategy.cpp | 2 - src/Ai/Base/Strategy/NonCombatStrategy.cpp | 2 - src/Ai/Base/Strategy/RTSCStrategy.cpp | 2 - src/Ai/Base/Strategy/RacialsStrategy.cpp | 2 - src/Ai/Base/Strategy/UsePotionsStrategy.cpp | 2 - src/Ai/Base/Trigger/GenericTriggers.cpp | 2 - src/Ai/Base/Trigger/RangeTriggers.cpp | 1 - src/Ai/Base/Value/Arrow.cpp | 3 +- src/Ai/Base/Value/CcTargetValue.cpp | 4 +- src/Ai/Base/Value/CurrentCcTargetValue.cpp | 3 +- src/Ai/Base/Value/DpsTargetValue.cpp | 9 +- src/Ai/Base/Value/ItemCountValue.cpp | 4 +- src/Ai/Base/Value/LastMovementValue.cpp | 1 - src/Ai/Base/Value/LeastHpTargetValue.cpp | 2 +- src/Ai/Base/Value/LogLevelValue.h | 1 + src/Ai/Base/Value/NearestCorpsesValue.cpp | 1 - src/Ai/Base/Value/PartyMemberToDispel.cpp | 3 +- src/Ai/Base/Value/PartyMemberValue.cpp | 5 +- .../Base/Value/PartyMemberWithoutItemValue.h | 4 +- src/Ai/Base/Value/PossibleRpgTargetsValue.cpp | 19 ++- src/Ai/Base/Value/SnareTargetValue.cpp | 3 +- src/Ai/Base/Value/SpellIdValue.cpp | 1 - src/Ai/Base/Value/StatsValues.cpp | 8 +- src/Ai/Base/Value/TankTargetValue.cpp | 5 +- .../Strategy/GenericDKNonCombatStrategy.cpp | 2 - src/Ai/Class/Druid/Action/DruidActions.cpp | 4 +- .../Druid/Action/DruidShapeshiftActions.cpp | 4 +- src/Ai/Class/Hunter/Action/HunterActions.cpp | 4 +- .../Hunter/Strategy/GenericHunterStrategy.cpp | 3 - .../Class/Paladin/Action/PaladinActions.cpp | 17 +- .../Strategy/GenericPaladinStrategy.cpp | 1 - src/Ai/Class/Priest/Action/PriestActions.cpp | 2 +- src/Ai/Class/Rogue/Action/RogueActions.cpp | 10 +- src/Ai/Class/Shaman/Action/ShamanActions.cpp | 5 +- .../Shaman/Strategy/TotemsShamanStrategy.h | 1 - src/Ai/Class/Shaman/Trigger/ShamanTriggers.h | 2 - .../Class/Warlock/Action/WarlockActions.cpp | 14 +- .../Strategy/GenericWarlockStrategy.cpp | 2 - .../Warlock/Strategy/TankWarlockStrategy.cpp | 1 - .../Class/Warrior/Action/WarriorActions.cpp | 4 +- .../Warrior/Strategy/ArmsWarriorStrategy.cpp | 2 - .../Warrior/Strategy/FuryWarriorStrategy.cpp | 2 - .../Warrior/Strategy/TankWarriorStrategy.cpp | 2 - .../AzjolNerub/Action/AzjolNerubActions.cpp | 7 +- .../Action/CullingOfStratholmeActions.cpp | 5 +- .../Action/DrakTharonKeepActions.cpp | 28 +--- .../Action/ForgeOfSoulsActions.cpp | 10 +- .../Dungeon/Gundrak/Action/GundrakActions.cpp | 7 +- .../Action/HallsOfLightningActions.cpp | 19 ++- .../Action/HallsOfStoneActions.cpp | 5 +- src/Ai/Dungeon/Nexus/Action/NexusActions.cpp | 38 ++--- .../Dungeon/Oculus/Action/OculusActions.cpp | 18 +-- src/Ai/Dungeon/Oculus/Action/OculusActions.h | 2 - .../Oculus/Multiplier/OculusMultipliers.cpp | 23 +-- .../OldKingdom/Action/OldKingdomActions.cpp | 7 +- .../PitOfSaron/Action/PitOfSaronActions.cpp | 6 +- .../Action/TrialOfTheChampionActions.cpp | 17 +- .../Strategy/TrialOfTheChampionStrategy.cpp | 1 - .../UtgardeKeep/Action/UtgardeKeepActions.cpp | 11 +- .../Action/UtgardePinnacleActions.cpp | 5 +- .../VioletHold/Action/VioletHoldActions.cpp | 9 +- src/Ai/Raid/Aq20/Action/RaidAq20Actions.cpp | 3 +- src/Ai/Raid/Aq20/Trigger/RaidAq20Triggers.cpp | 1 - .../BlackwingLair/Action/RaidBwlActions.cpp | 9 +- .../EyeOfEternity/Action/RaidEoEActions.cpp | 10 +- .../Action/RaidGruulsLairActions.cpp | 26 +-- .../Raid/Icecrown/Action/RaidIccActions.cpp | 108 ++++++------- .../Multiplier/RaidIccMultipliers.cpp | 1 - .../Raid/Icecrown/Trigger/RaidIccTriggers.cpp | 5 - .../Karazhan/Action/RaidKarazhanActions.cpp | 75 +++++---- .../Karazhan/Util/RaidKarazhanHelpers.cpp | 4 - .../Action/RaidMagtheridonActions.cpp | 20 +-- .../Raid/MoltenCore/Action/RaidMcActions.cpp | 11 +- .../ObsidianSanctum/Action/RaidOsActions.cpp | 12 +- .../Raid/Onyxia/Action/RaidOnyxiaActions.cpp | 11 +- .../Raid/Ulduar/Action/RaidUlduarActions.cpp | 149 +++++++----------- .../Ulduar/Trigger/RaidUlduarTriggers.cpp | 146 ++--------------- .../VaultOfArchavon/Action/RaidVoAActions.cpp | 8 +- src/Ai/World/Rpg/Action/NewRpgAction.cpp | 21 +-- src/Ai/World/Rpg/Strategy/NewRpgStrategy.cpp | 5 +- src/Bot/Cmd/PlayerbotCommandServer.cpp | 5 +- src/Bot/Engine/Action/Action.h | 1 - src/Bot/Engine/Strategy/CustomStrategy.cpp | 2 - src/Bot/Engine/Trigger/Trigger.cpp | 3 +- src/Bot/Engine/Trigger/Trigger.h | 1 - src/Bot/Factory/AiFactory.h | 1 - src/Bot/Factory/PlayerbotFactory.cpp | 32 ++-- src/Bot/Factory/RandomPlayerbotFactory.cpp | 7 +- src/Bot/PlayerbotAI.cpp | 2 - src/Bot/PlayerbotAI.h | 2 - src/Bot/PlayerbotMgr.cpp | 22 ++- src/Bot/RandomPlayerbotMgr.cpp | 23 ++- src/Db/PlayerbotRepository.cpp | 5 +- src/Mgr/Item/RandomItemMgr.cpp | 5 +- src/Mgr/Item/StatsCollector.cpp | 5 - src/Mgr/Talent/Talentspec.cpp | 4 +- src/Mgr/Talent/Talentspec.h | 4 +- src/Mgr/Travel/TravelMgr.cpp | 3 +- src/Script/Playerbots.cpp | 36 ----- .../WorldThr/PlayerbotWorldThreadProcessor.h | 1 - 185 files changed, 653 insertions(+), 1064 deletions(-) diff --git a/src/Ai/Base/Actions/AcceptBattlegroundInvitationAction.cpp b/src/Ai/Base/Actions/AcceptBattlegroundInvitationAction.cpp index 96d61d4ba3..91be09df92 100644 --- a/src/Ai/Base/Actions/AcceptBattlegroundInvitationAction.cpp +++ b/src/Ai/Base/Actions/AcceptBattlegroundInvitationAction.cpp @@ -6,9 +6,9 @@ #include "AcceptBattlegroundInvitationAction.h" #include "Event.h" -#include "Playerbots.h" +#include "PlayerbotAI.h" -bool AcceptBgInvitationAction::Execute(Event event) +bool AcceptBgInvitationAction::Execute(Event /*event*/) { uint8 type = 0; // arenatype if arena uint8 unk2 = 0; // unk, can be 0x0 (may be if was invited?) and 0x1 @@ -18,9 +18,9 @@ bool AcceptBgInvitationAction::Execute(Event event) WorldPacket packet(CMSG_BATTLEFIELD_PORT, 20); packet << type << unk2 << (uint32)bgTypeId_ << unk << action; - // packet << bgTypeId_ << action; bot->GetSession()->HandleBattleFieldPortOpcode(packet); botAI->ResetStrategies(); + return true; } diff --git a/src/Ai/Base/Actions/AddLootAction.cpp b/src/Ai/Base/Actions/AddLootAction.cpp index 9e16ee2d3a..40cda0e6e1 100644 --- a/src/Ai/Base/Actions/AddLootAction.cpp +++ b/src/Ai/Base/Actions/AddLootAction.cpp @@ -22,7 +22,7 @@ bool AddLootAction::Execute(Event event) return AI_VALUE(LootObjectStack*, "available loot")->Add(guid); } -bool AddAllLootAction::Execute(Event event) +bool AddAllLootAction::Execute(Event /*event*/) { bool added = false; diff --git a/src/Ai/Base/Actions/AreaTriggerAction.cpp b/src/Ai/Base/Actions/AreaTriggerAction.cpp index c610085400..a3339ea5c8 100644 --- a/src/Ai/Base/Actions/AreaTriggerAction.cpp +++ b/src/Ai/Base/Actions/AreaTriggerAction.cpp @@ -58,7 +58,7 @@ bool ReachAreaTriggerAction::Execute(Event event) return true; } -bool AreaTriggerAction::Execute(Event event) +bool AreaTriggerAction::Execute(Event /*event*/) { LastMovement& movement = context->GetValue("last area trigger")->Get(); diff --git a/src/Ai/Base/Actions/AutoMaintenanceOnLevelupAction.cpp b/src/Ai/Base/Actions/AutoMaintenanceOnLevelupAction.cpp index de8b5c6fdc..72433c15fd 100644 --- a/src/Ai/Base/Actions/AutoMaintenanceOnLevelupAction.cpp +++ b/src/Ai/Base/Actions/AutoMaintenanceOnLevelupAction.cpp @@ -1,19 +1,20 @@ #include "AutoMaintenanceOnLevelupAction.h" -#include "GuildMgr.h" +#include "SpellMgr.h" + #include "PlayerbotAIConfig.h" #include "PlayerbotFactory.h" -#include "Playerbots.h" #include "RandomPlayerbotMgr.h" #include "SharedDefines.h" #include "BroadcastHelper.h" -bool AutoMaintenanceOnLevelupAction::Execute(Event event) +bool AutoMaintenanceOnLevelupAction::Execute(Event /*event*/) { AutoPickTalents(); AutoLearnSpell(); AutoUpgradeEquip(); AutoTeleportForLevel(); + return true; } diff --git a/src/Ai/Base/Actions/BattleGroundJoinAction.cpp b/src/Ai/Base/Actions/BattleGroundJoinAction.cpp index 418b0f1fa7..24b23cb9d4 100644 --- a/src/Ai/Base/Actions/BattleGroundJoinAction.cpp +++ b/src/Ai/Base/Actions/BattleGroundJoinAction.cpp @@ -13,9 +13,8 @@ #include "PlayerbotAI.h" #include "Playerbots.h" #include "PositionValue.h" -#include "UpdateTime.h" -bool BGJoinAction::Execute(Event event) +bool BGJoinAction::Execute(Event /*event*/) { uint32 queueType = AI_VALUE(uint32, "bg type"); if (!queueType) // force join to fill bg @@ -653,7 +652,7 @@ bool FreeBGJoinAction::shouldJoinBg(BattlegroundQueueTypeId queueTypeId, Battleg return false; } -bool BGLeaveAction::Execute(Event event) +bool BGLeaveAction::Execute(Event /*event*/) { if (!(bot->InBattlegroundQueue() || bot->InBattleground())) return false; @@ -1064,7 +1063,7 @@ bool BGStatusAction::Execute(Event event) return true; } -bool BGStatusCheckAction::Execute(Event event) +bool BGStatusCheckAction::Execute(Event /*event*/) { if (bot->IsBeingTeleported()) return false; @@ -1080,7 +1079,7 @@ bool BGStatusCheckAction::Execute(Event event) bool BGStatusCheckAction::isUseful() { return bot->InBattlegroundQueue(); } -bool BGStrategyCheckAction::Execute(Event event) +bool BGStrategyCheckAction::Execute(Event /*event*/) { bool inside_bg = bot->InBattleground() && bot->GetBattleground(); ; diff --git a/src/Ai/Base/Actions/BattleGroundTactics.cpp b/src/Ai/Base/Actions/BattleGroundTactics.cpp index 2b198022e0..78c156280f 100644 --- a/src/Ai/Base/Actions/BattleGroundTactics.cpp +++ b/src/Ai/Base/Actions/BattleGroundTactics.cpp @@ -1557,7 +1557,7 @@ bool BGTactics::eyJumpDown() // // actual bg tactics below // -bool BGTactics::Execute(Event event) +bool BGTactics::Execute(Event /*event*/) { Battleground* bg = bot->GetBattleground(); if (!bg) @@ -4249,7 +4249,7 @@ bool BGTactics::IsLockedInsideKeep() return false; } -bool ArenaTactics::Execute(Event event) +bool ArenaTactics::Execute(Event /*event*/) { if (!bot->InBattleground()) { diff --git a/src/Ai/Base/Actions/BossAuraActions.cpp b/src/Ai/Base/Actions/BossAuraActions.cpp index d711559ee5..90284db104 100644 --- a/src/Ai/Base/Actions/BossAuraActions.cpp +++ b/src/Ai/Base/Actions/BossAuraActions.cpp @@ -18,7 +18,7 @@ bool BossFireResistanceAction::isUseful() return bossFireResistanceTrigger.IsActive(); } -bool BossFireResistanceAction::Execute(Event event) +bool BossFireResistanceAction::Execute(Event /*event*/) { PaladinFireResistanceStrategy paladinFireResistanceStrategy(botAI); botAI->ChangeStrategy(ADD_STRATEGY_CHAR + paladinFireResistanceStrategy.getName(), BotState::BOT_STATE_COMBAT); @@ -32,7 +32,7 @@ bool BossFrostResistanceAction::isUseful() return bossFrostResistanceTrigger.IsActive(); } -bool BossFrostResistanceAction::Execute(Event event) +bool BossFrostResistanceAction::Execute(Event /*event*/) { PaladinFrostResistanceStrategy paladinFrostResistanceStrategy(botAI); botAI->ChangeStrategy(ADD_STRATEGY_CHAR + paladinFrostResistanceStrategy.getName(), BotState::BOT_STATE_COMBAT); @@ -46,7 +46,7 @@ bool BossNatureResistanceAction::isUseful() return bossNatureResistanceTrigger.IsActive(); } -bool BossNatureResistanceAction::Execute(Event event) +bool BossNatureResistanceAction::Execute(Event /*event*/) { HunterNatureResistanceStrategy hunterNatureResistanceStrategy(botAI); botAI->ChangeStrategy(ADD_STRATEGY_CHAR + hunterNatureResistanceStrategy.getName(), BotState::BOT_STATE_COMBAT); @@ -60,7 +60,7 @@ bool BossShadowResistanceAction::isUseful() return bossShadowResistanceTrigger.IsActive(); } -bool BossShadowResistanceAction::Execute(Event event) +bool BossShadowResistanceAction::Execute(Event /*event*/) { PaladinShadowResistanceStrategy paladinShadowResistanceStrategy(botAI); botAI->ChangeStrategy(ADD_STRATEGY_CHAR + paladinShadowResistanceStrategy.getName(), BotState::BOT_STATE_COMBAT); diff --git a/src/Ai/Base/Actions/CancelChannelAction.cpp b/src/Ai/Base/Actions/CancelChannelAction.cpp index 9f359f392b..103aec4d8f 100644 --- a/src/Ai/Base/Actions/CancelChannelAction.cpp +++ b/src/Ai/Base/Actions/CancelChannelAction.cpp @@ -7,12 +7,14 @@ #include "Player.h" #include "PlayerbotAI.h" -bool CancelChannelAction::Execute(Event event) +bool CancelChannelAction::Execute(Event /*event*/) { if (bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL)) { bot->InterruptSpell(CURRENT_CHANNELED_SPELL); + return true; } + return false; } diff --git a/src/Ai/Base/Actions/CastCustomSpellAction.cpp b/src/Ai/Base/Actions/CastCustomSpellAction.cpp index 2ec7210d29..15c35ee430 100644 --- a/src/Ai/Base/Actions/CastCustomSpellAction.cpp +++ b/src/Ai/Base/Actions/CastCustomSpellAction.cpp @@ -334,7 +334,7 @@ bool CastRandomSpellAction::castSpell(uint32 spellId, WorldObject* wo) return botAI->CastSpell(spellId, wo->GetPositionX(), wo->GetPositionY(), wo->GetPositionZ()); } -bool DisEnchantRandomItemAction::Execute(Event event) +bool DisEnchantRandomItemAction::Execute(Event /*event*/) { std::vector items = AI_VALUE2(std::vector, "inventory items", "usage " + std::to_string(ITEM_USAGE_DISENCHANT)); diff --git a/src/Ai/Base/Actions/ChangeTalentsAction.cpp b/src/Ai/Base/Actions/ChangeTalentsAction.cpp index df09dadaca..f04db2207b 100644 --- a/src/Ai/Base/Actions/ChangeTalentsAction.cpp +++ b/src/Ai/Base/Actions/ChangeTalentsAction.cpp @@ -10,9 +10,9 @@ #include "Event.h" #include "PlayerbotAIConfig.h" #include "PlayerbotFactory.h" -#include "Playerbots.h" #include "AiObjectContext.h" #include "Log.h" +#include "RandomPlayerbotMgr.h" bool ChangeTalentsAction::Execute(Event event) { @@ -368,11 +368,11 @@ std::string ChangeTalentsAction::SpecApply(std::string param) // return nullptr; // } -bool AutoSetTalentsAction::Execute(Event event) +bool AutoSetTalentsAction::Execute(Event /*event*/) { std::ostringstream out; - if (!sPlayerbotAIConfig.autoPickTalents || !sRandomPlayerbotMgr.IsRandomBot(bot)) + if (!PlayerbotAIConfig::instance().autoPickTalents || !RandomPlayerbotMgr::instance().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 85b141d4d1..30563e8e68 100644 --- a/src/Ai/Base/Actions/ChatShortcutActions.cpp +++ b/src/Ai/Base/Actions/ChatShortcutActions.cpp @@ -42,7 +42,7 @@ void PositionsResetAction::SetStayPosition(float x, float y, float z) posMap["stay"] = pos; } -bool FollowChatShortcutAction::Execute(Event event) +bool FollowChatShortcutAction::Execute(Event /*event*/) { Player* master = GetMaster(); if (!master) @@ -116,7 +116,7 @@ bool FollowChatShortcutAction::Execute(Event event) return true; } -bool StayChatShortcutAction::Execute(Event event) +bool StayChatShortcutAction::Execute(Event /*event*/) { Player* master = GetMaster(); if (!master) @@ -133,7 +133,7 @@ bool StayChatShortcutAction::Execute(Event event) return true; } -bool MoveFromGroupChatShortcutAction::Execute(Event event) +bool MoveFromGroupChatShortcutAction::Execute(Event /*event*/) { Player* master = GetMaster(); if (!master) @@ -148,7 +148,7 @@ bool MoveFromGroupChatShortcutAction::Execute(Event event) return true; } -bool FleeChatShortcutAction::Execute(Event event) +bool FleeChatShortcutAction::Execute(Event /*event*/) { Player* master = GetMaster(); if (!master) @@ -171,7 +171,7 @@ bool FleeChatShortcutAction::Execute(Event event) return true; } -bool GoawayChatShortcutAction::Execute(Event event) +bool GoawayChatShortcutAction::Execute(Event /*event*/) { Player* master = GetMaster(); if (!master) @@ -188,7 +188,7 @@ bool GoawayChatShortcutAction::Execute(Event event) return true; } -bool GrindChatShortcutAction::Execute(Event event) +bool GrindChatShortcutAction::Execute(Event /*event*/) { Player* master = GetMaster(); if (!master) @@ -204,7 +204,7 @@ bool GrindChatShortcutAction::Execute(Event event) return true; } -bool TankAttackChatShortcutAction::Execute(Event event) +bool TankAttackChatShortcutAction::Execute(Event /*event*/) { Player* master = GetMaster(); if (!master) @@ -224,7 +224,7 @@ bool TankAttackChatShortcutAction::Execute(Event event) return true; } -bool MaxDpsChatShortcutAction::Execute(Event event) +bool MaxDpsChatShortcutAction::Execute(Event /*event*/) { Player* master = GetMaster(); if (!master) @@ -241,7 +241,7 @@ bool MaxDpsChatShortcutAction::Execute(Event event) return true; } -bool BwlChatShortcutAction::Execute(Event event) +bool BwlChatShortcutAction::Execute(Event /*event*/) { Player* master = GetMaster(); if (!master) diff --git a/src/Ai/Base/Actions/CheckMailAction.cpp b/src/Ai/Base/Actions/CheckMailAction.cpp index f9c70ff11f..4a39909cdf 100644 --- a/src/Ai/Base/Actions/CheckMailAction.cpp +++ b/src/Ai/Base/Actions/CheckMailAction.cpp @@ -7,9 +7,10 @@ #include "Event.h" #include "GuildTaskMgr.h" -#include "Playerbots.h" +#include "PlayerbotAIConfig.h" +#include "PlayerbotAI.h" -bool CheckMailAction::Execute(Event event) +bool CheckMailAction::Execute(Event /*event*/) { WorldPacket p; bot->GetSession()->HandleQueryNextMailTime(p); @@ -28,7 +29,7 @@ bool CheckMailAction::Execute(Event event) continue; uint32 account = owner->GetSession()->GetAccountId(); - if (sPlayerbotAIConfig.IsInRandomAccountList(account)) + if (PlayerbotAIConfig::instance().IsInRandomAccountList(account)) continue; ProcessMail(mail, owner, trans); diff --git a/src/Ai/Base/Actions/CheckValuesAction.cpp b/src/Ai/Base/Actions/CheckValuesAction.cpp index 7c64b7c513..dce66bd47b 100644 --- a/src/Ai/Base/Actions/CheckValuesAction.cpp +++ b/src/Ai/Base/Actions/CheckValuesAction.cpp @@ -6,12 +6,15 @@ #include "CheckValuesAction.h" #include "Event.h" -#include "Playerbots.h" #include "ServerFacade.h" +#include "PlayerbotAI.h" +#include "TravelNode.h" +#include "AiObjectContext.h" + CheckValuesAction::CheckValuesAction(PlayerbotAI* botAI) : Action(botAI, "check values") {} -bool CheckValuesAction::Execute(Event event) +bool CheckValuesAction::Execute(Event /*event*/) { if (botAI->HasStrategy("debug move", BOT_STATE_NON_COMBAT)) { diff --git a/src/Ai/Base/Actions/ChooseRpgTargetAction.cpp b/src/Ai/Base/Actions/ChooseRpgTargetAction.cpp index ce3d63562e..e14d0cf3a4 100644 --- a/src/Ai/Base/Actions/ChooseRpgTargetAction.cpp +++ b/src/Ai/Base/Actions/ChooseRpgTargetAction.cpp @@ -6,7 +6,6 @@ #include #include "ChooseRpgTargetAction.h" -#include "BattlegroundMgr.h" #include "BudgetValues.h" #include "ChatHelper.h" #include "Event.h" @@ -14,7 +13,6 @@ #include "GuildCreateActions.h" #include "Playerbots.h" #include "RpgSubActions.h" -#include "Util.h" #include "ServerFacade.h" #include "PossibleRpgTargetsValue.h" @@ -112,7 +110,7 @@ float ChooseRpgTargetAction::getMaxRelevance(GuidPosition guidP) return floor((maxRelevance - 1.0) * 1000.0f); } -bool ChooseRpgTargetAction::Execute(Event event) +bool ChooseRpgTargetAction::Execute(Event /*event*/) { //TravelTarget* travelTarget = AI_VALUE(TravelTarget*, "travel target"); //not used, line marked for removal. Player* master = botAI->GetMaster(); diff --git a/src/Ai/Base/Actions/ChooseTargetActions.cpp b/src/Ai/Base/Actions/ChooseTargetActions.cpp index 3446c9b521..f7538c03ca 100644 --- a/src/Ai/Base/Actions/ChooseTargetActions.cpp +++ b/src/Ai/Base/Actions/ChooseTargetActions.cpp @@ -30,7 +30,7 @@ bool AttackEnemyFlagCarrierAction::isUseful() PlayerHasFlag::IsCapturingFlag(bot); } -bool DropTargetAction::Execute(Event event) +bool DropTargetAction::Execute(Event /*event*/) { Unit* target = context->GetValue("current target")->Get(); if (target && target->isDead()) @@ -137,7 +137,7 @@ bool DpsAssistAction::isUseful() return true; } -bool AttackRtiTargetAction::Execute(Event event) +bool AttackRtiTargetAction::Execute(Event /*event*/) { Unit* rtiTarget = AI_VALUE(Unit*, "rti target"); diff --git a/src/Ai/Base/Actions/ChooseTravelTargetAction.cpp b/src/Ai/Base/Actions/ChooseTravelTargetAction.cpp index 0f75a30798..695f0d3111 100644 --- a/src/Ai/Base/Actions/ChooseTravelTargetAction.cpp +++ b/src/Ai/Base/Actions/ChooseTravelTargetAction.cpp @@ -9,7 +9,7 @@ #include "LootObjectStack.h" #include "Playerbots.h" -bool ChooseTravelTargetAction::Execute(Event event) +bool ChooseTravelTargetAction::Execute(Event /*event*/) { // Player* requester = event.getOwner() ? event.getOwner() : GetMaster(); //not used, line marked for removal. diff --git a/src/Ai/Base/Actions/DebugAction.cpp b/src/Ai/Base/Actions/DebugAction.cpp index d7fe0f8b75..378cdab79f 100644 --- a/src/Ai/Base/Actions/DebugAction.cpp +++ b/src/Ai/Base/Actions/DebugAction.cpp @@ -7,7 +7,11 @@ #include "ChooseTravelTargetAction.h" #include "MapMgr.h" -#include "Playerbots.h" +#include "TravelMgr.h" +#include "Player.h" +#include "PlayerbotAI.h" +#include "SpellMgr.h" +#include "Spell.h" bool DebugAction::Execute(Event event) { diff --git a/src/Ai/Base/Actions/DelayAction.cpp b/src/Ai/Base/Actions/DelayAction.cpp index b47b6898d1..3daf9ea25d 100644 --- a/src/Ai/Base/Actions/DelayAction.cpp +++ b/src/Ai/Base/Actions/DelayAction.cpp @@ -6,15 +6,19 @@ #include "DelayAction.h" #include "Event.h" -#include "Playerbots.h" +#include "PlayerbotAI.h" +#include "PlayerbotAIConfig.h" -bool DelayAction::Execute(Event event) +bool DelayAction::Execute(Event /*event*/) { - uint32 delay = sPlayerbotAIConfig.passiveDelay + sPlayerbotAIConfig.globalCoolDown; + const uint32 delay = PlayerbotAIConfig::instance().passiveDelay + PlayerbotAIConfig::instance().globalCoolDown; botAI->SetNextCheckDelay(delay); return true; } -bool DelayAction::isUseful() { return !botAI->AllowActivity(ALL_ACTIVITY); } +bool DelayAction::isUseful() +{ + return !botAI->AllowActivity(ALL_ACTIVITY); +} diff --git a/src/Ai/Base/Actions/DestroyItemAction.cpp b/src/Ai/Base/Actions/DestroyItemAction.cpp index 0fce4ad701..ffcf0fdcf6 100644 --- a/src/Ai/Base/Actions/DestroyItemAction.cpp +++ b/src/Ai/Base/Actions/DestroyItemAction.cpp @@ -39,7 +39,7 @@ void DestroyItemAction::DestroyItem(FindItemVisitor* visitor) bool SmartDestroyItemAction::isUseful() { return !botAI->HasActivePlayerMaster(); } -bool SmartDestroyItemAction::Execute(Event event) +bool SmartDestroyItemAction::Execute(Event /*event*/) { uint8 bagSpace = AI_VALUE(uint8, "bag space"); diff --git a/src/Ai/Base/Actions/EmoteAction.cpp b/src/Ai/Base/Actions/EmoteAction.cpp index 197cc436b1..dd6957cc88 100644 --- a/src/Ai/Base/Actions/EmoteAction.cpp +++ b/src/Ai/Base/Actions/EmoteAction.cpp @@ -6,7 +6,6 @@ #include "EmoteAction.h" #include "Event.h" -#include "PlayerbotTextMgr.h" #include "Playerbots.h" #include "ServerFacade.h" @@ -787,7 +786,7 @@ bool EmoteAction::isUseful() return time(nullptr) >= lastEmote; } -bool TalkAction::Execute(Event event) +bool TalkAction::Execute(Event /*event*/) { Unit* target = botAI->GetUnit(AI_VALUE(ObjectGuid, "talk target")); if (!target) diff --git a/src/Ai/Base/Actions/EquipAction.cpp b/src/Ai/Base/Actions/EquipAction.cpp index a8b262692b..5c91c13c8a 100644 --- a/src/Ai/Base/Actions/EquipAction.cpp +++ b/src/Ai/Base/Actions/EquipAction.cpp @@ -406,7 +406,7 @@ bool EquipUpgradesTriggeredAction::Execute(Event event) return true; } -bool EquipUpgradeAction::Execute(Event event) +bool EquipUpgradeAction::Execute(Event /*event*/) { ItemIds items = SelectInventoryItemsToEquip(); EquipItems(items); diff --git a/src/Ai/Base/Actions/FishingAction.cpp b/src/Ai/Base/Actions/FishingAction.cpp index 0fc09c3bce..a6057cabf8 100644 --- a/src/Ai/Base/Actions/FishingAction.cpp +++ b/src/Ai/Base/Actions/FishingAction.cpp @@ -246,7 +246,7 @@ WorldPosition FindFishingHole(PlayerbotAI* botAI) return WorldPosition(); } -bool MoveNearWaterAction::Execute(Event event) +bool MoveNearWaterAction::Execute(Event /*event*/) { WorldPosition landSpot = AI_VALUE(WorldPosition, "fishing spot"); if (landSpot.IsValid()) @@ -336,7 +336,7 @@ bool MoveNearWaterAction::isPossible() return false; } -bool EquipFishingPoleAction::Execute(Event event) +bool EquipFishingPoleAction::Execute(Event /*event*/) { if (!_pole) return false; @@ -463,7 +463,7 @@ bool UseBobberAction::isUseful() return AI_VALUE(bool, "can use fishing bobber"); } -bool UseBobberAction::Execute(Event event) +bool UseBobberAction::Execute(Event /*event*/) { GuidVector gos = AI_VALUE(GuidVector, "nearest game objects no los"); for (auto const& guid : gos) @@ -485,7 +485,7 @@ bool UseBobberAction::Execute(Event event) return false; } -bool EndMasterFishingAction::Execute(Event event) +bool EndMasterFishingAction::Execute(Event /*event*/) { botAI->ChangeStrategy("-master fishing", BOT_STATE_NON_COMBAT); return true; @@ -503,7 +503,7 @@ bool EndMasterFishingAction::isUseful() return !nearWater.IsValid(); } -bool RemoveBobberStrategyAction::Execute(Event event) +bool RemoveBobberStrategyAction::Execute(Event /*event*/) { botAI->ChangeStrategy("-use bobber", BOT_STATE_NON_COMBAT); return true; diff --git a/src/Ai/Base/Actions/FollowActions.cpp b/src/Ai/Base/Actions/FollowActions.cpp index b9623233a9..c27de01ffc 100644 --- a/src/Ai/Base/Actions/FollowActions.cpp +++ b/src/Ai/Base/Actions/FollowActions.cpp @@ -5,17 +5,14 @@ #include "FollowActions.h" -#include - #include "Event.h" #include "Formations.h" #include "LastMovementValue.h" #include "PlayerbotAI.h" #include "Playerbots.h" #include "ServerFacade.h" -#include "SharedDefines.h" -bool FollowAction::Execute(Event event) +bool FollowAction::Execute(Event /*event*/) { Formation* formation = AI_VALUE(Formation*, "formation"); std::string const target = formation->GetTargetName(); @@ -116,7 +113,7 @@ bool FollowAction::CanDeadFollow(Unit* target) return true; } -bool FleeToGroupLeaderAction::Execute(Event event) +bool FleeToGroupLeaderAction::Execute(Event /*event*/) { Unit* fTarget = AI_VALUE(Unit*, "group leader"); bool canFollow = Follow(fTarget); diff --git a/src/Ai/Base/Actions/GenericActions.cpp b/src/Ai/Base/Actions/GenericActions.cpp index 453834c097..35fa51b316 100644 --- a/src/Ai/Base/Actions/GenericActions.cpp +++ b/src/Ai/Base/Actions/GenericActions.cpp @@ -11,8 +11,6 @@ #include "CreatureAI.h" #include "Playerbots.h" #include "CharmInfo.h" -#include "SharedDefines.h" -#include "ObjectGuid.h" #include "SpellMgr.h" #include "SpellInfo.h" #include @@ -54,7 +52,7 @@ bool MeleeAction::isUseful() return true; } -bool TogglePetSpellAutoCastAction::Execute(Event event) +bool TogglePetSpellAutoCastAction::Execute(Event /*event*/) { Pet* pet = bot->GetPet(); if (!pet) @@ -119,7 +117,7 @@ bool TogglePetSpellAutoCastAction::Execute(Event event) return toggled; } -bool PetAttackAction::Execute(Event event) +bool PetAttackAction::Execute(Event /*event*/) { Guardian* pet = bot->GetGuardianPet(); if (!pet) diff --git a/src/Ai/Base/Actions/GenericSpellActions.cpp b/src/Ai/Base/Actions/GenericSpellActions.cpp index 02d1decc69..148bc6d3c2 100644 --- a/src/Ai/Base/Actions/GenericSpellActions.cpp +++ b/src/Ai/Base/Actions/GenericSpellActions.cpp @@ -17,19 +17,17 @@ #include "WorldPacket.h" #include "Group.h" #include "Chat.h" -#include "Language.h" #include "GenericBuffUtils.h" #include "PlayerbotAI.h" using ai::buff::MakeAuraQualifierForBuff; -using ai::buff::UpgradeToGroupIfAppropriate; CastSpellAction::CastSpellAction(PlayerbotAI* botAI, std::string const spell) : Action(botAI, spell), range(botAI->GetRange("spell")), spell(spell) { } -bool CastSpellAction::Execute(Event event) +bool CastSpellAction::Execute(Event /*event*/) { if (spell == "conjure food" || spell == "conjure water") { @@ -232,7 +230,7 @@ Value* BuffOnPartyAction::GetTargetValue() return context->GetValue("party member without aura", MakeAuraQualifierForBuff(spell)); } -bool BuffOnPartyAction::Execute(Event event) +bool BuffOnPartyAction::Execute(Event /*event*/) { std::string castName = spell; // default = mono @@ -289,7 +287,7 @@ Value* CastSnareSpellAction::GetTargetValue() { return context->GetValue< Value* CastCrowdControlSpellAction::GetTargetValue() { return context->GetValue("cc target", getName()); } -bool CastCrowdControlSpellAction::Execute(Event event) { return botAI->CastSpell(getName(), GetTarget()); } +bool CastCrowdControlSpellAction::Execute(Event /*event*/) { return botAI->CastSpell(getName(), GetTarget()); } bool CastCrowdControlSpellAction::isPossible() { return botAI->CanCastSpell(getName(), GetTarget()); } @@ -307,13 +305,13 @@ bool CastVehicleSpellAction::isPossible() bool CastVehicleSpellAction::isUseful() { return botAI->IsInVehicle(false, true); } -bool CastVehicleSpellAction::Execute(Event event) +bool CastVehicleSpellAction::Execute(Event /*event*/) { uint32 spellId = AI_VALUE2(uint32, "vehicle spell id", spell); return botAI->CastVehicleSpell(spellId, GetTarget()); } -bool UseTrinketAction::Execute(Event event) +bool UseTrinketAction::Execute(Event /*event*/) { Item* trinket1 = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_TRINKET1); diff --git a/src/Ai/Base/Actions/GiveItemAction.cpp b/src/Ai/Base/Actions/GiveItemAction.cpp index 350b465b83..6019d1e38e 100644 --- a/src/Ai/Base/Actions/GiveItemAction.cpp +++ b/src/Ai/Base/Actions/GiveItemAction.cpp @@ -11,7 +11,7 @@ std::vector split(std::string const s, char delim); -bool GiveItemAction::Execute(Event event) +bool GiveItemAction::Execute(Event /*event*/) { Unit* target = GetTarget(); if (!target) diff --git a/src/Ai/Base/Actions/GreetAction.cpp b/src/Ai/Base/Actions/GreetAction.cpp index a9fda122ec..bf6d1e455c 100644 --- a/src/Ai/Base/Actions/GreetAction.cpp +++ b/src/Ai/Base/Actions/GreetAction.cpp @@ -10,7 +10,7 @@ GreetAction::GreetAction(PlayerbotAI* botAI) : Action(botAI, "greet") {} -bool GreetAction::Execute(Event event) +bool GreetAction::Execute(Event /*event*/) { ObjectGuid guid = AI_VALUE(ObjectGuid, "new player nearby"); if (!guid || !guid.IsPlayer()) diff --git a/src/Ai/Base/Actions/GuildBankAction.cpp b/src/Ai/Base/Actions/GuildBankAction.cpp index 2d9c74ff69..9693556d78 100644 --- a/src/Ai/Base/Actions/GuildBankAction.cpp +++ b/src/Ai/Base/Actions/GuildBankAction.cpp @@ -6,7 +6,8 @@ #include "GuildBankAction.h" #include "GuildMgr.h" -#include "Playerbots.h" +#include "PlayerbotAI.h" +#include "AiObjectContext.h" bool GuildBankAction::Execute(Event event) { diff --git a/src/Ai/Base/Actions/GuildCreateActions.cpp b/src/Ai/Base/Actions/GuildCreateActions.cpp index c9f9e689eb..0e7098f1ef 100644 --- a/src/Ai/Base/Actions/GuildCreateActions.cpp +++ b/src/Ai/Base/Actions/GuildCreateActions.cpp @@ -12,9 +12,9 @@ #include "Playerbots.h" #include "RandomPlayerbotFactory.h" #include "ServerFacade.h" -#include "SharedDefines.h" // GOLD +#include "SharedDefines.h" -bool BuyPetitionAction::Execute(Event event) +bool BuyPetitionAction::Execute(Event /*event*/) { GuidVector vendors = botAI->GetAiObjectContext()->GetValue("nearest npcs")->Get(); bool vendored = false, result = false; @@ -152,7 +152,7 @@ bool PetitionOfferAction::Execute(Event event) bool PetitionOfferAction::isUseful() { return !bot->GetGuildId(); } -bool PetitionOfferNearbyAction::Execute(Event event) +bool PetitionOfferNearbyAction::Execute(Event /*event*/) { uint32 found = 0; @@ -209,7 +209,7 @@ bool PetitionOfferNearbyAction::isUseful() AI_VALUE(uint8, "petition signs") < sWorld->getIntConfig(CONFIG_MIN_PETITION_SIGNS); } -bool PetitionTurnInAction::Execute(Event event) +bool PetitionTurnInAction::Execute(Event /*event*/) { GuidVector vendors = botAI->GetAiObjectContext()->GetValue("nearest npcs")->Get(); bool vendored = false, result = false; @@ -297,7 +297,7 @@ bool PetitionTurnInAction::isUseful() !context->GetValue("travel target")->Get()->isTraveling(); } -bool BuyTabardAction::Execute(Event event) +bool BuyTabardAction::Execute(Event /*event*/) { bool canBuy = botAI->DoSpecificAction("buy", Event("buy tabard", "Hitem:5976:")); if (canBuy && AI_VALUE2(uint32, "item count", chat->FormatQItem(5976))) diff --git a/src/Ai/Base/Actions/GuildManagementActions.cpp b/src/Ai/Base/Actions/GuildManagementActions.cpp index be94b6ae62..0b5a7cf48a 100644 --- a/src/Ai/Base/Actions/GuildManagementActions.cpp +++ b/src/Ai/Base/Actions/GuildManagementActions.cpp @@ -128,7 +128,7 @@ bool GuildRemoveAction::PlayerIsValid(Player* member) return member->GetGuildId() == bot->GetGuildId() && GetRankId(bot) < GetRankId(member); }; -bool GuildManageNearbyAction::Execute(Event event) +bool GuildManageNearbyAction::Execute(Event /*event*/) { uint32 found = 0; @@ -149,7 +149,6 @@ bool GuildManageNearbyAction::Execute(Event event) // Promote or demote nearby members based on chance. if (player->GetGuildId() && player->GetGuildId() == bot->GetGuildId()) { - Guild::Member* member = guild->GetMember(player->GetGUID()); uint32 dCount = AI_VALUE(uint32, "death count"); if (!urand(0, 30) && dCount < 2 && guild->GetRankRights(botMember->GetRankId()) & GR_RIGHT_PROMOTE) diff --git a/src/Ai/Base/Actions/HelpAction.cpp b/src/Ai/Base/Actions/HelpAction.cpp index 3dbe1d6e4b..c3d14a0ffc 100644 --- a/src/Ai/Base/Actions/HelpAction.cpp +++ b/src/Ai/Base/Actions/HelpAction.cpp @@ -7,13 +7,13 @@ #include "ChatActionContext.h" #include "Event.h" -#include "Playerbots.h" +#include "AiObjectContext.h" HelpAction::HelpAction(PlayerbotAI* botAI) : Action(botAI, "help") { chatContext = new ChatActionContext(); } HelpAction::~HelpAction() { delete chatContext; } -bool HelpAction::Execute(Event event) +bool HelpAction::Execute(Event /*event*/) { TellChatCommands(); TellStrategies(); diff --git a/src/Ai/Base/Actions/HireAction.cpp b/src/Ai/Base/Actions/HireAction.cpp index eba607d2e5..374438f742 100644 --- a/src/Ai/Base/Actions/HireAction.cpp +++ b/src/Ai/Base/Actions/HireAction.cpp @@ -6,15 +6,16 @@ #include "HireAction.h" #include "Event.h" -#include "Playerbots.h" +#include "RandomPlayerbotMgr.h" +#include "PlayerbotAI.h" -bool HireAction::Execute(Event event) +bool HireAction::Execute(Event /*event*/) { Player* master = GetMaster(); if (!master) return false; - if (!sRandomPlayerbotMgr.IsRandomBot(bot)) + if (!RandomPlayerbotMgr::instance().IsRandomBot(bot)) return false; uint32 account = master->GetSession()->GetAccountId(); @@ -39,7 +40,7 @@ bool HireAction::Execute(Event event) return false; } - uint32 discount = sRandomPlayerbotMgr.GetTradeDiscount(bot, master); + uint32 discount = RandomPlayerbotMgr::instance().GetTradeDiscount(bot, master); uint32 m = 1 + (bot->GetLevel() / 10); uint32 moneyReq = m * 5000 * bot->GetLevel(); if (discount < moneyReq) @@ -54,7 +55,7 @@ bool HireAction::Execute(Event event) botAI->TellMaster("I will join you at your next relogin"); bot->SetMoney(moneyReq); - sRandomPlayerbotMgr.Remove(bot); + RandomPlayerbotMgr::instance().Remove(bot); CharacterDatabase.Execute("UPDATE characters SET account = {} WHERE guid = {}", account, bot->GetGUID().GetCounter()); diff --git a/src/Ai/Base/Actions/ImbueAction.cpp b/src/Ai/Base/Actions/ImbueAction.cpp index 8c151ef8a6..a6d56d2e50 100644 --- a/src/Ai/Base/Actions/ImbueAction.cpp +++ b/src/Ai/Base/Actions/ImbueAction.cpp @@ -10,7 +10,7 @@ ImbueWithPoisonAction::ImbueWithPoisonAction(PlayerbotAI* botAI) : Action(botAI, "apply poison") {} -bool ImbueWithPoisonAction::Execute(Event event) +bool ImbueWithPoisonAction::Execute(Event /*event*/) { if (bot->IsInCombat()) return false; @@ -103,7 +103,7 @@ bool ImbueWithPoisonAction::Execute(Event event) // Search and apply stone to weapons ImbueWithStoneAction::ImbueWithStoneAction(PlayerbotAI* botAI) : Action(botAI, "apply stone") {} -bool ImbueWithStoneAction::Execute(Event event) +bool ImbueWithStoneAction::Execute(Event /*event*/) { if (bot->IsInCombat()) return false; @@ -148,7 +148,7 @@ bool ImbueWithStoneAction::Execute(Event event) // Search and apply oil to weapons ImbueWithOilAction::ImbueWithOilAction(PlayerbotAI* botAI) : Action(botAI, "apply oil") {} -bool ImbueWithOilAction::Execute(Event event) +bool ImbueWithOilAction::Execute(Event /*event*/) { if (bot->IsInCombat()) return false; @@ -201,7 +201,7 @@ static const uint32 uPrioritizedHealingItemIds[19] = { TryEmergencyAction::TryEmergencyAction(PlayerbotAI* botAI) : Action(botAI, "try emergency") {} -bool TryEmergencyAction::Execute(Event event) +bool TryEmergencyAction::Execute(Event /*event*/) { // Do not use consumable if bot can heal self if ((botAI->IsHeal(bot)) && (bot->GetPowerPct(POWER_MANA) > 20)) diff --git a/src/Ai/Base/Actions/InventoryAction.cpp b/src/Ai/Base/Actions/InventoryAction.cpp index f7b606a91a..83fc00f124 100644 --- a/src/Ai/Base/Actions/InventoryAction.cpp +++ b/src/Ai/Base/Actions/InventoryAction.cpp @@ -351,9 +351,7 @@ uint32 InventoryAction::GetItemCount(FindItemVisitor* visitor, IterateItemsMask std::vector& items = visitor->GetResult(); for (Item* item : items) - { count += item->GetCount(); - } return count; } diff --git a/src/Ai/Base/Actions/InviteToGroupAction.cpp b/src/Ai/Base/Actions/InviteToGroupAction.cpp index 14f943f76c..136f13958a 100644 --- a/src/Ai/Base/Actions/InviteToGroupAction.cpp +++ b/src/Ai/Base/Actions/InviteToGroupAction.cpp @@ -8,7 +8,6 @@ #include "BroadcastHelper.h" #include "Event.h" #include "GuildMgr.h" -#include "Log.h" #include "PlayerbotOperations.h" #include "Playerbots.h" #include "PlayerbotWorldThreadProcessor.h" @@ -44,7 +43,7 @@ bool InviteToGroupAction::Invite(Player* inviter, Player* player) return true; } -bool InviteNearbyToGroupAction::Execute(Event event) +bool InviteNearbyToGroupAction::Execute(Event /*event*/) { GuidVector nearGuids = botAI->GetAiObjectContext()->GetValue("nearest friendly players")->Get(); for (auto& i : nearGuids) @@ -62,7 +61,7 @@ bool InviteNearbyToGroupAction::Execute(Event event) if (player->GetGroup()) continue; - if (!sPlayerbotAIConfig.randomBotInvitePlayer && GET_PLAYERBOT_AI(player)->IsRealPlayer()) + if (!PlayerbotAIConfig::instance().randomBotInvitePlayer && GET_PLAYERBOT_AI(player)->IsRealPlayer()) continue; Group* group = bot->GetGroup(); @@ -88,7 +87,7 @@ bool InviteNearbyToGroupAction::Execute(Event event) if (abs(int32(player->GetLevel() - bot->GetLevel())) > 2) continue; - if (ServerFacade::instance().GetDistance2d(bot, player) > sPlayerbotAIConfig.sightDistance) + if (ServerFacade::instance().GetDistance2d(bot, player) > PlayerbotAIConfig::instance().sightDistance) continue; // When inviting the 5th member of the group convert to raid for future invites. @@ -99,7 +98,7 @@ bool InviteNearbyToGroupAction::Execute(Event event) PlayerbotWorldThreadProcessor::instance().QueueOperation(std::move(convertOp)); } - if (sPlayerbotAIConfig.inviteChat && sRandomPlayerbotMgr.IsRandomBot(bot)) + if (PlayerbotAIConfig::instance().inviteChat && RandomPlayerbotMgr::instance().IsRandomBot(bot)) { std::map placeholders; placeholders["%player"] = player->GetName(); @@ -120,7 +119,7 @@ bool InviteNearbyToGroupAction::Execute(Event event) bool InviteNearbyToGroupAction::isUseful() { - if (!sPlayerbotAIConfig.randomBotGroupNearby) + if (!PlayerbotAIConfig::instance().randomBotGroupNearby) return false; if (bot->InBattleground()) @@ -166,7 +165,7 @@ std::vector InviteGuildToGroupAction::getGuildMembers() return worker.GetResult(); } -bool InviteGuildToGroupAction::Execute(Event event) +bool InviteGuildToGroupAction::Execute(Event /*event*/) { Guild* guild = sGuildMgr->GetGuildById(bot->GetGuildId()); @@ -186,7 +185,7 @@ bool InviteGuildToGroupAction::Execute(Event event) if (player->isDND()) continue; - if (!sPlayerbotAIConfig.randomBotInvitePlayer && GET_PLAYERBOT_AI(player)->IsRealPlayer()) + if (!PlayerbotAIConfig::instance().randomBotInvitePlayer && GET_PLAYERBOT_AI(player)->IsRealPlayer()) continue; if (player->IsBeingTeleported()) @@ -221,7 +220,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 && ServerFacade::instance().GetDistance2d(bot, player) > sPlayerbotAIConfig.sightDistance) + if (!playerAi && ServerFacade::instance().GetDistance2d(bot, player) > PlayerbotAIConfig::instance().sightDistance) continue; Group* group = bot->GetGroup(); @@ -233,8 +232,8 @@ bool InviteGuildToGroupAction::Execute(Event event) PlayerbotWorldThreadProcessor::instance().QueueOperation(std::move(convertOp)); } - if (sPlayerbotAIConfig.inviteChat && - (sRandomPlayerbotMgr.IsRandomBot(bot) || !botAI->HasActivePlayerMaster())) + if (PlayerbotAIConfig::instance().inviteChat && + (RandomPlayerbotMgr::instance().IsRandomBot(bot) || !botAI->HasActivePlayerMaster())) { BroadcastHelper::BroadcastGuildGroupOrRaidInvite(botAI, bot, player, group); } diff --git a/src/Ai/Base/Actions/LeaveGroupAction.cpp b/src/Ai/Base/Actions/LeaveGroupAction.cpp index 03a24bd199..337d115444 100644 --- a/src/Ai/Base/Actions/LeaveGroupAction.cpp +++ b/src/Ai/Base/Actions/LeaveGroupAction.cpp @@ -92,7 +92,7 @@ bool LeaveGroupAction::Leave() return true; } -bool LeaveFarAwayAction::Execute(Event event) +bool LeaveFarAwayAction::Execute(Event /*event*/) { // allow bot to leave party when they want return Leave(); diff --git a/src/Ai/Base/Actions/LfgActions.cpp b/src/Ai/Base/Actions/LfgActions.cpp index a34c3efc63..93e6ff4e33 100644 --- a/src/Ai/Base/Actions/LfgActions.cpp +++ b/src/Ai/Base/Actions/LfgActions.cpp @@ -8,11 +8,11 @@ #include "AiFactory.h" #include "ItemVisitors.h" #include "LFGMgr.h" -#include "LFGPackets.h" #include "Opcodes.h" #include "Playerbots.h" #include "World.h" #include "WorldPacket.h" +#include "RandomPlayerbotMgr.h" using namespace lfg; @@ -20,7 +20,7 @@ bool LfgJoinAction::Execute(Event event) { return JoinLFG(); } uint32 LfgJoinAction::GetRoles() { - if (!sRandomPlayerbotMgr.IsRandomBot(bot)) + if (!RandomPlayerbotMgr::instance().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 = RandomPlayerbotMgr::instance().LfgDungeons[bot->GetTeamId()]; if (!dungeons.size()) return false; @@ -170,7 +170,7 @@ bool LfgJoinAction::JoinLFG() return true; } -bool LfgRoleCheckAction::Execute(Event event) +bool LfgRoleCheckAction::Execute(Event /*event*/) { if (Group* group = bot->GetGroup()) { @@ -216,9 +216,9 @@ bool LfgAcceptAction::Execute(Event event) *packet << id << true; bot->GetSession()->QueuePacket(packet); - if (sRandomPlayerbotMgr.IsRandomBot(bot) && !bot->GetGroup()) + if (RandomPlayerbotMgr::instance().IsRandomBot(bot) && !bot->GetGroup()) { - sRandomPlayerbotMgr.Refresh(bot); + RandomPlayerbotMgr::instance().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 (RandomPlayerbotMgr::instance().IsRandomBot(bot) && !bot->GetGroup()) { - sRandomPlayerbotMgr.Refresh(bot); + RandomPlayerbotMgr::instance().Refresh(bot); botAI->ResetStrategies(); } @@ -265,7 +265,7 @@ bool LfgAcceptAction::Execute(Event event) return false; } -bool LfgLeaveAction::Execute(Event event) +bool LfgLeaveAction::Execute(Event /*event*/) { // Don't leave if lfg strategy enabled // if (botAI->HasStrategy("lfg", BOT_STATE_NON_COMBAT)) @@ -337,7 +337,7 @@ bool LfgJoinAction::isUseful() if (bot->isDead()) return false; - if (!sRandomPlayerbotMgr.IsRandomBot(bot)) + if (!RandomPlayerbotMgr::instance().IsRandomBot(bot)) return false; Map* map = bot->GetMap(); diff --git a/src/Ai/Base/Actions/LootRollAction.cpp b/src/Ai/Base/Actions/LootRollAction.cpp index 9a21c8139f..7bbdc95c1b 100644 --- a/src/Ai/Base/Actions/LootRollAction.cpp +++ b/src/Ai/Base/Actions/LootRollAction.cpp @@ -13,7 +13,7 @@ #include "PlayerbotAIConfig.h" #include "Playerbots.h" -bool LootRollAction::Execute(Event event) +bool LootRollAction::Execute(Event /*event*/) { Group* group = bot->GetGroup(); if (!group) diff --git a/src/Ai/Base/Actions/MoveToRpgTargetAction.cpp b/src/Ai/Base/Actions/MoveToRpgTargetAction.cpp index 4e898f30ef..ad197e4fe6 100644 --- a/src/Ai/Base/Actions/MoveToRpgTargetAction.cpp +++ b/src/Ai/Base/Actions/MoveToRpgTargetAction.cpp @@ -11,20 +11,16 @@ #include "LastMovementValue.h" #include "Playerbots.h" -bool MoveToRpgTargetAction::Execute(Event event) +bool MoveToRpgTargetAction::Execute(Event /*event*/) { GuidPosition guidP = AI_VALUE(GuidPosition, "rpg target"); Unit* unit = botAI->GetUnit(guidP); if (unit && !unit->IsInWorld()) - { return false; - } + GameObject* go = botAI->GetGameObject(guidP); if (go && !go->IsInWorld()) - { return false; - } - Player* player = guidP.GetPlayer(); WorldObject* wo = nullptr; if (unit) diff --git a/src/Ai/Base/Actions/MoveToTravelTargetAction.cpp b/src/Ai/Base/Actions/MoveToTravelTargetAction.cpp index 958dfe7391..f238135d9f 100644 --- a/src/Ai/Base/Actions/MoveToTravelTargetAction.cpp +++ b/src/Ai/Base/Actions/MoveToTravelTargetAction.cpp @@ -7,10 +7,9 @@ #include "ChooseRpgTargetAction.h" #include "LootObjectStack.h" -#include "PathGenerator.h" #include "Playerbots.h" -bool MoveToTravelTargetAction::Execute(Event event) +bool MoveToTravelTargetAction::Execute(Event /*event*/) { TravelTarget* target = AI_VALUE(TravelTarget*, "travel target"); diff --git a/src/Ai/Base/Actions/MovementActions.cpp b/src/Ai/Base/Actions/MovementActions.cpp index 4f04d17995..2b0359254e 100644 --- a/src/Ai/Base/Actions/MovementActions.cpp +++ b/src/Ai/Base/Actions/MovementActions.cpp @@ -15,7 +15,6 @@ #include "FleeManager.h" #include "G3D/Vector3.h" #include "GameObject.h" -#include "Geometry.h" #include "LastMovementValue.h" #include "LootObjectStack.h" #include "Map.h" @@ -36,9 +35,7 @@ #include "SpellAuraEffects.h" #include "SpellInfo.h" #include "Stances.h" -#include "TargetedMovementGenerator.h" #include "Timer.h" -#include "Transport.h" #include "Unit.h" #include "Vehicle.h" #include "WaypointMovementGenerator.h" @@ -67,18 +64,14 @@ bool MovementAction::JumpTo(uint32 mapId, float x, float y, float z, MovementPri { UpdateMovementState(); if (!IsMovingAllowed(mapId, x, y, z)) - { return false; - } + if (IsDuplicateMove(mapId, x, y, z)) - { return false; - } + if (IsWaitingForLastMove(priority)) - { return false; - } - float botZ = bot->GetPositionZ(); + float speed = bot->GetSpeed(MOVE_RUN); MotionMaster& mm = *bot->GetMotionMaster(); mm.Clear(); @@ -1846,7 +1839,7 @@ void MovementAction::DoMovePoint(Unit* unit, float x, float y, float z, bool gen } } -bool FleeAction::Execute(Event event) +bool FleeAction::Execute(Event /*event*/) { return MoveAway(AI_VALUE(Unit*, "current target"), sPlayerbotAIConfig.fleeDistance, true); } @@ -1854,9 +1847,8 @@ bool FleeAction::Execute(Event event) bool FleeAction::isUseful() { if (bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL) != nullptr) - { return false; - } + Unit* target = AI_VALUE(Unit*, "current target"); if (target && target->IsInWorld() && !bot->IsWithinMeleeRange(target)) return false; @@ -1864,12 +1856,10 @@ bool FleeAction::isUseful() return true; } -bool FleeWithPetAction::Execute(Event event) +bool FleeWithPetAction::Execute(Event /*event*/) { if (Pet* pet = bot->GetPet()) - { botAI->PetFollow(); - } return Flee(AI_VALUE(Unit*, "current target")); } @@ -1877,15 +1867,14 @@ bool FleeWithPetAction::Execute(Event event) bool AvoidAoeAction::isUseful() { if (getMSTime() - moveInterval < lastMoveTimer) - { return false; - } + GuidVector traps = AI_VALUE(GuidVector, "nearest trap with damage"); GuidVector triggers = AI_VALUE(GuidVector, "possible triggers"); return AI_VALUE(Aura*, "area debuff") || !traps.empty() || !triggers.empty(); } -bool AvoidAoeAction::Execute(Event event) +bool AvoidAoeAction::Execute(Event /*event*/) { // Case #1: Aura with dynamic object (e.g. rain of fire) if (AvoidAuraWithDynamicObj()) @@ -2309,17 +2298,15 @@ bool MovementAction::CheckLastFlee(float curAngle, std::list& infoList bool CombatFormationMoveAction::isUseful() { if (getMSTime() - moveInterval < lastMoveTimer) - { return false; - } + if (bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL) != nullptr) - { return false; - } + return true; } -bool CombatFormationMoveAction::Execute(Event event) +bool CombatFormationMoveAction::Execute(Event /*event*/) { float dis = AI_VALUE(float, "disperse distance"); if (dis <= 0.0f || (!bot->IsInCombat() && botAI->HasStrategy("stay", BotState::BOT_STATE_NON_COMBAT)) || @@ -2450,7 +2437,7 @@ Player* CombatFormationMoveAction::NearestGroupMember(float dis) return result; } -bool TankFaceAction::Execute(Event event) +bool TankFaceAction::Execute(Event /*event*/) { Unit* target = AI_VALUE(Unit*, "current target"); if (!target) @@ -2534,7 +2521,7 @@ bool RearFlankAction::isUseful() return inFront || inRear; } -bool RearFlankAction::Execute(Event event) +bool RearFlankAction::Execute(Event /*event*/) { Unit* target = AI_VALUE(Unit*, "current target"); if (!target) @@ -2645,9 +2632,9 @@ bool DisperseSetAction::Execute(Event event) return true; } -bool RunAwayAction::Execute(Event event) { return Flee(AI_VALUE(Unit*, "group leader")); } +bool RunAwayAction::Execute(Event /*event*/) { return Flee(AI_VALUE(Unit*, "group leader")); } -bool MoveToLootAction::Execute(Event event) +bool MoveToLootAction::Execute(Event /*event*/) { LootObject loot = AI_VALUE(LootObject, "loot target"); if (!loot.IsLootPossible(bot)) @@ -2656,7 +2643,7 @@ bool MoveToLootAction::Execute(Event event) return MoveNear(loot.GetWorldObject(bot), sPlayerbotAIConfig.contactDistance); } -bool MoveOutOfEnemyContactAction::Execute(Event event) +bool MoveOutOfEnemyContactAction::Execute(Event /*event*/) { Unit* target = AI_VALUE(Unit*, "current target"); if (!target) @@ -2667,7 +2654,7 @@ bool MoveOutOfEnemyContactAction::Execute(Event event) bool MoveOutOfEnemyContactAction::isUseful() { return AI_VALUE2(bool, "inside target", "current target"); } -bool SetFacingTargetAction::Execute(Event event) +bool SetFacingTargetAction::Execute(Event /*event*/) { Unit* target = AI_VALUE(Unit*, "current target"); if (!target) @@ -2693,7 +2680,7 @@ bool SetFacingTargetAction::isPossible() return true; } -bool SetBehindTargetAction::Execute(Event event) +bool SetBehindTargetAction::Execute(Event /*event*/) { Unit* target = AI_VALUE(Unit*, "current target"); if (!target) @@ -2753,7 +2740,7 @@ bool SetBehindTargetAction::Execute(Event event) false, true, MovementPriority::MOVEMENT_COMBAT); } -bool MoveOutOfCollisionAction::Execute(Event event) +bool MoveOutOfCollisionAction::Execute(Event /*event*/) { float angle = M_PI * 2000 / frand(1.f, 1000.f); float distance = sPlayerbotAIConfig.followDistance; @@ -2771,7 +2758,7 @@ bool MoveOutOfCollisionAction::isUseful() botAI->GetAiObjectContext()->GetValue("nearest friendly players")->Get().size() < 15; } -bool MoveRandomAction::Execute(Event event) +bool MoveRandomAction::Execute(Event /*event*/) { float distance = sPlayerbotAIConfig.tooCloseDistance + urand(10, 30); @@ -2803,9 +2790,9 @@ bool MoveRandomAction::Execute(Event event) bool MoveRandomAction::isUseful() { return !AI_VALUE(GuidPosition, "rpg target"); } -bool MoveInsideAction::Execute(Event event) { return MoveInside(bot->GetMapId(), x, y, bot->GetPositionZ(), distance); } +bool MoveInsideAction::Execute(Event /*event*/) { return MoveInside(bot->GetMapId(), x, y, bot->GetPositionZ(), distance); } -bool RotateAroundTheCenterPointAction::Execute(Event event) +bool RotateAroundTheCenterPointAction::Execute(Event /*event*/) { uint32 next_point = GetCurrWaypoint(); if (MoveTo(bot->GetMapId(), waypoints[next_point].first, waypoints[next_point].second, bot->GetPositionZ(), false, @@ -2825,10 +2812,9 @@ bool MoveFromGroupAction::Execute(Event event) return MoveFromGroup(distance); } -bool MoveAwayFromCreatureAction::Execute(Event event) +bool MoveAwayFromCreatureAction::Execute(Event /*event*/) { GuidVector targets = AI_VALUE(GuidVector, "nearest npcs"); - Creature* nearestCreature = bot->FindNearestCreature(creatureId, range, alive); // Find all creatures with the specified Id std::vector creatures; @@ -2906,16 +2892,14 @@ bool MoveAwayFromCreatureAction::Execute(Event event) bool MoveAwayFromCreatureAction::isPossible() { return bot->CanFreeMove(); } -bool MoveAwayFromPlayerWithDebuffAction::Execute(Event event) +bool MoveAwayFromPlayerWithDebuffAction::Execute(Event /*event*/) { Player* closestPlayer = nullptr; float minDistance = 0.0f; Group* group = bot->GetGroup(); if (!group) - { return false; - } std::vector debuffedPlayers; diff --git a/src/Ai/Base/Actions/OpenItemAction.cpp b/src/Ai/Base/Actions/OpenItemAction.cpp index d1ebcbaaaf..0ea7560ed4 100644 --- a/src/Ai/Base/Actions/OpenItemAction.cpp +++ b/src/Ai/Base/Actions/OpenItemAction.cpp @@ -7,7 +7,7 @@ #include "LootObjectStack.h" #include "AiObjectContext.h" -bool OpenItemAction::Execute(Event event) +bool OpenItemAction::Execute(Event /*event*/) { bool foundOpenable = false; diff --git a/src/Ai/Base/Actions/PassLeadershipToMasterAction.cpp b/src/Ai/Base/Actions/PassLeadershipToMasterAction.cpp index 4ecfdc3bc1..e2e9f0fe6b 100644 --- a/src/Ai/Base/Actions/PassLeadershipToMasterAction.cpp +++ b/src/Ai/Base/Actions/PassLeadershipToMasterAction.cpp @@ -7,10 +7,9 @@ #include "Event.h" #include "PlayerbotOperations.h" -#include "Playerbots.h" #include "PlayerbotWorldThreadProcessor.h" -bool PassLeadershipToMasterAction::Execute(Event event) +bool PassLeadershipToMasterAction::Execute(Event /*event*/) { if (Player* master = GetMaster()) if (master && master != bot && bot->GetGroup() && bot->GetGroup()->IsMember(master->GetGUID())) diff --git a/src/Ai/Base/Actions/PetsAction.h b/src/Ai/Base/Actions/PetsAction.h index f9334e3194..78f9c0b50a 100644 --- a/src/Ai/Base/Actions/PetsAction.h +++ b/src/Ai/Base/Actions/PetsAction.h @@ -9,8 +9,6 @@ #include #include "Action.h" -#include "PlayerbotFactory.h" -#include "Unit.h" class PlayerbotAI; diff --git a/src/Ai/Base/Actions/PositionAction.cpp b/src/Ai/Base/Actions/PositionAction.cpp index 7d8f4264ca..34d339f2c8 100644 --- a/src/Ai/Base/Actions/PositionAction.cpp +++ b/src/Ai/Base/Actions/PositionAction.cpp @@ -102,7 +102,7 @@ bool PositionAction::Execute(Event event) return false; } -bool MoveToPositionAction::Execute(Event event) +bool MoveToPositionAction::Execute(Event /*event*/) { PositionInfo pos = context->GetValue("position")->Get()[qualifier]; if (!pos.isSet()) @@ -123,7 +123,7 @@ bool MoveToPositionAction::isUseful() return pos.isSet() && distance > sPlayerbotAIConfig.followDistance && distance < sPlayerbotAIConfig.reactDistance; } -bool SetReturnPositionAction::Execute(Event event) +bool SetReturnPositionAction::Execute(Event /*event*/) { PositionMap& posMap = context->GetValue("position")->Get(); PositionInfo returnPos = posMap["return"]; diff --git a/src/Ai/Base/Actions/QueryQuestAction.cpp b/src/Ai/Base/Actions/QueryQuestAction.cpp index 5f5d63e08c..9540a9bf89 100644 --- a/src/Ai/Base/Actions/QueryQuestAction.cpp +++ b/src/Ai/Base/Actions/QueryQuestAction.cpp @@ -7,7 +7,7 @@ #include "ChatHelper.h" #include "Event.h" -#include "Playerbots.h" +#include "PlayerbotAI.h" void QueryQuestAction::TellObjective(std::string const name, uint32 available, uint32 required) { diff --git a/src/Ai/Base/Actions/QuestAction.cpp b/src/Ai/Base/Actions/QuestAction.cpp index dfe7bb9f1e..f442f91a73 100644 --- a/src/Ai/Base/Actions/QuestAction.cpp +++ b/src/Ai/Base/Actions/QuestAction.cpp @@ -5,6 +5,7 @@ #include "QuestAction.h" #include +#include #include "Chat.h" #include "ChatHelper.h" @@ -116,7 +117,8 @@ bool QuestAction::CompleteQuest(Player* player, uint32 entry) player->CastedCreatureOrGO(creature, ObjectGuid(), spell_id); } }*/ - /*else*/ if (creature > 0) + /*else*/ + if (creature > 0) { if (CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(creature)) for (uint16 z = 0; z < creaturecount; ++z) @@ -432,7 +434,7 @@ bool QuestItemPushResultAction::Execute(Event event) return false; } -bool QuestUpdateFailedAction::Execute(Event event) +bool QuestUpdateFailedAction::Execute(Event /*event*/) { //opcode SMSG_QUESTUPDATE_FAILED is never sent...(yet?) return false; diff --git a/src/Ai/Base/Actions/RandomBotUpdateAction.cpp b/src/Ai/Base/Actions/RandomBotUpdateAction.cpp index 76ed46c901..7d7cba6055 100644 --- a/src/Ai/Base/Actions/RandomBotUpdateAction.cpp +++ b/src/Ai/Base/Actions/RandomBotUpdateAction.cpp @@ -8,7 +8,7 @@ #include "Event.h" #include "Playerbots.h" -bool RandomBotUpdateAction::Execute(Event event) +bool RandomBotUpdateAction::Execute(Event /*event*/) { if (!sRandomPlayerbotMgr.IsRandomBot(bot)) return false; diff --git a/src/Ai/Base/Actions/ReachTargetActions.cpp b/src/Ai/Base/Actions/ReachTargetActions.cpp index a58e9b96ea..03eecda65c 100644 --- a/src/Ai/Base/Actions/ReachTargetActions.cpp +++ b/src/Ai/Base/Actions/ReachTargetActions.cpp @@ -10,7 +10,7 @@ #include "Playerbots.h" #include "ServerFacade.h" -bool ReachTargetAction::Execute(Event event) { return ReachCombatTo(AI_VALUE(Unit*, GetTargetName()), distance); } +bool ReachTargetAction::Execute(Event /*event*/) { return ReachCombatTo(AI_VALUE(Unit*, GetTargetName()), distance); } bool ReachTargetAction::isUseful() { diff --git a/src/Ai/Base/Actions/ReleaseSpiritAction.cpp b/src/Ai/Base/Actions/ReleaseSpiritAction.cpp index 9e3ec445d2..9a923d4fd3 100644 --- a/src/Ai/Base/Actions/ReleaseSpiritAction.cpp +++ b/src/Ai/Base/Actions/ReleaseSpiritAction.cpp @@ -78,7 +78,7 @@ void ReleaseSpiritAction::LogRelease(const std::string& releaseMsg, bool isAutoR } // AutoReleaseSpiritAction implementation -bool AutoReleaseSpiritAction::Execute(Event event) +bool AutoReleaseSpiritAction::Execute(Event /*event*/) { IncrementDeathCount(); bot->DurabilityRepairAll(false, 1.0f, false); @@ -214,7 +214,7 @@ bool AutoReleaseSpiritAction::ShouldDelayBattlegroundRelease() const return true; } -bool RepopAction::Execute(Event event) +bool RepopAction::Execute(Event /*event*/) { const GraveyardStruct* graveyard = GetGrave( AI_VALUE(uint32, "death count") > 10 || @@ -250,7 +250,7 @@ void RepopAction::PerformGraveyardTeleport(const GraveyardStruct* graveyard) con } // SelfResurrectAction implementation for Warlock's Soulstone Resurrection/Shaman's Reincarnation -bool SelfResurrectAction::Execute(Event event) +bool SelfResurrectAction::Execute(Event /*event*/) { if (!bot->IsAlive() && bot->GetUInt32Value(PLAYER_SELF_RES_SPELL)) { diff --git a/src/Ai/Base/Actions/RememberTaxiAction.cpp b/src/Ai/Base/Actions/RememberTaxiAction.cpp index af5a540370..16132d7cb8 100644 --- a/src/Ai/Base/Actions/RememberTaxiAction.cpp +++ b/src/Ai/Base/Actions/RememberTaxiAction.cpp @@ -7,7 +7,7 @@ #include "Event.h" #include "LastMovementValue.h" -#include "Playerbots.h" +#include "AiObjectContext.h" bool RememberTaxiAction::Execute(Event event) { diff --git a/src/Ai/Base/Actions/RepairAllAction.cpp b/src/Ai/Base/Actions/RepairAllAction.cpp index a66ba3a1bb..efb20d88d3 100644 --- a/src/Ai/Base/Actions/RepairAllAction.cpp +++ b/src/Ai/Base/Actions/RepairAllAction.cpp @@ -9,7 +9,7 @@ #include "Event.h" #include "Playerbots.h" -bool RepairAllAction::Execute(Event event) +bool RepairAllAction::Execute(Event /*event*/) { GuidVector npcs = AI_VALUE(GuidVector, "nearest npcs"); for (ObjectGuid const guid : npcs) diff --git a/src/Ai/Base/Actions/ResetInstancesAction.cpp b/src/Ai/Base/Actions/ResetInstancesAction.cpp index cce5eef1e3..eef29fc946 100644 --- a/src/Ai/Base/Actions/ResetInstancesAction.cpp +++ b/src/Ai/Base/Actions/ResetInstancesAction.cpp @@ -5,14 +5,13 @@ #include "ResetInstancesAction.h" -#include "Playerbots.h" +#include "PlayerbotAI.h" -bool ResetInstancesAction::Execute(Event event) +bool ResetInstancesAction::Execute(Event /*event*/) { WorldPacket packet(CMSG_RESET_INSTANCES, 0); bot->GetSession()->HandleResetInstancesOpcode(packet); - botAI->TellMaster("Resetting all instances"); return true; } diff --git a/src/Ai/Base/Actions/RevealGatheringItemAction.cpp b/src/Ai/Base/Actions/RevealGatheringItemAction.cpp index 35cd7f8f04..72b0ef2ec9 100644 --- a/src/Ai/Base/Actions/RevealGatheringItemAction.cpp +++ b/src/Ai/Base/Actions/RevealGatheringItemAction.cpp @@ -10,11 +10,11 @@ #include "Event.h" #include "GridNotifiers.h" #include "GridNotifiersImpl.h" -#include "Playerbots.h" +#include "PlayerbotAI.h" #include "ServerFacade.h" #include "NearestGameObjects.h" -bool RevealGatheringItemAction::Execute(Event event) +bool RevealGatheringItemAction::Execute(Event /*event*/) { if (!bot->GetGroup()) return false; diff --git a/src/Ai/Base/Actions/ReviveFromCorpseAction.cpp b/src/Ai/Base/Actions/ReviveFromCorpseAction.cpp index 970d378f47..0051a5a49b 100644 --- a/src/Ai/Base/Actions/ReviveFromCorpseAction.cpp +++ b/src/Ai/Base/Actions/ReviveFromCorpseAction.cpp @@ -9,7 +9,6 @@ #include "FleeManager.h" #include "GameGraveyard.h" #include "MapMgr.h" -#include "PlayerbotFactory.h" #include "Playerbots.h" #include "RandomPlayerbotMgr.h" #include "ServerFacade.h" @@ -74,7 +73,7 @@ bool ReviveFromCorpseAction::Execute(Event event) return true; } -bool FindCorpseAction::Execute(Event event) +bool FindCorpseAction::Execute(Event /*event*/) { if (bot->InBattleground()) return false; @@ -293,7 +292,7 @@ GraveyardStruct const* SpiritHealerAction::GetGrave(bool startZone) return ClosestGrave; } -bool SpiritHealerAction::Execute(Event event) +bool SpiritHealerAction::Execute(Event /*event*/) { Corpse* corpse = bot->GetCorpse(); if (!corpse) diff --git a/src/Ai/Base/Actions/RpgAction.cpp b/src/Ai/Base/Actions/RpgAction.cpp index 1e461b068e..73068a0b1b 100644 --- a/src/Ai/Base/Actions/RpgAction.cpp +++ b/src/Ai/Base/Actions/RpgAction.cpp @@ -7,7 +7,6 @@ #include -#include "BattlegroundMgr.h" #include "ChatHelper.h" #include "EmoteAction.h" #include "Event.h" @@ -16,7 +15,7 @@ #include "ServerFacade.h" #include "RpgSubActions.h" -bool RpgAction::Execute(Event event) +bool RpgAction::Execute(Event /*event*/) { GuidPosition guidP = AI_VALUE(GuidPosition, "rpg target"); if (!guidP && botAI->GetMaster()) diff --git a/src/Ai/Base/Actions/RpgSubActions.cpp b/src/Ai/Base/Actions/RpgSubActions.cpp index 3f0947ac83..de16ed17de 100644 --- a/src/Ai/Base/Actions/RpgSubActions.cpp +++ b/src/Ai/Base/Actions/RpgSubActions.cpp @@ -99,7 +99,7 @@ Event RpgSubAction::ActionEvent(Event event) { return event; } bool RpgStayAction::isUseful() { return rpg->InRange() && !botAI->HasRealPlayerMaster(); } -bool RpgStayAction::Execute(Event event) +bool RpgStayAction::Execute(Event /*event*/) { bot->PlayerTalkClass->SendCloseGossip(); @@ -109,7 +109,7 @@ bool RpgStayAction::Execute(Event event) bool RpgWorkAction::isUseful() { return rpg->InRange() && !botAI->HasRealPlayerMaster(); } -bool RpgWorkAction::Execute(Event event) +bool RpgWorkAction::Execute(Event /*event*/) { bot->HandleEmoteCommand(EMOTE_STATE_USE_STANDING); rpg->AfterExecute(); @@ -118,7 +118,7 @@ bool RpgWorkAction::Execute(Event event) bool RpgEmoteAction::isUseful() { return rpg->InRange() && !botAI->HasRealPlayerMaster(); } -bool RpgEmoteAction::Execute(Event event) +bool RpgEmoteAction::Execute(Event /*event*/) { uint32 type = TalkAction::GetRandomEmote(rpg->guidP().GetUnit()); @@ -133,7 +133,7 @@ bool RpgEmoteAction::Execute(Event event) return true; } -bool RpgCancelAction::Execute(Event event) +bool RpgCancelAction::Execute(Event /*event*/) { RESET_AI_VALUE(GuidPosition, "rpg target"); rpg->OnExecute(""); @@ -142,7 +142,7 @@ bool RpgCancelAction::Execute(Event event) bool RpgTaxiAction::isUseful() { return rpg->InRange() && !botAI->HasRealPlayerMaster(); } -bool RpgTaxiAction::Execute(Event event) +bool RpgTaxiAction::Execute(Event /*event*/) { GuidPosition guidP = rpg->guidP(); @@ -203,7 +203,7 @@ bool RpgTaxiAction::Execute(Event event) return true; } -bool RpgDiscoverAction::Execute(Event event) +bool RpgDiscoverAction::Execute(Event /*event*/) { GuidPosition guidP = rpg->guidP(); @@ -222,7 +222,7 @@ bool RpgDiscoverAction::Execute(Event event) std::string const RpgStartQuestAction::ActionName() { return "accept all quests"; } -Event RpgStartQuestAction::ActionEvent(Event event) +Event RpgStartQuestAction::ActionEvent(Event /*event*/) { WorldPacket p(CMSG_QUESTGIVER_ACCEPT_QUEST); p << rpg->guid(); @@ -232,7 +232,7 @@ Event RpgStartQuestAction::ActionEvent(Event event) std::string const RpgEndQuestAction::ActionName() { return "talk to quest giver"; } -Event RpgEndQuestAction::ActionEvent(Event event) +Event RpgEndQuestAction::ActionEvent(Event /*event*/) { WorldPacket p(CMSG_QUESTGIVER_COMPLETE_QUEST); p << rpg->guid(); @@ -242,17 +242,17 @@ Event RpgEndQuestAction::ActionEvent(Event event) std::string const RpgBuyAction::ActionName() { return "buy"; } -Event RpgBuyAction::ActionEvent(Event event) { return Event("rpg action", "vendor"); } +Event RpgBuyAction::ActionEvent(Event /*event*/) { return Event("rpg action", "vendor"); } std::string const RpgSellAction::ActionName() { return "sell"; } -Event RpgSellAction::ActionEvent(Event event) { return Event("rpg action", "vendor"); } +Event RpgSellAction::ActionEvent(Event /*event*/) { return Event("rpg action", "vendor"); } std::string const RpgRepairAction::ActionName() { return "repair"; } std::string const RpgTrainAction::ActionName() { return "trainer"; } -bool RpgHealAction::Execute(Event event) +bool RpgHealAction::Execute(Event /*event*/) { bool retVal = false; @@ -287,21 +287,21 @@ std::string const RpgBuyPetitionAction::ActionName() { return "buy petition"; } std::string const RpgUseAction::ActionName() { return "use"; } -Event RpgUseAction::ActionEvent(Event event) +Event RpgUseAction::ActionEvent(Event /*event*/) { return Event("rpg action", chat->FormatWorldobject(rpg->guidP().GetWorldObject())); } std::string const RpgSpellAction::ActionName() { return "cast random spell"; } -Event RpgSpellAction::ActionEvent(Event event) +Event RpgSpellAction::ActionEvent(Event /*event*/) { return Event("rpg action", chat->FormatWorldobject(rpg->guidP().GetWorldObject())); } std::string const RpgCraftAction::ActionName() { return "craft random item"; } -Event RpgCraftAction::ActionEvent(Event event) +Event RpgCraftAction::ActionEvent(Event /*event*/) { return Event("rpg action", chat->FormatWorldobject(rpg->guidP().GetWorldObject())); } @@ -341,7 +341,7 @@ std::vector RpgTradeUsefulAction::CanGiveItems(GuidPosition guidPosition) return giveItems; } -bool RpgTradeUsefulAction::Execute(Event event) +bool RpgTradeUsefulAction::Execute(Event /*event*/) { GuidPosition guidP = AI_VALUE(GuidPosition, "rpg target"); @@ -416,7 +416,7 @@ bool RpgDuelAction::isUseful() return true; } -bool RpgDuelAction::Execute(Event event) +bool RpgDuelAction::Execute(Event /*event*/) { GuidPosition guidP = AI_VALUE(GuidPosition, "rpg target"); @@ -434,7 +434,7 @@ bool RpgMountAnimAction::isUseful() return AI_VALUE2(bool, "mounted", "self target") && !AI_VALUE2(bool, "moving", "self target"); } -bool RpgMountAnimAction::Execute(Event event) +bool RpgMountAnimAction::Execute(Event /*event*/) { WorldPacket p; bot->GetSession()->HandleMountSpecialAnimOpcode(p); diff --git a/src/Ai/Base/Actions/RtiAction.cpp b/src/Ai/Base/Actions/RtiAction.cpp index b34b3eec3d..807a59724d 100644 --- a/src/Ai/Base/Actions/RtiAction.cpp +++ b/src/Ai/Base/Actions/RtiAction.cpp @@ -55,7 +55,7 @@ void RtiAction::AppendRti(std::ostringstream& out, std::string const type) out << " (" << target->GetName() << ")"; } -bool MarkRtiAction::Execute(Event event) +bool MarkRtiAction::Execute(Event /*event*/) { Group* group = bot->GetGroup(); if (!group) diff --git a/src/Ai/Base/Actions/SayAction.cpp b/src/Ai/Base/Actions/SayAction.cpp index 050d7ddbba..4505a76ef1 100644 --- a/src/Ai/Base/Actions/SayAction.cpp +++ b/src/Ai/Base/Actions/SayAction.cpp @@ -9,9 +9,7 @@ #include #include -#include "ChannelMgr.h" #include "Event.h" -#include "GuildMgr.h" #include "PlayerbotTextMgr.h" #include "Playerbots.h" @@ -56,7 +54,7 @@ static const std::unordered_set noReplyMsgStarts = {"e ", "accept " SayAction::SayAction(PlayerbotAI* botAI) : Action(botAI, "say"), Qualified() {} -bool SayAction::Execute(Event event) +bool SayAction::Execute(Event /*event*/) { std::string text = ""; std::map placeholders; @@ -92,7 +90,6 @@ bool SayAction::Execute(Event event) } // set delay before next say - time_t lastSaid = AI_VALUE2(time_t, "last said", qualifier); uint32 nextTime = time(nullptr) + urand(1, 30); botAI->GetAiObjectContext()->GetValue("last said", qualifier)->Set(nextTime); diff --git a/src/Ai/Base/Actions/SecurityCheckAction.cpp b/src/Ai/Base/Actions/SecurityCheckAction.cpp index ede59440e3..512d7357f8 100644 --- a/src/Ai/Base/Actions/SecurityCheckAction.cpp +++ b/src/Ai/Base/Actions/SecurityCheckAction.cpp @@ -14,7 +14,7 @@ bool SecurityCheckAction::isUseful() botAI->GetMaster()->GetSession()->GetSecurity() < SEC_GAMEMASTER && !GET_PLAYERBOT_AI(botAI->GetMaster()); } -bool SecurityCheckAction::Execute(Event event) +bool SecurityCheckAction::Execute(Event /*event*/) { if (Group* group = bot->GetGroup()) { diff --git a/src/Ai/Base/Actions/SeeSpellAction.cpp b/src/Ai/Base/Actions/SeeSpellAction.cpp index 03117586e4..f7d7eab239 100644 --- a/src/Ai/Base/Actions/SeeSpellAction.cpp +++ b/src/Ai/Base/Actions/SeeSpellAction.cpp @@ -7,7 +7,6 @@ #include "Event.h" #include "Formations.h" -#include "PathGenerator.h" #include "Playerbots.h" #include "RTSCValues.h" #include "RtscAction.h" diff --git a/src/Ai/Base/Actions/SetCraftAction.cpp b/src/Ai/Base/Actions/SetCraftAction.cpp index f062ce5c23..f8682c45eb 100644 --- a/src/Ai/Base/Actions/SetCraftAction.cpp +++ b/src/Ai/Base/Actions/SetCraftAction.cpp @@ -49,9 +49,7 @@ bool SetCraftAction::Execute(Event event) if (skillSpells.empty()) { for (SkillLineAbilityEntry const* skillLine : sSkillLineAbilityStore) - { skillSpells[skillLine->Spell] = skillLine; - } } data.required.clear(); @@ -78,9 +76,7 @@ bool SetCraftAction::Execute(Event event) for (uint32 x = 0; x < MAX_SPELL_REAGENTS; ++x) { if (spellInfo->Reagent[x] <= 0) - { continue; - } uint32 itemid = spellInfo->Reagent[x]; uint32 reagentsRequired = spellInfo->ReagentCount[x]; @@ -132,9 +128,8 @@ void SetCraftAction::TellCraft() if (ItemTemplate const* reagent = sObjectMgr->GetItemTemplate(item)) { if (first) - { first = false; - } + else out << ", "; @@ -142,9 +137,7 @@ void SetCraftAction::TellCraft() uint32 given = data.obtained[item]; if (given) - { out << "|cffffff00(x" << given << " given)|r "; - } } } diff --git a/src/Ai/Base/Actions/SetHomeAction.cpp b/src/Ai/Base/Actions/SetHomeAction.cpp index 44f3c7b49f..0fa2ef50b4 100644 --- a/src/Ai/Base/Actions/SetHomeAction.cpp +++ b/src/Ai/Base/Actions/SetHomeAction.cpp @@ -8,7 +8,7 @@ #include "Event.h" #include "Playerbots.h" -bool SetHomeAction::Execute(Event event) +bool SetHomeAction::Execute(Event /*event*/) { Player* master = GetMaster(); diff --git a/src/Ai/Base/Actions/StatsAction.cpp b/src/Ai/Base/Actions/StatsAction.cpp index f6872c8468..aaf8d1bd13 100644 --- a/src/Ai/Base/Actions/StatsAction.cpp +++ b/src/Ai/Base/Actions/StatsAction.cpp @@ -7,9 +7,9 @@ #include "ChatHelper.h" #include "Event.h" -#include "Playerbots.h" +#include "PlayerbotAI.h" -bool StatsAction::Execute(Event event) +bool StatsAction::Execute(Event /*event*/) { std::ostringstream out; diff --git a/src/Ai/Base/Actions/StayActions.cpp b/src/Ai/Base/Actions/StayActions.cpp index dbc7abd090..ad10e89907 100644 --- a/src/Ai/Base/Actions/StayActions.cpp +++ b/src/Ai/Base/Actions/StayActions.cpp @@ -39,7 +39,7 @@ bool StayActionBase::Stay() return true; } -bool StayAction::Execute(Event event) { return Stay(); } +bool StayAction::Execute(Event /*event*/) { return Stay(); } bool StayAction::isUseful() { @@ -49,9 +49,7 @@ bool StayAction::isUseful() { const float distance = bot->GetDistance(stayPosition.x, stayPosition.y, stayPosition.z); if (sPlayerbotAIConfig.followDistance) - { return false; - } } // move from group takes priority over stay as it's added and removed automatically @@ -64,7 +62,7 @@ bool StayAction::isUseful() return AI_VALUE2(bool, "moving", "self target"); } -bool SitAction::Execute(Event event) +bool SitAction::Execute(Event /*event*/) { if (bot->isMoving()) return false; diff --git a/src/Ai/Base/Actions/SuggestWhatToDoAction.cpp b/src/Ai/Base/Actions/SuggestWhatToDoAction.cpp index 9012443beb..5bd1911406 100644 --- a/src/Ai/Base/Actions/SuggestWhatToDoAction.cpp +++ b/src/Ai/Base/Actions/SuggestWhatToDoAction.cpp @@ -7,25 +7,19 @@ #include "SuggestWhatToDoAction.h" #include "ServerFacade.h" -#include "ChannelMgr.h" #include "Event.h" #include "ItemVisitors.h" #include "AiFactory.h" #include "ChatHelper.h" #include "Playerbots.h" -#include "PlayerbotTextMgr.h" -#include "Config.h" #include "BroadcastHelper.h" #include "AiFactory.h" -#include "ChannelMgr.h" #include "ChatHelper.h" -#include "Config.h" #include "Event.h" -#include "GuildMgr.h" #include "ItemVisitors.h" -#include "PlayerbotTextMgr.h" #include "Playerbots.h" #include "ServerFacade.h" +#include "Channel.h" enum eTalkType { @@ -62,7 +56,7 @@ bool SuggestWhatToDoAction::isUseful() return (time(0) - lastSaid) > 30; } -bool SuggestWhatToDoAction::Execute(Event event) +bool SuggestWhatToDoAction::Execute(Event /*event*/) { uint32 index = rand() % suggestions.size(); auto fnct_ptr = suggestions[index]; @@ -258,7 +252,7 @@ class FindTradeItemsVisitor : public IterateItemsVisitor SuggestDungeonAction::SuggestDungeonAction(PlayerbotAI* botAI) : SuggestWhatToDoAction(botAI, "suggest dungeon") {} -bool SuggestDungeonAction::Execute(Event event) +bool SuggestDungeonAction::Execute(Event /*event*/) { // TODO: use PlayerbotDungeonRepository::instance() @@ -325,7 +319,7 @@ bool SuggestDungeonAction::Execute(Event event) SuggestTradeAction::SuggestTradeAction(PlayerbotAI* botAI) : SuggestWhatToDoAction(botAI, "suggest trade") {} -bool SuggestTradeAction::Execute(Event event) +bool SuggestTradeAction::Execute(Event /*event*/) { uint32 quality = urand(0, 100); if (quality > 95) diff --git a/src/Ai/Base/Actions/TameAction.cpp b/src/Ai/Base/Actions/TameAction.cpp index 5b3eda1034..e968a0b01b 100644 --- a/src/Ai/Base/Actions/TameAction.cpp +++ b/src/Ai/Base/Actions/TameAction.cpp @@ -6,12 +6,10 @@ #include "TameAction.h" #include #include -#include #include #include #include #include "DBCStructure.h" -#include "Log.h" #include "ObjectMgr.h" #include "Pet.h" #include "Player.h" diff --git a/src/Ai/Base/Actions/TeleportAction.cpp b/src/Ai/Base/Actions/TeleportAction.cpp index 65aa24e09f..b981decbf1 100644 --- a/src/Ai/Base/Actions/TeleportAction.cpp +++ b/src/Ai/Base/Actions/TeleportAction.cpp @@ -7,9 +7,12 @@ #include "Event.h" #include "LastMovementValue.h" -#include "Playerbots.h" +#include "AiObjectContext.h" +#include "PlayerbotAI.h" +#include "SpellMgr.h" +#include "Spell.h" -bool TeleportAction::Execute(Event event) +bool TeleportAction::Execute(Event /*event*/) { /* // List of allowed portal entries (you can populate this dynamically) @@ -74,7 +77,7 @@ bool TeleportAction::Execute(Event event) continue; uint32 spellId = goInfo->spellcaster.spellId; - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); + SpellInfo const* spellInfo = SpellMgr::instance()->GetSpellInfo(spellId); if (!spellInfo || !spellInfo->HasEffect(SPELL_EFFECT_TELEPORT_UNITS)) continue; diff --git a/src/Ai/Base/Actions/TellLosAction.cpp b/src/Ai/Base/Actions/TellLosAction.cpp index 13852bdc0a..3d18c5826f 100644 --- a/src/Ai/Base/Actions/TellLosAction.cpp +++ b/src/Ai/Base/Actions/TellLosAction.cpp @@ -4,11 +4,9 @@ */ #include "TellLosAction.h" -#include #include #include "ChatHelper.h" -#include "DBCStores.h" #include "Event.h" #include "ItemTemplate.h" #include "ObjectMgr.h" @@ -77,7 +75,7 @@ void TellLosAction::ListGameObjects(std::string const title, GuidVector gos) } } -bool TellAuraAction::Execute(Event event) +bool TellAuraAction::Execute(Event /*event*/) { botAI->TellMaster("--- Auras ---"); sLog->outMessage("playerbot", LOG_LEVEL_DEBUG, "--- Auras ---"); @@ -130,7 +128,7 @@ bool TellAuraAction::Execute(Event event) return true; } -bool TellEstimatedDpsAction::Execute(Event event) +bool TellEstimatedDpsAction::Execute(Event /*event*/) { float dps = AI_VALUE(float, "estimated group dps"); botAI->TellMaster("Estimated Group DPS: " + std::to_string(dps)); diff --git a/src/Ai/Base/Actions/TellMasterAction.cpp b/src/Ai/Base/Actions/TellMasterAction.cpp index 4b8d96d561..6156ce0819 100644 --- a/src/Ai/Base/Actions/TellMasterAction.cpp +++ b/src/Ai/Base/Actions/TellMasterAction.cpp @@ -8,13 +8,13 @@ #include "Event.h" #include "Playerbots.h" -bool TellMasterAction::Execute(Event event) +bool TellMasterAction::Execute(Event /*event*/) { botAI->TellMaster(text); return true; } -bool OutOfReactRangeAction::Execute(Event event) +bool OutOfReactRangeAction::Execute(Event /*event*/) { botAI->TellMaster("Wait for me!"); return true; diff --git a/src/Ai/Base/Actions/TellReputationAction.cpp b/src/Ai/Base/Actions/TellReputationAction.cpp index e08a38d840..0ccff606a2 100644 --- a/src/Ai/Base/Actions/TellReputationAction.cpp +++ b/src/Ai/Base/Actions/TellReputationAction.cpp @@ -6,10 +6,10 @@ #include "TellReputationAction.h" #include "Event.h" -#include "Playerbots.h" +#include "PlayerbotAI.h" #include "ReputationMgr.h" -bool TellReputationAction::Execute(Event event) +bool TellReputationAction::Execute(Event /*event*/) { Player* master = GetMaster(); if (!master) diff --git a/src/Ai/Base/Actions/TellTargetAction.cpp b/src/Ai/Base/Actions/TellTargetAction.cpp index 7ecd8efb5a..422d5e542a 100644 --- a/src/Ai/Base/Actions/TellTargetAction.cpp +++ b/src/Ai/Base/Actions/TellTargetAction.cpp @@ -6,10 +6,11 @@ #include "TellTargetAction.h" #include "Event.h" -#include "Playerbots.h" #include "ThreatMgr.h" +#include "AiObjectContext.h" +#include "PlayerbotAI.h" -bool TellTargetAction::Execute(Event event) +bool TellTargetAction::Execute(Event /*event*/) { Unit* target = context->GetValue("current target")->Get(); if (target) @@ -24,7 +25,7 @@ bool TellTargetAction::Execute(Event event) return true; } -bool TellAttackersAction::Execute(Event event) +bool TellAttackersAction::Execute(Event /*event*/) { botAI->TellMaster("--- Attackers ---"); diff --git a/src/Ai/Base/Actions/TradeAction.cpp b/src/Ai/Base/Actions/TradeAction.cpp index b6f6912be2..810710abbf 100644 --- a/src/Ai/Base/Actions/TradeAction.cpp +++ b/src/Ai/Base/Actions/TradeAction.cpp @@ -8,7 +8,7 @@ #include "Event.h" #include "ItemCountValue.h" #include "ItemVisitors.h" -#include "Playerbots.h" +#include "PlayerbotAI.h" bool TradeAction::Execute(Event event) { @@ -106,9 +106,7 @@ bool TradeAction::TradeItem(Item const* item, int8 slot) for (uint8 i = 0; i < TRADE_SLOT_TRADED_COUNT && tradeSlot == -1; i++) { if (pTrade->GetItem(TradeSlots(i)) == nullptr) - { tradeSlot = i; - } } } diff --git a/src/Ai/Base/Actions/TradeStatusAction.cpp b/src/Ai/Base/Actions/TradeStatusAction.cpp index 3057fff097..1c6ffc8eed 100644 --- a/src/Ai/Base/Actions/TradeStatusAction.cpp +++ b/src/Ai/Base/Actions/TradeStatusAction.cpp @@ -276,14 +276,11 @@ bool TradeStatusAction::CheckTrade() botAI->PlaySound(TEXT_EMOTE_NO); return false; } - success = true; } } else - { success = true; - } if (success) { @@ -340,9 +337,7 @@ int32 TradeStatusAction::CalculateCost(Player* player, bool sell) if (!craftData.IsEmpty()) { if (player == trader && !sell && craftData.IsRequired(proto->ItemId)) - { continue; - } if (player == bot && sell && craftData.itemId == proto->ItemId && craftData.IsFulfilled()) { @@ -352,13 +347,11 @@ int32 TradeStatusAction::CalculateCost(Player* player, bool sell) } if (sell) - { sum += item->GetCount() * proto->SellPrice * sRandomPlayerbotMgr.GetSellMultiplier(bot); - } + else - { sum += item->GetCount() * proto->BuyPrice * sRandomPlayerbotMgr.GetBuyMultiplier(bot); - } + } return sum; diff --git a/src/Ai/Base/Actions/TrainerAction.cpp b/src/Ai/Base/Actions/TrainerAction.cpp index e377fa3eb2..0f32560994 100644 --- a/src/Ai/Base/Actions/TrainerAction.cpp +++ b/src/Ai/Base/Actions/TrainerAction.cpp @@ -155,7 +155,7 @@ void TrainerAction::TellFooter(uint32 totalCost) } } -bool MaintenanceAction::Execute(Event event) +bool MaintenanceAction::Execute(Event /*event*/) { if (!sPlayerbotAIConfig.maintenanceCommand) { @@ -255,7 +255,7 @@ bool MaintenanceAction::Execute(Event event) return true; } -bool RemoveGlyphAction::Execute(Event event) +bool RemoveGlyphAction::Execute(Event /*event*/) { for (uint32 slotIndex = 0; slotIndex < MAX_GLYPH_SLOT_INDEX; ++slotIndex) { @@ -265,7 +265,7 @@ bool RemoveGlyphAction::Execute(Event event) return true; } -bool AutoGearAction::Execute(Event event) +bool AutoGearAction::Execute(Event /*event*/) { if (!sPlayerbotAIConfig.autoGearCommand) { diff --git a/src/Ai/Base/Actions/TravelAction.cpp b/src/Ai/Base/Actions/TravelAction.cpp index f0afef01bf..3516426ce8 100644 --- a/src/Ai/Base/Actions/TravelAction.cpp +++ b/src/Ai/Base/Actions/TravelAction.cpp @@ -10,7 +10,7 @@ #include "GridNotifiersImpl.h" #include "Playerbots.h" -bool TravelAction::Execute(Event event) +bool TravelAction::Execute(Event /*event*/) { TravelTarget* target = AI_VALUE(TravelTarget*, "travel target"); @@ -60,12 +60,14 @@ bool TravelAction::isUseful() (!AI_VALUE(GuidPosition, "rpg target") || !AI_VALUE(ObjectGuid, "pull target")); } -bool MoveToDarkPortalAction::Execute(Event event) +bool MoveToDarkPortalAction::Execute(Event /*event*/) { if (bot->GetGroup()) + { if (bot->GetGroup()->GetLeaderGUID() != bot->GetGUID() && !GET_PLAYERBOT_AI(GET_PLAYERBOT_AI(bot)->GetGroupLeader())) return false; + } if (bot->GetLevel() > 57) { @@ -111,7 +113,7 @@ bool MoveToDarkPortalAction::Execute(Event event) bool MoveToDarkPortalAction::isUseful() { return bot->GetLevel() > 54; } -bool DarkPortalAzerothAction::Execute(Event event) +bool DarkPortalAzerothAction::Execute(Event /*event*/) { if (bot->GetLevel() > 57) { @@ -126,7 +128,7 @@ bool DarkPortalAzerothAction::Execute(Event event) bool DarkPortalAzerothAction::isUseful() { return bot->GetLevel() > 57; } -bool MoveFromDarkPortalAction::Execute(Event event) +bool MoveFromDarkPortalAction::Execute(Event /*event*/) { RESET_AI_VALUE(GuidPosition, "rpg target"); diff --git a/src/Ai/Base/Actions/UnlockItemAction.cpp b/src/Ai/Base/Actions/UnlockItemAction.cpp index 367a4fcbfb..7a4b3bd177 100644 --- a/src/Ai/Base/Actions/UnlockItemAction.cpp +++ b/src/Ai/Base/Actions/UnlockItemAction.cpp @@ -1,14 +1,13 @@ #include "UnlockItemAction.h" #include "PlayerbotAI.h" #include "ItemTemplate.h" -#include "WorldPacket.h" #include "Player.h" #include "ObjectMgr.h" #include "SpellInfo.h" -#define PICK_LOCK_SPELL_ID 1804 +inline constexpr uint32_t PICK_LOCK_SPELL_ID = 1804; -bool UnlockItemAction::Execute(Event event) +bool UnlockItemAction::Execute(Event /*event*/) { bool foundLockedItem = false; @@ -32,7 +31,5 @@ void UnlockItemAction::UnlockItem(Item* item) botAI->TellMaster(out.str()); } else - { botAI->TellError("Failed to cast Pick Lock."); - } } diff --git a/src/Ai/Base/Actions/UnlockTradedItemAction.cpp b/src/Ai/Base/Actions/UnlockTradedItemAction.cpp index 047cd2e201..90cdbfb02b 100644 --- a/src/Ai/Base/Actions/UnlockTradedItemAction.cpp +++ b/src/Ai/Base/Actions/UnlockTradedItemAction.cpp @@ -1,11 +1,11 @@ #include "UnlockTradedItemAction.h" -#include "Playerbots.h" +#include "PlayerbotAI.h" #include "TradeData.h" #include "SpellInfo.h" -#define PICK_LOCK_SPELL_ID 1804 +inline constexpr uint32_t PICK_LOCK_SPELL_ID = 1804; -bool UnlockTradedItemAction::Execute(Event event) +bool UnlockTradedItemAction::Execute(Event /*event*/) { Player* trader = bot->GetTrader(); if (!trader) diff --git a/src/Ai/Base/Actions/UseItemAction.cpp b/src/Ai/Base/Actions/UseItemAction.cpp index 77c865dba8..3483986f93 100644 --- a/src/Ai/Base/Actions/UseItemAction.cpp +++ b/src/Ai/Base/Actions/UseItemAction.cpp @@ -416,7 +416,7 @@ bool UseHearthStone::Execute(Event event) bool UseHearthStone::isUseful() { return !bot->InBattleground(); } -bool UseRandomRecipe::Execute(Event event) +bool UseRandomRecipe::Execute(Event /*event*/) { std::vector recipes = AI_VALUE2(std::vector, "inventory items", "recipe"); @@ -445,7 +445,7 @@ bool UseRandomRecipe::isUseful() bool UseRandomRecipe::isPossible() { return AI_VALUE2(uint32, "item count", "recipe") > 0; } -bool UseRandomQuestItem::Execute(Event event) +bool UseRandomQuestItem::Execute(Event /*event*/) { Unit* unitTarget = nullptr; ObjectGuid goTarget; diff --git a/src/Ai/Base/Actions/UseMeetingStoneAction.cpp b/src/Ai/Base/Actions/UseMeetingStoneAction.cpp index b334429189..0862be68f3 100644 --- a/src/Ai/Base/Actions/UseMeetingStoneAction.cpp +++ b/src/Ai/Base/Actions/UseMeetingStoneAction.cpp @@ -55,16 +55,14 @@ bool UseMeetingStoneAction::Execute(Event event) return Teleport(master, bot, false); } -bool SummonAction::Execute(Event event) +bool SummonAction::Execute(Event /*event*/) { Player* master = GetMaster(); if (!master) return false; if (Pet* pet = bot->GetPet()) - { botAI->PetFollow(); - } if (master->GetSession()->GetSecurity() >= SEC_PLAYER) { diff --git a/src/Ai/Base/Actions/VehicleActions.cpp b/src/Ai/Base/Actions/VehicleActions.cpp index ac925fbcb1..71963690da 100644 --- a/src/Ai/Base/Actions/VehicleActions.cpp +++ b/src/Ai/Base/Actions/VehicleActions.cpp @@ -95,7 +95,7 @@ bool EnterVehicleAction::EnterVehicle(Unit* vehicleBase, bool moveIfFar) return true; } -bool LeaveVehicleAction::Execute(Event event) +bool LeaveVehicleAction::Execute(Event /*event*/) { Vehicle* myVehicle = bot->GetVehicle(); if (!myVehicle) diff --git a/src/Ai/Base/Strategy/ChatCommandHandlerStrategy.cpp b/src/Ai/Base/Strategy/ChatCommandHandlerStrategy.cpp index 04f7979012..f67ae49ca7 100644 --- a/src/Ai/Base/Strategy/ChatCommandHandlerStrategy.cpp +++ b/src/Ai/Base/Strategy/ChatCommandHandlerStrategy.cpp @@ -5,8 +5,6 @@ #include "ChatCommandHandlerStrategy.h" -#include "Playerbots.h" - class ChatCommandActionNodeFactoryInternal : public NamedObjectFactory { public: diff --git a/src/Ai/Base/Strategy/CombatStrategy.cpp b/src/Ai/Base/Strategy/CombatStrategy.cpp index 75700e9a6d..62972d4b1d 100644 --- a/src/Ai/Base/Strategy/CombatStrategy.cpp +++ b/src/Ai/Base/Strategy/CombatStrategy.cpp @@ -5,7 +5,6 @@ #include "CombatStrategy.h" -#include "Playerbots.h" #include "Strategy.h" void CombatStrategy::InitTriggers(std::vector& triggers) diff --git a/src/Ai/Base/Strategy/DuelStrategy.cpp b/src/Ai/Base/Strategy/DuelStrategy.cpp index 4c08397e6b..c7f7e9d235 100644 --- a/src/Ai/Base/Strategy/DuelStrategy.cpp +++ b/src/Ai/Base/Strategy/DuelStrategy.cpp @@ -5,8 +5,6 @@ #include "DuelStrategy.h" -#include "Playerbots.h" - void DuelStrategy::InitTriggers(std::vector& triggers) { PassTroughStrategy::InitTriggers(triggers); diff --git a/src/Ai/Base/Strategy/FollowMasterStrategy.cpp b/src/Ai/Base/Strategy/FollowMasterStrategy.cpp index 0308b113f9..6701fdcac2 100644 --- a/src/Ai/Base/Strategy/FollowMasterStrategy.cpp +++ b/src/Ai/Base/Strategy/FollowMasterStrategy.cpp @@ -5,8 +5,6 @@ #include "FollowMasterStrategy.h" -#include "Playerbots.h" - std::vector FollowMasterStrategy::getDefaultActions() { return { diff --git a/src/Ai/Base/Strategy/GuardStrategy.cpp b/src/Ai/Base/Strategy/GuardStrategy.cpp index 9a396ac57f..96017365f8 100644 --- a/src/Ai/Base/Strategy/GuardStrategy.cpp +++ b/src/Ai/Base/Strategy/GuardStrategy.cpp @@ -5,8 +5,6 @@ #include "GuardStrategy.h" -#include "Playerbots.h" - std::vector GuardStrategy::getDefaultActions() { return { diff --git a/src/Ai/Base/Strategy/NonCombatStrategy.cpp b/src/Ai/Base/Strategy/NonCombatStrategy.cpp index 6b8f76ac95..136dcc53e7 100644 --- a/src/Ai/Base/Strategy/NonCombatStrategy.cpp +++ b/src/Ai/Base/Strategy/NonCombatStrategy.cpp @@ -5,8 +5,6 @@ #include "NonCombatStrategy.h" -#include "Playerbots.h" - void NonCombatStrategy::InitTriggers(std::vector& triggers) { triggers.push_back(new TriggerNode("random", { NextAction("clean quest log", 1.0f) })); diff --git a/src/Ai/Base/Strategy/RTSCStrategy.cpp b/src/Ai/Base/Strategy/RTSCStrategy.cpp index 2c64092e3c..525338c156 100644 --- a/src/Ai/Base/Strategy/RTSCStrategy.cpp +++ b/src/Ai/Base/Strategy/RTSCStrategy.cpp @@ -5,8 +5,6 @@ #include "RTSCStrategy.h" -#include "Playerbots.h" - RTSCStrategy::RTSCStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} void RTSCStrategy::InitTriggers(std::vector& triggers) {} diff --git a/src/Ai/Base/Strategy/RacialsStrategy.cpp b/src/Ai/Base/Strategy/RacialsStrategy.cpp index f4f270b1b0..dc6d5bc482 100644 --- a/src/Ai/Base/Strategy/RacialsStrategy.cpp +++ b/src/Ai/Base/Strategy/RacialsStrategy.cpp @@ -5,8 +5,6 @@ #include "RacialsStrategy.h" -#include "Playerbots.h" - class RacialsStrategyActionNodeFactory : public NamedObjectFactory { public: diff --git a/src/Ai/Base/Strategy/UsePotionsStrategy.cpp b/src/Ai/Base/Strategy/UsePotionsStrategy.cpp index b0e0049269..55827fbd20 100644 --- a/src/Ai/Base/Strategy/UsePotionsStrategy.cpp +++ b/src/Ai/Base/Strategy/UsePotionsStrategy.cpp @@ -5,8 +5,6 @@ #include "UsePotionsStrategy.h" -#include "Playerbots.h" - class UsePotionsStrategyActionNodeFactory : public NamedObjectFactory { public: diff --git a/src/Ai/Base/Trigger/GenericTriggers.cpp b/src/Ai/Base/Trigger/GenericTriggers.cpp index a933bc232d..209766be73 100644 --- a/src/Ai/Base/Trigger/GenericTriggers.cpp +++ b/src/Ai/Base/Trigger/GenericTriggers.cpp @@ -7,9 +7,7 @@ #include -#include "BattlegroundWS.h" #include "CreatureAI.h" -#include "GameTime.h" #include "ItemVisitors.h" #include "LastSpellCastValue.h" #include "ObjectGuid.h" diff --git a/src/Ai/Base/Trigger/RangeTriggers.cpp b/src/Ai/Base/Trigger/RangeTriggers.cpp index 2acc2aec85..01a4d0f617 100644 --- a/src/Ai/Base/Trigger/RangeTriggers.cpp +++ b/src/Ai/Base/Trigger/RangeTriggers.cpp @@ -201,7 +201,6 @@ bool PartyMemberToHealOutOfSpellRangeTrigger::IsActive() if (!target) return false; - float combatReach = bot->GetCombatReach() + target->GetCombatReach(); return target && (ServerFacade::instance().GetDistance2d(bot, target) > (distance + sPlayerbotAIConfig.contactDistance) || !bot->IsWithinLOSInMap(target)); } diff --git a/src/Ai/Base/Value/Arrow.cpp b/src/Ai/Base/Value/Arrow.cpp index 5fd710220c..114b2c058d 100644 --- a/src/Ai/Base/Value/Arrow.cpp +++ b/src/Ai/Base/Value/Arrow.cpp @@ -6,7 +6,8 @@ #include "Arrow.h" #include "Map.h" -#include "Playerbots.h" +#include "PlayerbotAI.h" +#include "Group.h" WorldLocation ArrowFormation::GetLocationInternal() { diff --git a/src/Ai/Base/Value/CcTargetValue.cpp b/src/Ai/Base/Value/CcTargetValue.cpp index 1c305aec3c..0559656f7a 100644 --- a/src/Ai/Base/Value/CcTargetValue.cpp +++ b/src/Ai/Base/Value/CcTargetValue.cpp @@ -6,7 +6,9 @@ #include "CcTargetValue.h" #include "Action.h" -#include "Playerbots.h" +#include "AiObjectContext.h" +#include "Group.h" +#include "PlayerbotAI.h" #include "ServerFacade.h" class FindTargetForCcStrategy : public FindTargetStrategy diff --git a/src/Ai/Base/Value/CurrentCcTargetValue.cpp b/src/Ai/Base/Value/CurrentCcTargetValue.cpp index 27b8100264..97f35c1b55 100644 --- a/src/Ai/Base/Value/CurrentCcTargetValue.cpp +++ b/src/Ai/Base/Value/CurrentCcTargetValue.cpp @@ -4,8 +4,7 @@ */ #include "CurrentCcTargetValue.h" - -#include "Playerbots.h" +#include "PlayerbotAI.h" class FindCurrentCcTargetStrategy : public FindTargetStrategy { diff --git a/src/Ai/Base/Value/DpsTargetValue.cpp b/src/Ai/Base/Value/DpsTargetValue.cpp index 1a813c9940..28f308471f 100644 --- a/src/Ai/Base/Value/DpsTargetValue.cpp +++ b/src/Ai/Base/Value/DpsTargetValue.cpp @@ -29,7 +29,6 @@ class FindMaxThreatGapTargetStrategy : public FindTargetStrategy foundHighPriority = true; return; } - Unit* victim = attacker->GetVictim(); if (!result || CalcThreatGap(attacker, threatMgr) > CalcThreatGap(result, &result->GetThreatMgr())) result = attacker; } @@ -144,7 +143,7 @@ class GeneralFindTargetSmartStrategy : public FindTargetStrategy { } - void CheckAttacker(Unit* attacker, ThreatMgr* threatMgr) override + void CheckAttacker(Unit* attacker, ThreatMgr*) override { if (Group* group = botAI->GetBot()->GetGroup()) { @@ -195,7 +194,6 @@ class GeneralFindTargetSmartStrategy : public FindTargetStrategy } int32_t GetIntervalLevel(Unit* unit) { - float time = unit->GetHealth() / dps_; float dis = unit->GetDistance(botAI->GetBot()); float attackRange = botAI->IsRanged(botAI->GetBot()) ? sPlayerbotAIConfig.spellDistance : sPlayerbotAIConfig.meleeDistance; @@ -218,7 +216,7 @@ class ComboFindTargetSmartStrategy : public FindTargetStrategy { } - void CheckAttacker(Unit* attacker, ThreatMgr* threatMgr) override + void CheckAttacker(Unit* attacker, ThreatMgr*) override { if (Group* group = botAI->GetBot()->GetGroup()) { @@ -276,7 +274,6 @@ class ComboFindTargetSmartStrategy : public FindTargetStrategy } int32_t GetIntervalLevel(Unit* unit) { - float time = unit->GetHealth() / dps_; float dis = unit->GetDistance(botAI->GetBot()); float attackRange = botAI->IsRanged(botAI->GetBot()) ? sPlayerbotAIConfig.spellDistance : sPlayerbotAIConfig.meleeDistance; @@ -322,7 +319,7 @@ class FindMaxHpTargetStrategy : public FindTargetStrategy public: FindMaxHpTargetStrategy(PlayerbotAI* botAI) : FindTargetStrategy(botAI), maxHealth(0) {} - void CheckAttacker(Unit* attacker, ThreatMgr* threatMgr) override + void CheckAttacker(Unit* attacker, ThreatMgr*) override { if (Group* group = botAI->GetBot()->GetGroup()) { diff --git a/src/Ai/Base/Value/ItemCountValue.cpp b/src/Ai/Base/Value/ItemCountValue.cpp index 2c719d8f4d..9ea7da2e06 100644 --- a/src/Ai/Base/Value/ItemCountValue.cpp +++ b/src/Ai/Base/Value/ItemCountValue.cpp @@ -5,7 +5,7 @@ #include "ItemCountValue.h" -#include "Playerbots.h" +#include "PlayerbotAI.h" std::vector InventoryItemValueBase::Find(std::string const qualifier) { @@ -25,9 +25,7 @@ uint32 ItemCountValue::Calculate() uint32 count = 0; std::vector items = Find(qualifier); for (Item* item : items) - { count += item->GetCount(); - } return count; } diff --git a/src/Ai/Base/Value/LastMovementValue.cpp b/src/Ai/Base/Value/LastMovementValue.cpp index ba54020e39..22c35450d5 100644 --- a/src/Ai/Base/Value/LastMovementValue.cpp +++ b/src/Ai/Base/Value/LastMovementValue.cpp @@ -5,7 +5,6 @@ #include "LastMovementValue.h" -#include "Playerbots.h" #include "Timer.h" LastMovement::LastMovement() { clear(); } diff --git a/src/Ai/Base/Value/LeastHpTargetValue.cpp b/src/Ai/Base/Value/LeastHpTargetValue.cpp index 8b9f1a55fc..423db1de9c 100644 --- a/src/Ai/Base/Value/LeastHpTargetValue.cpp +++ b/src/Ai/Base/Value/LeastHpTargetValue.cpp @@ -6,7 +6,7 @@ #include "LeastHpTargetValue.h" #include "AttackersValue.h" -#include "Playerbots.h" +#include "PlayerbotAI.h" class FindLeastHpTargetStrategy : public FindNonCcTargetStrategy { diff --git a/src/Ai/Base/Value/LogLevelValue.h b/src/Ai/Base/Value/LogLevelValue.h index 18fdbddcee..f8515b13b2 100644 --- a/src/Ai/Base/Value/LogLevelValue.h +++ b/src/Ai/Base/Value/LogLevelValue.h @@ -6,6 +6,7 @@ #ifndef _PLAYERBOT_LOGLEVELVALUE_H #define _PLAYERBOT_LOGLEVELVALUE_H +#include "LogCommon.h" #include "Value.h" class PlayerbotAI; diff --git a/src/Ai/Base/Value/NearestCorpsesValue.cpp b/src/Ai/Base/Value/NearestCorpsesValue.cpp index fec1aed21f..6091c32419 100644 --- a/src/Ai/Base/Value/NearestCorpsesValue.cpp +++ b/src/Ai/Base/Value/NearestCorpsesValue.cpp @@ -8,7 +8,6 @@ #include "CellImpl.h" #include "GridNotifiers.h" #include "GridNotifiersImpl.h" -#include "Playerbots.h" class AnyDeadUnitInObjectRangeCheck { diff --git a/src/Ai/Base/Value/PartyMemberToDispel.cpp b/src/Ai/Base/Value/PartyMemberToDispel.cpp index 91b1955b27..d3bf4e9ab9 100644 --- a/src/Ai/Base/Value/PartyMemberToDispel.cpp +++ b/src/Ai/Base/Value/PartyMemberToDispel.cpp @@ -4,8 +4,7 @@ */ #include "PartyMemberToDispel.h" - -#include "Playerbots.h" +#include "PlayerbotAI.h" class PartyMemberToDispelPredicate : public FindPlayerPredicate, public PlayerbotAIAware { diff --git a/src/Ai/Base/Value/PartyMemberValue.cpp b/src/Ai/Base/Value/PartyMemberValue.cpp index 7477429094..53a93b2b43 100644 --- a/src/Ai/Base/Value/PartyMemberValue.cpp +++ b/src/Ai/Base/Value/PartyMemberValue.cpp @@ -6,8 +6,11 @@ #include "PartyMemberValue.h" #include "Corpse.h" -#include "Playerbots.h" +#include "Group.h" +#include "PlayerbotAI.h" #include "ServerFacade.h" +#include "Pet.h" +#include "Spell.h" Unit* PartyMemberValue::FindPartyMember(std::vector* party, FindPlayerPredicate& predicate) { diff --git a/src/Ai/Base/Value/PartyMemberWithoutItemValue.h b/src/Ai/Base/Value/PartyMemberWithoutItemValue.h index 2918ea4c57..379835543e 100644 --- a/src/Ai/Base/Value/PartyMemberWithoutItemValue.h +++ b/src/Ai/Base/Value/PartyMemberWithoutItemValue.h @@ -8,7 +8,6 @@ #include "NamedObjectContext.h" #include "PartyMemberValue.h" -#include "PlayerbotAIConfig.h" class PlayerbotAI; class Unit; @@ -16,8 +15,7 @@ class Unit; class PartyMemberWithoutItemValue : public PartyMemberValue, public Qualified { public: - PartyMemberWithoutItemValue(PlayerbotAI* botAI, std::string const name = "party member without item", - float range = sPlayerbotAIConfig.farDistance) + PartyMemberWithoutItemValue(PlayerbotAI* botAI, std::string const name = "party member without item") : PartyMemberValue(botAI, name) { } diff --git a/src/Ai/Base/Value/PossibleRpgTargetsValue.cpp b/src/Ai/Base/Value/PossibleRpgTargetsValue.cpp index f2b6aef105..fbcb8fb2d4 100644 --- a/src/Ai/Base/Value/PossibleRpgTargetsValue.cpp +++ b/src/Ai/Base/Value/PossibleRpgTargetsValue.cpp @@ -5,14 +5,15 @@ #include "PossibleRpgTargetsValue.h" -#include "CellImpl.h" -#include "GridNotifiers.h" -#include "GridNotifiersImpl.h" +#include "AiObjectContext.h" #include "ObjectGuid.h" -#include "Playerbots.h" #include "ServerFacade.h" #include "SharedDefines.h" #include "NearestGameObjects.h" +#include "GridNotifiers.h" +#include "GridNotifiersImpl.h" +#include "CellImpl.h" +#include "TravelMgr.h" std::vector PossibleRpgTargetsValue::allowedNpcFlags; @@ -73,9 +74,15 @@ bool PossibleRpgTargetsValue::AcceptUnit(Unit* unit) } TravelTarget* travelTarget = context->GetValue("travel target")->Get(); - if (travelTarget && travelTarget->getDestination() && - travelTarget->getDestination()->getEntry() == unit->GetEntry()) + + if ( + travelTarget != nullptr + && travelTarget->getDestination() + && (uint32_t)travelTarget->getDestination()->getEntry() == unit->GetEntry() + ) + { return true; + } if (urand(1, 100) < 25 && unit->IsFriendlyTo(bot)) return true; diff --git a/src/Ai/Base/Value/SnareTargetValue.cpp b/src/Ai/Base/Value/SnareTargetValue.cpp index 7965198aa4..f2440fcec9 100644 --- a/src/Ai/Base/Value/SnareTargetValue.cpp +++ b/src/Ai/Base/Value/SnareTargetValue.cpp @@ -5,7 +5,8 @@ #include "SnareTargetValue.h" -#include "Playerbots.h" +#include "AiObjectContext.h" +#include "PlayerbotAI.h" #include "ServerFacade.h" Unit* SnareTargetValue::Calculate() diff --git a/src/Ai/Base/Value/SpellIdValue.cpp b/src/Ai/Base/Value/SpellIdValue.cpp index 682c0b3ffd..244634bcdc 100644 --- a/src/Ai/Base/Value/SpellIdValue.cpp +++ b/src/Ai/Base/Value/SpellIdValue.cpp @@ -8,7 +8,6 @@ #include "ChatHelper.h" #include "Playerbots.h" #include "Vehicle.h" -#include "World.h" SpellIdValue::SpellIdValue(PlayerbotAI* botAI) : CalculatedValue(botAI, "spell id", 20 * 1000) {} diff --git a/src/Ai/Base/Value/StatsValues.cpp b/src/Ai/Base/Value/StatsValues.cpp index 615f39f019..5205ec05b7 100644 --- a/src/Ai/Base/Value/StatsValues.cpp +++ b/src/Ai/Base/Value/StatsValues.cpp @@ -4,8 +4,12 @@ */ #include "StatsValues.h" -#include "Playerbots.h" +#include "AiObjectContext.h" +#include "Group.h" +#include "Pet.h" +#include "PlayerbotAIConfig.h" #include "ServerFacade.h" +#include "Player.h" Unit* HealthValue::GetTarget() { @@ -184,7 +188,7 @@ bool IsInCombatValue::Calculate() if (member->IsInCombat() && ServerFacade::instance().IsDistanceLessOrEqualThan(ServerFacade::instance().GetDistance2d(member, bot), - sPlayerbotAIConfig.reactDistance)) + PlayerbotAIConfig::instance().reactDistance)) return true; } } diff --git a/src/Ai/Base/Value/TankTargetValue.cpp b/src/Ai/Base/Value/TankTargetValue.cpp index ef2d1e959c..90c759a7da 100644 --- a/src/Ai/Base/Value/TankTargetValue.cpp +++ b/src/Ai/Base/Value/TankTargetValue.cpp @@ -5,9 +5,10 @@ #include "TankTargetValue.h" +#include "AiObjectContext.h" #include "AttackersValue.h" -#include "PlayerbotAIConfig.h" -#include "Playerbots.h" +#include "Group.h" +#include "PlayerbotAI.h" class FindTargetForTankStrategy : public FindNonCcTargetStrategy { diff --git a/src/Ai/Class/Dk/Strategy/GenericDKNonCombatStrategy.cpp b/src/Ai/Class/Dk/Strategy/GenericDKNonCombatStrategy.cpp index edead06755..d358d43705 100644 --- a/src/Ai/Class/Dk/Strategy/GenericDKNonCombatStrategy.cpp +++ b/src/Ai/Class/Dk/Strategy/GenericDKNonCombatStrategy.cpp @@ -5,8 +5,6 @@ #include "GenericDKNonCombatStrategy.h" -#include "Playerbots.h" - class GenericDKNonCombatStrategyActionNodeFactory : public NamedObjectFactory { public: diff --git a/src/Ai/Class/Druid/Action/DruidActions.cpp b/src/Ai/Class/Druid/Action/DruidActions.cpp index 33b7826bf8..d6a950b175 100644 --- a/src/Ai/Class/Druid/Action/DruidActions.cpp +++ b/src/Ai/Class/Druid/Action/DruidActions.cpp @@ -28,11 +28,11 @@ Value* CastEntanglingRootsCcAction::GetTargetValue() return context->GetValue("cc target", "entangling roots"); } -bool CastEntanglingRootsCcAction::Execute(Event event) { return botAI->CastSpell("entangling roots", GetTarget()); } +bool CastEntanglingRootsCcAction::Execute(Event /*event*/) { return botAI->CastSpell("entangling roots", GetTarget()); } Value* CastHibernateCcAction::GetTargetValue() { return context->GetValue("cc target", "hibernate"); } -bool CastHibernateCcAction::Execute(Event event) { return botAI->CastSpell("hibernate", GetTarget()); } +bool CastHibernateCcAction::Execute(Event /*event*/) { return botAI->CastSpell("hibernate", GetTarget()); } bool CastStarfallAction::isUseful() { if (!CastSpellAction::isUseful()) diff --git a/src/Ai/Class/Druid/Action/DruidShapeshiftActions.cpp b/src/Ai/Class/Druid/Action/DruidShapeshiftActions.cpp index 42e639f93b..a87f22a3b5 100644 --- a/src/Ai/Class/Druid/Action/DruidShapeshiftActions.cpp +++ b/src/Ai/Class/Druid/Action/DruidShapeshiftActions.cpp @@ -31,7 +31,7 @@ bool CastTravelFormAction::isUseful() !botAI->HasAura("dash", bot); } -bool CastCasterFormAction::Execute(Event event) +bool CastCasterFormAction::Execute(Event /*event*/) { botAI->RemoveShapeshift(); return true; @@ -44,7 +44,7 @@ bool CastCasterFormAction::isUseful() AI_VALUE2(uint8, "mana", "self target") > sPlayerbotAIConfig.mediumHealth; } -bool CastCancelTreeFormAction::Execute(Event event) +bool CastCancelTreeFormAction::Execute(Event /*event*/) { botAI->RemoveAura("tree of life"); return true; diff --git a/src/Ai/Class/Hunter/Action/HunterActions.cpp b/src/Ai/Class/Hunter/Action/HunterActions.cpp index a1588f8537..18e5d59057 100644 --- a/src/Ai/Class/Hunter/Action/HunterActions.cpp +++ b/src/Ai/Class/Hunter/Action/HunterActions.cpp @@ -59,7 +59,7 @@ bool CastImmolationTrapAction::isUseful() Value* CastFreezingTrap::GetTargetValue() { return context->GetValue("cc target", "freezing trap"); } -bool FeedPetAction::Execute(Event event) +bool FeedPetAction::Execute(Event /*event*/) { if (Pet* pet = bot->GetPet()) if (pet->getPetType() == HUNTER_PET && pet->GetHappinessState() != HAPPY) @@ -99,7 +99,7 @@ bool CastDisengageAction::isUseful() Value* CastScareBeastCcAction::GetTargetValue() { return context->GetValue("cc target", "scare beast"); } -bool CastScareBeastCcAction::Execute(Event event) { return botAI->CastSpell("scare beast", GetTarget()); } +bool CastScareBeastCcAction::Execute(Event /*event*/) { return botAI->CastSpell("scare beast", GetTarget()); } bool CastWingClipAction::isUseful() { return CastSpellAction::isUseful() && !botAI->HasAura(spell, GetTarget()); } diff --git a/src/Ai/Class/Hunter/Strategy/GenericHunterStrategy.cpp b/src/Ai/Class/Hunter/Strategy/GenericHunterStrategy.cpp index 04afc6f766..16a22eaeb9 100644 --- a/src/Ai/Class/Hunter/Strategy/GenericHunterStrategy.cpp +++ b/src/Ai/Class/Hunter/Strategy/GenericHunterStrategy.cpp @@ -5,9 +5,6 @@ #include "GenericHunterStrategy.h" -#include "Playerbots.h" -#include "Strategy.h" - class GenericHunterStrategyActionNodeFactory : public NamedObjectFactory { public: diff --git a/src/Ai/Class/Paladin/Action/PaladinActions.cpp b/src/Ai/Class/Paladin/Action/PaladinActions.cpp index 190ecb264a..726f83546f 100644 --- a/src/Ai/Class/Paladin/Action/PaladinActions.cpp +++ b/src/Ai/Class/Paladin/Action/PaladinActions.cpp @@ -8,13 +8,10 @@ #include "AiFactory.h" #include "Event.h" #include "PlayerbotAI.h" -#include "PlayerbotAIConfig.h" -#include "PlayerbotFactory.h" #include "Playerbots.h" #include "SharedDefines.h" #include "../../../../../src/server/scripts/Spells/spell_generic.cpp" #include "GenericBuffUtils.h" -#include "Config.h" #include "Group.h" #include "ObjectAccessor.h" @@ -167,7 +164,7 @@ Value* CastBlessingOnPartyAction::GetTargetValue() return context->GetValue("party member without aura", MakeAuraQualifierForBuff(spell)); } -bool CastBlessingOfMightAction::Execute(Event event) +bool CastBlessingOfMightAction::Execute(Event /*event*/) { Unit* target = GetTarget(); if (!target) @@ -188,7 +185,7 @@ Value* CastBlessingOfMightOnPartyAction::GetTargetValue() ); } -bool CastBlessingOfMightOnPartyAction::Execute(Event event) +bool CastBlessingOfMightOnPartyAction::Execute(Event /*event*/) { Unit* target = GetTarget(); if (!target) @@ -201,7 +198,7 @@ bool CastBlessingOfMightOnPartyAction::Execute(Event event) return botAI->CastSpell(castName, target); } -bool CastBlessingOfWisdomAction::Execute(Event event) +bool CastBlessingOfWisdomAction::Execute(Event /*event*/) { Unit* target = GetTarget(); if (!target) @@ -222,7 +219,7 @@ Value* CastBlessingOfWisdomOnPartyAction::GetTargetValue() ); } -bool CastBlessingOfWisdomOnPartyAction::Execute(Event event) +bool CastBlessingOfWisdomOnPartyAction::Execute(Event /*event*/) { Unit* target = GetTarget(); if (!target) @@ -258,7 +255,7 @@ Value* CastBlessingOfSanctuaryOnPartyAction::GetTargetValue() ); } -bool CastBlessingOfSanctuaryOnPartyAction::Execute(Event event) +bool CastBlessingOfSanctuaryOnPartyAction::Execute(Event /*event*/) { if (!bot->HasSpell(SPELL_BLESSING_OF_SANCTUARY)) return false; @@ -382,7 +379,7 @@ Value* CastBlessingOfKingsOnPartyAction::GetTargetValue() ); } -bool CastBlessingOfKingsOnPartyAction::Execute(Event event) +bool CastBlessingOfKingsOnPartyAction::Execute(Event /*event*/) { Unit* target = GetTarget(); if (!target) @@ -493,7 +490,7 @@ bool CastDivineSacrificeAction::isUseful() !botAI->HasAura("divine guardian", GetTarget(), false, false, -1, true); } -bool CastCancelDivineSacrificeAction::Execute(Event event) +bool CastCancelDivineSacrificeAction::Execute(Event /*event*/) { botAI->RemoveAura("divine sacrifice"); return true; diff --git a/src/Ai/Class/Paladin/Strategy/GenericPaladinStrategy.cpp b/src/Ai/Class/Paladin/Strategy/GenericPaladinStrategy.cpp index 62d7dfb10b..49143f9aef 100644 --- a/src/Ai/Class/Paladin/Strategy/GenericPaladinStrategy.cpp +++ b/src/Ai/Class/Paladin/Strategy/GenericPaladinStrategy.cpp @@ -6,7 +6,6 @@ #include "GenericPaladinStrategy.h" #include "GenericPaladinStrategyActionNodeFactory.h" -#include "Playerbots.h" GenericPaladinStrategy::GenericPaladinStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) { diff --git a/src/Ai/Class/Priest/Action/PriestActions.cpp b/src/Ai/Class/Priest/Action/PriestActions.cpp index bdc33ac340..1bfd7d2a8d 100644 --- a/src/Ai/Class/Priest/Action/PriestActions.cpp +++ b/src/Ai/Class/Priest/Action/PriestActions.cpp @@ -8,7 +8,7 @@ #include "Event.h" #include "Playerbots.h" -bool CastRemoveShadowformAction::Execute(Event event) +bool CastRemoveShadowformAction::Execute(Event /*event*/) { botAI->RemoveAura("shadowform"); return true; diff --git a/src/Ai/Class/Rogue/Action/RogueActions.cpp b/src/Ai/Class/Rogue/Action/RogueActions.cpp index fb4975ab33..b554a82537 100644 --- a/src/Ai/Class/Rogue/Action/RogueActions.cpp +++ b/src/Ai/Class/Rogue/Action/RogueActions.cpp @@ -25,7 +25,7 @@ bool CastStealthAction::isPossible() return !botAI->HasAura(23333, bot) && !botAI->HasAura(23335, bot) && !botAI->HasAura(34976, bot); } -bool UnstealthAction::Execute(Event event) +bool UnstealthAction::Execute(Event /*event*/) { botAI->RemoveAura("stealth"); // botAI->ChangeStrategy("+dps,-stealthed", BOT_STATE_COMBAT); @@ -33,7 +33,7 @@ bool UnstealthAction::Execute(Event event) return true; } -bool CheckStealthAction::Execute(Event event) +bool CheckStealthAction::Execute(Event /*event*/) { if (botAI->HasAura("stealth", bot)) { @@ -69,7 +69,7 @@ bool CastTricksOfTheTradeOnMainTankAction::isUseful() return CastSpellAction::isUseful() && AI_VALUE2(float, "distance", GetTargetName()) < 20.0f; } -bool UseDeadlyPoisonAction::Execute(Event event) +bool UseDeadlyPoisonAction::Execute(Event /*event*/) { std::vector poison_suffixs = {" IX", " VIII", " VII", " VI", " V", " IV", " III", " II", ""}; std::vector items; @@ -109,7 +109,7 @@ bool UseDeadlyPoisonAction::isPossible() return !items.empty(); } -bool UseInstantPoisonAction::Execute(Event event) +bool UseInstantPoisonAction::Execute(Event /*event*/) { std::vector poison_suffixs = {" IX", " VIII", " VII", " VI", " V", " IV", " III", " II", ""}; std::vector items; @@ -148,7 +148,7 @@ bool UseInstantPoisonAction::isPossible() return !items.empty(); } -bool UseInstantPoisonOffHandAction::Execute(Event event) +bool UseInstantPoisonOffHandAction::Execute(Event /*event*/) { std::vector poison_suffixs = {" IX", " VIII", " VII", " VI", " V", " IV", " III", " II", ""}; std::vector items; diff --git a/src/Ai/Class/Shaman/Action/ShamanActions.cpp b/src/Ai/Class/Shaman/Action/ShamanActions.cpp index e8ed1234bf..3bb0ae86f4 100644 --- a/src/Ai/Class/Shaman/Action/ShamanActions.cpp +++ b/src/Ai/Class/Shaman/Action/ShamanActions.cpp @@ -6,7 +6,6 @@ #include "ShamanActions.h" #include "TotemsShamanStrategy.h" #include "Playerbots.h" -#include "Totem.h" #include "PlayerbotAI.h" #include "Action.h" @@ -71,7 +70,7 @@ bool CastLavaBurstAction::isUseful() // Logic for making a guardian (spirit wolf) use a spell (spirit walk) // There is no existing code for guardians casting spells in the AC/Playerbots repo. -bool CastSpiritWalkAction::Execute(Event event) +bool CastSpiritWalkAction::Execute(Event /*event*/) { constexpr uint32 SPIRIT_WOLF = 29264; constexpr uint32 SPIRIT_WALK_SPELL = 58875; @@ -93,7 +92,7 @@ bool CastSpiritWalkAction::Execute(Event event) // Set Strategy Assigned Totems (Actions) - First, it checks // the highest-rank spell the bot knows for each totem type, // then adds it to the Call of the Elements bar. -bool SetTotemAction::Execute(Event event) +bool SetTotemAction::Execute(Event /*event*/) { uint32 totemSpell = 0; for (size_t i = 0; i < totemSpellIdsCount; ++i) diff --git a/src/Ai/Class/Shaman/Strategy/TotemsShamanStrategy.h b/src/Ai/Class/Shaman/Strategy/TotemsShamanStrategy.h index 6d36f4a271..60488740c6 100644 --- a/src/Ai/Class/Shaman/Strategy/TotemsShamanStrategy.h +++ b/src/Ai/Class/Shaman/Strategy/TotemsShamanStrategy.h @@ -7,7 +7,6 @@ #define _PLAYERBOT_TOTEMSSHAMANSTRATEGY_H #include "GenericShamanStrategy.h" -#include #include #include diff --git a/src/Ai/Class/Shaman/Trigger/ShamanTriggers.h b/src/Ai/Class/Shaman/Trigger/ShamanTriggers.h index abf4ed566c..65d5081c86 100644 --- a/src/Ai/Class/Shaman/Trigger/ShamanTriggers.h +++ b/src/Ai/Class/Shaman/Trigger/ShamanTriggers.h @@ -10,9 +10,7 @@ #include "GenericTriggers.h" #include "SharedDefines.h" #include "Trigger.h" -#include #include "TotemsShamanStrategy.h" -#include "Player.h" #include "PlayerbotAI.h" #include diff --git a/src/Ai/Class/Warlock/Action/WarlockActions.cpp b/src/Ai/Class/Warlock/Action/WarlockActions.cpp index 4e09eb6523..93798b702f 100644 --- a/src/Ai/Class/Warlock/Action/WarlockActions.cpp +++ b/src/Ai/Class/Warlock/Action/WarlockActions.cpp @@ -116,7 +116,7 @@ bool CastSoulshatterAction::isUseful() } // Checks if the bot has enough bag space to create a soul shard, then does so -bool CreateSoulShardAction::Execute(Event event) +bool CreateSoulShardAction::Execute(Event /*event*/) { Player* bot = botAI->GetBot(); if (!bot) @@ -188,7 +188,7 @@ bool CastCreateSoulstoneAction::isUseful() return hasSpace; } -bool DestroySoulShardAction::Execute(Event event) +bool DestroySoulShardAction::Execute(Event /*event*/) { // Look for the first soul shard in any bag and destroy it for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) @@ -234,7 +234,7 @@ static bool HasSoulstoneAura(Unit* unit) } // Use the soulstone item on the bot itself with nc strategy "ss self" -bool UseSoulstoneSelfAction::Execute(Event event) +bool UseSoulstoneSelfAction::Execute(Event /*event*/) { std::vector items = AI_VALUE2(std::vector, "inventory items", "soulstone"); if (items.empty()) @@ -266,7 +266,7 @@ void CleanupSoulstoneReservations() } // Use the soulstone item on the bot's master with nc strategy "ss master" -bool UseSoulstoneMasterAction::Execute(Event event) +bool UseSoulstoneMasterAction::Execute(Event /*event*/) { CleanupSoulstoneReservations(); @@ -300,7 +300,7 @@ bool UseSoulstoneMasterAction::Execute(Event event) } // Use the soulstone item on a tank in the group with nc strategy "ss tank" -bool UseSoulstoneTankAction::Execute(Event event) +bool UseSoulstoneTankAction::Execute(Event /*event*/) { CleanupSoulstoneReservations(); @@ -368,7 +368,7 @@ bool UseSoulstoneTankAction::Execute(Event event) } // Use the soulstone item on a healer in the group with nc strategy "ss healer" -bool UseSoulstoneHealerAction::Execute(Event event) +bool UseSoulstoneHealerAction::Execute(Event /*event*/) { CleanupSoulstoneReservations(); @@ -425,7 +425,7 @@ CastCreateFirestoneAction::CastCreateFirestoneAction(PlayerbotAI* botAI) { } -bool CastCreateFirestoneAction::Execute(Event event) +bool CastCreateFirestoneAction::Execute(Event /*event*/) { for (uint32 spellId : firestoneSpellIds) { diff --git a/src/Ai/Class/Warlock/Strategy/GenericWarlockStrategy.cpp b/src/Ai/Class/Warlock/Strategy/GenericWarlockStrategy.cpp index 1759776580..9c85f78612 100644 --- a/src/Ai/Class/Warlock/Strategy/GenericWarlockStrategy.cpp +++ b/src/Ai/Class/Warlock/Strategy/GenericWarlockStrategy.cpp @@ -4,8 +4,6 @@ */ #include "GenericWarlockStrategy.h" -#include "Strategy.h" -#include "Playerbots.h" class GenericWarlockStrategyActionNodeFactory : public NamedObjectFactory { diff --git a/src/Ai/Class/Warlock/Strategy/TankWarlockStrategy.cpp b/src/Ai/Class/Warlock/Strategy/TankWarlockStrategy.cpp index c54fd59684..5c922af90d 100644 --- a/src/Ai/Class/Warlock/Strategy/TankWarlockStrategy.cpp +++ b/src/Ai/Class/Warlock/Strategy/TankWarlockStrategy.cpp @@ -4,7 +4,6 @@ */ #include "TankWarlockStrategy.h" -#include "Playerbots.h" // Combat strategy for a Warlock Tank, for certain bosses like Twin Emperors // Priority is set to spam Searing Pain and use Shadow Ward on CD diff --git a/src/Ai/Class/Warrior/Action/WarriorActions.cpp b/src/Ai/Class/Warrior/Action/WarriorActions.cpp index 0bde24cd94..9f15cf7673 100644 --- a/src/Ai/Class/Warrior/Action/WarriorActions.cpp +++ b/src/Ai/Class/Warrior/Action/WarriorActions.cpp @@ -93,7 +93,7 @@ Unit* CastVigilanceAction::GetTarget() return nullptr; } -bool CastVigilanceAction::Execute(Event event) +bool CastVigilanceAction::Execute(Event /*event*/) { Unit* target = GetTarget(); if (!target || target == bot) @@ -176,7 +176,7 @@ Unit* CastShatteringThrowAction::GetTarget() return nullptr; // No valid target } -bool CastShatteringThrowAction::Execute(Event event) +bool CastShatteringThrowAction::Execute(Event /*event*/) { Unit* target = GetTarget(); if (!target) diff --git a/src/Ai/Class/Warrior/Strategy/ArmsWarriorStrategy.cpp b/src/Ai/Class/Warrior/Strategy/ArmsWarriorStrategy.cpp index eba5677e7f..6d6e0b6554 100644 --- a/src/Ai/Class/Warrior/Strategy/ArmsWarriorStrategy.cpp +++ b/src/Ai/Class/Warrior/Strategy/ArmsWarriorStrategy.cpp @@ -5,8 +5,6 @@ #include "ArmsWarriorStrategy.h" -#include "Playerbots.h" - class ArmsWarriorStrategyActionNodeFactory : public NamedObjectFactory { public: diff --git a/src/Ai/Class/Warrior/Strategy/FuryWarriorStrategy.cpp b/src/Ai/Class/Warrior/Strategy/FuryWarriorStrategy.cpp index 6400ee4a8e..67e5cb461a 100644 --- a/src/Ai/Class/Warrior/Strategy/FuryWarriorStrategy.cpp +++ b/src/Ai/Class/Warrior/Strategy/FuryWarriorStrategy.cpp @@ -5,8 +5,6 @@ #include "FuryWarriorStrategy.h" -#include "Playerbots.h" - class FuryWarriorStrategyActionNodeFactory : public NamedObjectFactory { public: diff --git a/src/Ai/Class/Warrior/Strategy/TankWarriorStrategy.cpp b/src/Ai/Class/Warrior/Strategy/TankWarriorStrategy.cpp index 05f732c30c..e6daa2646c 100644 --- a/src/Ai/Class/Warrior/Strategy/TankWarriorStrategy.cpp +++ b/src/Ai/Class/Warrior/Strategy/TankWarriorStrategy.cpp @@ -5,8 +5,6 @@ #include "TankWarriorStrategy.h" -#include "Playerbots.h" - class TankWarriorStrategyActionNodeFactory : public NamedObjectFactory { public: diff --git a/src/Ai/Dungeon/AzjolNerub/Action/AzjolNerubActions.cpp b/src/Ai/Dungeon/AzjolNerub/Action/AzjolNerubActions.cpp index e31ffd00f5..42026cecf3 100644 --- a/src/Ai/Dungeon/AzjolNerub/Action/AzjolNerubActions.cpp +++ b/src/Ai/Dungeon/AzjolNerub/Action/AzjolNerubActions.cpp @@ -1,9 +1,8 @@ #include "Playerbots.h" #include "AzjolNerubActions.h" -#include "AzjolNerubStrategy.h" bool AttackWebWrapAction::isUseful() { return !botAI->IsHeal(bot); } -bool AttackWebWrapAction::Execute(Event event) +bool AttackWebWrapAction::Execute(Event /*event*/) { Unit* webWrap = nullptr; @@ -29,7 +28,7 @@ bool AttackWebWrapAction::Execute(Event event) } bool WatchersTargetAction::isUseful() { return !botAI->IsHeal(bot); } -bool WatchersTargetAction::Execute(Event event) +bool WatchersTargetAction::Execute(Event /*event*/) { // Always prioritise web wraps Unit* currTarget = AI_VALUE(Unit*, "current target"); @@ -95,7 +94,7 @@ bool WatchersTargetAction::Execute(Event event) } bool AnubarakDodgePoundAction::isUseful() { return !AI_VALUE2(bool, "behind", "current target"); } -bool AnubarakDodgePoundAction::Execute(Event event) +bool AnubarakDodgePoundAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "anub'arak"); if (!boss) { return false; } diff --git a/src/Ai/Dungeon/CullingOfStratholme/Action/CullingOfStratholmeActions.cpp b/src/Ai/Dungeon/CullingOfStratholme/Action/CullingOfStratholmeActions.cpp index 15121f4a2e..b2732c3cfe 100644 --- a/src/Ai/Dungeon/CullingOfStratholme/Action/CullingOfStratholmeActions.cpp +++ b/src/Ai/Dungeon/CullingOfStratholme/Action/CullingOfStratholmeActions.cpp @@ -1,8 +1,7 @@ #include "Playerbots.h" #include "CullingOfStratholmeActions.h" -#include "CullingOfStratholmeStrategy.h" -bool ExplodeGhoulSpreadAction::Execute(Event event) +bool ExplodeGhoulSpreadAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "salramm the fleshcrafter"); if (!boss) { return false; } @@ -38,7 +37,7 @@ bool EpochStackAction::isUseful() // else return !(bot->getClass() == CLASS_HUNTER) && AI_VALUE2(float, "distance", "current target") > 5.0f; } -bool EpochStackAction::Execute(Event event) +bool EpochStackAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "chrono-lord epoch"); if (!boss) { return false; } diff --git a/src/Ai/Dungeon/DraktharonKeep/Action/DrakTharonKeepActions.cpp b/src/Ai/Dungeon/DraktharonKeep/Action/DrakTharonKeepActions.cpp index 8cbbe49c2a..07e23699e7 100644 --- a/src/Ai/Dungeon/DraktharonKeep/Action/DrakTharonKeepActions.cpp +++ b/src/Ai/Dungeon/DraktharonKeep/Action/DrakTharonKeepActions.cpp @@ -1,8 +1,7 @@ #include "Playerbots.h" #include "DrakTharonKeepActions.h" -#include "DrakTharonKeepStrategy.h" -bool CorpseExplodeSpreadAction::Execute(Event event) +bool CorpseExplodeSpreadAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "trollgore"); if (!boss) { return false; } @@ -17,15 +16,13 @@ bool CorpseExplodeSpreadAction::Execute(Event event) { float currentDistance = bot->GetExactDist2d(unit); if (currentDistance < distance + distanceExtra) - { return MoveAway(unit, distance + distanceExtra - currentDistance); - } } } return false; } -bool AvoidArcaneFieldAction::Execute(Event event) +bool AvoidArcaneFieldAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "novos the summoner"); if (!boss) { return false; } @@ -44,7 +41,7 @@ bool NovosDefaultPositionAction::isUseful() float threshold = 15.0f; return bot->GetDistance(NOVOS_PARTY_POSITION) > threshold; } -bool NovosDefaultPositionAction::Execute(Event event) +bool NovosDefaultPositionAction::Execute(Event /*event*/) { float clusterDistance = 4.0f; // Only reposition if we're not killing anything @@ -59,7 +56,7 @@ bool NovosDefaultPositionAction::Execute(Event event) return false; } -bool NovosTargetPriorityAction::Execute(Event event) +bool NovosTargetPriorityAction::Execute(Event /*event*/) { // TODO: This can be improved, some parts are still buggy. // But it works for now and this fight is very easy @@ -89,9 +86,7 @@ bool NovosTargetPriorityAction::Execute(Event event) // Ranged dps, only set if none already assigned. // Don't break, we want to keep searching for a melee instead. if (!stairsDps) - { stairsDps = groupMember; - } } } } @@ -112,13 +107,11 @@ bool NovosTargetPriorityAction::Execute(Event event) if (botAI->IsTank(bot)) { if (creatureId == NPC_HULKING_CORPSE) - { selectedTargets[0] = unit; - } + else if (creatureId == NPC_CRYSTAL_HANDLER) - { selectedTargets[1] = unit; - } + } // Dedicated stairs dps is assigned. // Priority: Risen Shadowcaster -> Fetid Troll Corpse @@ -127,17 +120,13 @@ bool NovosTargetPriorityAction::Execute(Event event) if (creatureId == NPC_RISEN_SHADOWCASTER) { if (!selectedTargets[0] || bot->GetDistance(unit) < bot->GetDistance(selectedTargets[0]) - 5.0f) - { selectedTargets[0] = unit; - } } else if (creatureId == NPC_FETID_TROLL_CORPSE) { if (!selectedTargets[1] || bot->GetDistance(unit) < bot->GetDistance(selectedTargets[1]) - 5.0f) - { selectedTargets[1] = unit; - } } } // All other dps priority: @@ -145,13 +134,10 @@ bool NovosTargetPriorityAction::Execute(Event event) else if (botAI->IsDps(bot)) { if (creatureId == NPC_CRYSTAL_HANDLER) - { selectedTargets[0] = unit; - } + else if (creatureId == NPC_HULKING_CORPSE) - { selectedTargets[1] = unit; - } } } diff --git a/src/Ai/Dungeon/ForgeOfSouls/Action/ForgeOfSoulsActions.cpp b/src/Ai/Dungeon/ForgeOfSouls/Action/ForgeOfSoulsActions.cpp index e72fe98d5f..d83b69bb4b 100644 --- a/src/Ai/Dungeon/ForgeOfSouls/Action/ForgeOfSoulsActions.cpp +++ b/src/Ai/Dungeon/ForgeOfSouls/Action/ForgeOfSoulsActions.cpp @@ -1,9 +1,7 @@ #include "Playerbots.h" #include "ForgeOfSoulsActions.h" -#include "ForgeOfSoulsStrategy.h" -#include "SharedDefines.h" -bool MoveFromBronjahmAction::Execute(Event event) +bool MoveFromBronjahmAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "bronjahm"); if (!boss) @@ -17,7 +15,7 @@ bool MoveFromBronjahmAction::Execute(Event event) return false; } -bool AttackCorruptedSoulFragmentAction::Execute(Event event) +bool AttackCorruptedSoulFragmentAction::Execute(Event /*event*/) { Unit* currentTarget = AI_VALUE(Unit*, "current target"); GuidVector targets = AI_VALUE(GuidVector, "possible targets"); @@ -51,7 +49,7 @@ bool AttackCorruptedSoulFragmentAction::Execute(Event event) return false; } -bool BronjahmGroupPositionAction::Execute(Event event) +bool BronjahmGroupPositionAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "bronjahm"); if (!boss) @@ -141,7 +139,7 @@ bool BronjahmGroupPositionAction::Execute(Event event) bool BronjahmGroupPositionAction::isUseful() { return true; } -bool DevourerOfSoulsAction::Execute(Event event) +bool DevourerOfSoulsAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "devourer of souls"); if (!boss) diff --git a/src/Ai/Dungeon/Gundrak/Action/GundrakActions.cpp b/src/Ai/Dungeon/Gundrak/Action/GundrakActions.cpp index 4e9910993d..775e8ef29a 100644 --- a/src/Ai/Dungeon/Gundrak/Action/GundrakActions.cpp +++ b/src/Ai/Dungeon/Gundrak/Action/GundrakActions.cpp @@ -1,8 +1,7 @@ #include "Playerbots.h" #include "GundrakActions.h" -#include "GundrakStrategy.h" -bool AvoidPoisonNovaAction::Execute(Event event) +bool AvoidPoisonNovaAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "slad'ran"); if (!boss) { return false; } @@ -19,7 +18,7 @@ bool AvoidPoisonNovaAction::Execute(Event event) return false; } -bool AttackSnakeWrapAction::Execute(Event event) +bool AttackSnakeWrapAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "slad'ran"); if (!boss) { return false; } @@ -45,7 +44,7 @@ bool AttackSnakeWrapAction::Execute(Event event) return false; } -bool AvoidWhirlingSlashAction::Execute(Event event) +bool AvoidWhirlingSlashAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "gal'darah"); if (!boss) { return false; } diff --git a/src/Ai/Dungeon/HallsOfLightning/Action/HallsOfLightningActions.cpp b/src/Ai/Dungeon/HallsOfLightning/Action/HallsOfLightningActions.cpp index 047e0aee13..7dcf643334 100644 --- a/src/Ai/Dungeon/HallsOfLightning/Action/HallsOfLightningActions.cpp +++ b/src/Ai/Dungeon/HallsOfLightning/Action/HallsOfLightningActions.cpp @@ -1,8 +1,7 @@ #include "Playerbots.h" #include "HallsOfLightningActions.h" -#include "HallsOfLightningStrategy.h" -bool BjarngrimTargetAction::Execute(Event event) +bool BjarngrimTargetAction::Execute(Event /*event*/) { Unit* target = nullptr; @@ -35,7 +34,7 @@ bool BjarngrimTargetAction::Execute(Event event) return Attack(target); } -bool AvoidWhirlwindAction::Execute(Event event) +bool AvoidWhirlwindAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "general bjarngrim"); if (!boss) { return false; } @@ -52,7 +51,7 @@ bool AvoidWhirlwindAction::Execute(Event event) return false; } -bool VolkhanTargetAction::Execute(Event event) +bool VolkhanTargetAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "volkhan"); if (!boss || AI_VALUE(Unit*, "current target") == boss) @@ -63,7 +62,7 @@ bool VolkhanTargetAction::Execute(Event event) return Attack(boss); } -bool StaticOverloadSpreadAction::Execute(Event event) +bool StaticOverloadSpreadAction::Execute(Event /*event*/) { float radius = 8.0f; float distanceExtra = 2.0f; @@ -86,7 +85,7 @@ bool StaticOverloadSpreadAction::Execute(Event event) return false; } -bool BallLightningSpreadAction::Execute(Event event) +bool BallLightningSpreadAction::Execute(Event /*event*/) { float radius = 6.0f; float distanceExtra = 1.0f; @@ -108,14 +107,14 @@ bool BallLightningSpreadAction::Execute(Event event) } bool IonarTankPositionAction::isUseful() { return bot->GetExactDist2d(IONAR_TANK_POSITION) > 10.0f; } -bool IonarTankPositionAction::Execute(Event event) +bool IonarTankPositionAction::Execute(Event /*event*/) { return MoveTo(bot->GetMapId(), IONAR_TANK_POSITION.GetPositionX(), IONAR_TANK_POSITION.GetPositionY(), IONAR_TANK_POSITION.GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_COMBAT); } bool DispersePositionAction::isUseful() { return bot->GetExactDist2d(DISPERSE_POSITION) > 8.0f; } -bool DispersePositionAction::Execute(Event event) +bool DispersePositionAction::Execute(Event /*event*/) { return MoveTo(bot->GetMapId(), DISPERSE_POSITION.GetPositionX(), DISPERSE_POSITION.GetPositionY(), DISPERSE_POSITION.GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_COMBAT); @@ -133,7 +132,7 @@ bool LokenStackAction::isUseful() // else return AI_VALUE2(float, "distance", "current target") > 2.0f; } -bool LokenStackAction::Execute(Event event) +bool LokenStackAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "loken"); if (!boss) { return false; } @@ -152,7 +151,7 @@ bool LokenStackAction::Execute(Event event) return false; } -bool AvoidLightningNovaAction::Execute(Event event) +bool AvoidLightningNovaAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "loken"); if (!boss) { return false; } diff --git a/src/Ai/Dungeon/HallsOfStone/Action/HallsOfStoneActions.cpp b/src/Ai/Dungeon/HallsOfStone/Action/HallsOfStoneActions.cpp index 30562f090d..51f1072b55 100644 --- a/src/Ai/Dungeon/HallsOfStone/Action/HallsOfStoneActions.cpp +++ b/src/Ai/Dungeon/HallsOfStone/Action/HallsOfStoneActions.cpp @@ -1,8 +1,7 @@ #include "Playerbots.h" #include "HallsOfStoneActions.h" -#include "HallsOfStoneStrategy.h" -bool ShatterSpreadAction::Execute(Event event) +bool ShatterSpreadAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "krystallus"); if (!boss) { return false; } @@ -35,7 +34,7 @@ bool ShatterSpreadAction::Execute(Event event) return false; } -bool AvoidLightningRingAction::Execute(Event event) +bool AvoidLightningRingAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "sjonnir the ironshaper"); if (!boss) { return false; } diff --git a/src/Ai/Dungeon/Nexus/Action/NexusActions.cpp b/src/Ai/Dungeon/Nexus/Action/NexusActions.cpp index f61a95aef4..12b546b5ea 100644 --- a/src/Ai/Dungeon/Nexus/Action/NexusActions.cpp +++ b/src/Ai/Dungeon/Nexus/Action/NexusActions.cpp @@ -1,8 +1,7 @@ #include "Playerbots.h" #include "NexusActions.h" -#include "NexusStrategy.h" -bool MoveFromWhirlwindAction::Execute(Event event) +bool MoveFromWhirlwindAction::Execute(Event /*event*/) { Unit* boss = nullptr; uint8 faction = bot->GetTeamId(); @@ -12,23 +11,19 @@ bool MoveFromWhirlwindAction::Execute(Event event) { case DUNGEON_DIFFICULTY_NORMAL: if (faction == TEAM_ALLIANCE) - { boss = AI_VALUE2(Unit*, "find target", "horde commander"); - } + else // TEAM_HORDE - { boss = AI_VALUE2(Unit*, "find target", "alliance commander"); - } + break; case DUNGEON_DIFFICULTY_HEROIC: if (faction == TEAM_ALLIANCE) - { boss = AI_VALUE2(Unit*, "find target", "commander kolurg"); - } + else // TEAM_HORDE - { boss = AI_VALUE2(Unit*, "find target", "commander stoutbeard"); - } + break; default: break; @@ -36,23 +31,19 @@ bool MoveFromWhirlwindAction::Execute(Event event) // Ensure boss is valid before accessing its methods if (!boss) - { return false; - } float bossDistance = bot->GetExactDist2d(boss->GetPosition()); // Check if the bot is already at a safe distance if (bossDistance > targetDist) - { return false; - } // Move away from the boss to avoid Whirlwind return MoveAway(boss, targetDist - bossDistance); } -bool FirebombSpreadAction::Execute(Event event) +bool FirebombSpreadAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "grand magus telestra"); float radius = 5.0f; @@ -66,15 +57,14 @@ bool FirebombSpreadAction::Execute(Event event) if (!unit || bot->GetGUID() == member) { continue; } if (bot->GetExactDist2d(unit) < targetDist) - { return MoveAway(unit, targetDist); - } + } return false; } bool TelestraSplitTargetAction::isUseful() { return !botAI->IsHeal(bot); } -bool TelestraSplitTargetAction::Execute(Event event) +bool TelestraSplitTargetAction::Execute(Event /*event*/) { GuidVector attackers = AI_VALUE(GuidVector, "attackers"); Unit* splitTargets[3] = {nullptr, nullptr, nullptr}; @@ -107,9 +97,8 @@ bool TelestraSplitTargetAction::Execute(Event event) if (target) { if (AI_VALUE(Unit*, "current target") != target) - { return Attack(target); - } + // Don't continue loop here, the target exists so we don't // want to move down the prio list. We just don't need to send attack // command again, just return false and exit the loop that way @@ -121,7 +110,7 @@ bool TelestraSplitTargetAction::Execute(Event event) } bool ChaoticRiftTargetAction::isUseful() { return !botAI->IsHeal(bot); } -bool ChaoticRiftTargetAction::Execute(Event event) +bool ChaoticRiftTargetAction::Execute(Event /*event*/) { Unit* chaoticRift = nullptr; @@ -139,9 +128,8 @@ bool ChaoticRiftTargetAction::Execute(Event event) } } if (!chaoticRift || AI_VALUE(Unit*, "current target") == chaoticRift) - { return false; - } + return Attack(chaoticRift); } @@ -152,7 +140,7 @@ bool DodgeSpikesAction::isUseful() return bot->GetExactDist2d(boss) > 0.5f; } -bool DodgeSpikesAction::Execute(Event event) +bool DodgeSpikesAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "ormorok the tree-shaper"); if (!boss) { return false; } @@ -160,7 +148,7 @@ bool DodgeSpikesAction::Execute(Event event) return Move(bot->GetAngle(boss), bot->GetExactDist2d(boss) - 0.3f); } -bool IntenseColdJumpAction::Execute(Event event) +bool IntenseColdJumpAction::Execute(Event /*event*/) { // This needs improving but maybe it should be done in the playerbot core. // Jump doesn't seem to support zero offset (eg. jump on the spot) so need to add a tiny delta. diff --git a/src/Ai/Dungeon/Oculus/Action/OculusActions.cpp b/src/Ai/Dungeon/Oculus/Action/OculusActions.cpp index bbb94d110a..3854510036 100644 --- a/src/Ai/Dungeon/Oculus/Action/OculusActions.cpp +++ b/src/Ai/Dungeon/Oculus/Action/OculusActions.cpp @@ -1,9 +1,9 @@ +#include "OculusTriggers.h" #include "Playerbots.h" #include "OculusActions.h" -#include "OculusStrategy.h" #include "LastSpellCastValue.h" -bool AvoidUnstableSphereAction::Execute(Event event) +bool AvoidUnstableSphereAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "drakos the interrogator"); if (!boss) { return false; } @@ -34,7 +34,7 @@ bool AvoidUnstableSphereAction::Execute(Event event) } bool MountDrakeAction::isPossible() { return bot->GetMapId() == OCULUS_MAP_ID; } -bool MountDrakeAction::Execute(Event event) +bool MountDrakeAction::Execute(Event /*event*/) { std::map drakeAssignments; // Composition can be adjusted - both 3/1/1 and 2/2/1 are good default comps @@ -88,7 +88,7 @@ bool MountDrakeAction::Execute(Event event) } // Correct/update the drake items in inventories incase assignments have changed - for (uint32 itemId : DRAKE_ITEMS) + for (int64_t itemId : DRAKE_ITEMS) { Item* item = bot->GetItemByEntry(itemId); if (!item) { continue; } @@ -109,7 +109,7 @@ bool MountDrakeAction::Execute(Event event) return false; } -bool DismountDrakeAction::Execute(Event event) +bool DismountDrakeAction::Execute(Event /*event*/) { if (bot->GetVehicle()) { @@ -119,7 +119,7 @@ bool DismountDrakeAction::Execute(Event event) return false; } -bool OccFlyDrakeAction::Execute(Event event) +bool OccFlyDrakeAction::Execute(Event /*event*/) { Player* master = botAI->GetMaster(); if (!master) { return false; } @@ -160,7 +160,7 @@ bool OccFlyDrakeAction::Execute(Event event) return false; } -bool OccDrakeAttackAction::Execute(Event event) +bool OccDrakeAttackAction::Execute(Event /*event*/) { vehicleBase = bot->GetVehicleBase(); if (!vehicleBase) { return false; } @@ -319,7 +319,7 @@ bool OccDrakeAttackAction::RubyDrakeAction(Unit* target) return CastDrakeSpellAction(target, SPELL_SEARING_WRATH, 0); } -bool AvoidArcaneExplosionAction::Execute(Event event) +bool AvoidArcaneExplosionAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "mage-lord urom"); if (!boss) { return false; } @@ -339,7 +339,7 @@ bool AvoidArcaneExplosionAction::Execute(Event event) return MoveNear(bot->GetMapId(), closestPos->GetPositionX(), closestPos->GetPositionY(), closestPos->GetPositionZ(), 2.0f, MovementPriority::MOVEMENT_COMBAT); } -bool TimeBombSpreadAction::Execute(Event event) +bool TimeBombSpreadAction::Execute(Event /*event*/) { float radius = 10.0f; float distanceExtra = 2.0f; diff --git a/src/Ai/Dungeon/Oculus/Action/OculusActions.h b/src/Ai/Dungeon/Oculus/Action/OculusActions.h index 6b26b9ddbc..6616ce884b 100644 --- a/src/Ai/Dungeon/Oculus/Action/OculusActions.h +++ b/src/Ai/Dungeon/Oculus/Action/OculusActions.h @@ -4,8 +4,6 @@ #include "Action.h" #include "AttackAction.h" #include "PlayerbotAI.h" -#include "Playerbots.h" -#include "OculusTriggers.h" #include "UseItemAction.h" #include "GenericSpellActions.h" diff --git a/src/Ai/Dungeon/Oculus/Multiplier/OculusMultipliers.cpp b/src/Ai/Dungeon/Oculus/Multiplier/OculusMultipliers.cpp index 7be543ee70..f8fcdd3f16 100644 --- a/src/Ai/Dungeon/Oculus/Multiplier/OculusMultipliers.cpp +++ b/src/Ai/Dungeon/Oculus/Multiplier/OculusMultipliers.cpp @@ -6,6 +6,7 @@ #include "OculusTriggers.h" #include "FollowActions.h" #include "ReachTargetActions.h" +#include "Playerbots.h" float MountingDrakeMultiplier::GetValue(Action* action) { @@ -15,14 +16,14 @@ float MountingDrakeMultiplier::GetValue(Action* action) // It seems like this is due to moving/other actions being processed during the 0.5 secs. // If we suppress everything, they seem to mount properly. A bit of a ham-fisted solution but it works Player* master = botAI->GetMaster(); - if (!master) { return 1.0f; } + if (!master) + return 1.0f; if (bot->GetMapId() != OCULUS_MAP_ID || !master->GetVehicleBase() || bot->GetVehicleBase()) { return 1.0f; } if (!dynamic_cast(action)) - { return 0.0f; - } + return 1.0f; } @@ -32,9 +33,8 @@ float OccFlyingMultiplier::GetValue(Action* action) // Suppresses FollowAction as well as some attack-based movements if (dynamic_cast(action) && !dynamic_cast(action)) - { return 0.0f; - } + return 1.0f; } @@ -44,9 +44,7 @@ float UromMultiplier::GetValue(Action* action) { Unit* target = action->GetTarget(); if (target && target->GetEntry() == NPC_MAGE_LORD_UROM) - { return 0.0f; - } } Unit* boss = AI_VALUE2(Unit*, "find target", "mage-lord urom"); @@ -57,26 +55,20 @@ float UromMultiplier::GetValue(Action* action) boss->FindCurrentSpellBySpellId(SPELL_EMPOWERED_ARCANE_EXPLOSION)) { if (dynamic_cast(action) && !dynamic_cast(action)) - { return 0.0f; - } } // Don't bother avoiding Frostbomb for melee if (botAI->IsMelee(bot)) { if (dynamic_cast(action)) - { return 0.0f; - } } if (bot->HasAura(SPELL_TIME_BOMB)) { if (dynamic_cast(action) && !dynamic_cast(action)) - { return 0.0f; - } } return 1.0f; @@ -91,9 +83,7 @@ uint8 UromMultiplier::GetPhaseByCurrentPosition(Unit* unit) for (uint8 i = 0; i < 3; ++i) { if (unit->GetDistance(uromCoords[i][0], uromCoords[i][1], uromCoords[i][2]) < distance) - { return i; - } } return 3; } @@ -104,8 +94,7 @@ float EregosMultiplier::GetValue(Action* action) if (!boss) { return 1.0f; } if (boss->HasAura(SPELL_PLANAR_SHIFT && dynamic_cast(action))) - { return 0.0f; - } + return 1.0f; } diff --git a/src/Ai/Dungeon/OldKingdom/Action/OldKingdomActions.cpp b/src/Ai/Dungeon/OldKingdom/Action/OldKingdomActions.cpp index 916bf29200..8a5250a8d8 100644 --- a/src/Ai/Dungeon/OldKingdom/Action/OldKingdomActions.cpp +++ b/src/Ai/Dungeon/OldKingdom/Action/OldKingdomActions.cpp @@ -1,8 +1,7 @@ #include "Playerbots.h" #include "OldKingdomActions.h" -#include "OldKingdomStrategy.h" -bool AttackNadoxGuardianAction::Execute(Event event) +bool AttackNadoxGuardianAction::Execute(Event /*event*/) { Unit* target = AI_VALUE2(Unit*, "find target", "ahn'kahar guardian"); if (!target || AI_VALUE(Unit*, "current target") == target) @@ -12,7 +11,7 @@ bool AttackNadoxGuardianAction::Execute(Event event) return Attack(target); } -bool AttackJedogaVolunteerAction::Execute(Event event) +bool AttackJedogaVolunteerAction::Execute(Event /*event*/) { Unit* target = nullptr; // Target is not findable from threat table using AI_VALUE2(), @@ -40,7 +39,7 @@ bool AttackJedogaVolunteerAction::Execute(Event event) return Attack(target); } -bool AvoidShadowCrashAction::Execute(Event event) +bool AvoidShadowCrashAction::Execute(Event /*event*/) { // Could check all enemy units in range as it's possible to pull multiple of these mobs. // They should really be killed 1 by 1, multipulls are messy so we just handle singles for now diff --git a/src/Ai/Dungeon/PitOfSaron/Action/PitOfSaronActions.cpp b/src/Ai/Dungeon/PitOfSaron/Action/PitOfSaronActions.cpp index d435a208d9..577575d428 100644 --- a/src/Ai/Dungeon/PitOfSaron/Action/PitOfSaronActions.cpp +++ b/src/Ai/Dungeon/PitOfSaron/Action/PitOfSaronActions.cpp @@ -1,9 +1,7 @@ #include "Playerbots.h" #include "PitOfSaronActions.h" -#include "PitOfSaronStrategy.h" -#include "SharedDefines.h" -bool IckAndKrickAction::Execute(Event event) +bool IckAndKrickAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "Ick"); if (!boss) @@ -271,7 +269,7 @@ bool IckAndKrickAction::ExplosiveBarrage(bool explosiveBarrage, Unit* boss) return false; } -bool TyrannusAction::Execute(Event event) +bool TyrannusAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "scourgelord tyrannus"); if (!boss) diff --git a/src/Ai/Dungeon/TrialOfTheChampion/Action/TrialOfTheChampionActions.cpp b/src/Ai/Dungeon/TrialOfTheChampion/Action/TrialOfTheChampionActions.cpp index 92b687b292..da865a2d17 100644 --- a/src/Ai/Dungeon/TrialOfTheChampion/Action/TrialOfTheChampionActions.cpp +++ b/src/Ai/Dungeon/TrialOfTheChampion/Action/TrialOfTheChampionActions.cpp @@ -1,15 +1,12 @@ #include "Playerbots.h" #include "TrialOfTheChampionActions.h" -#include "TrialOfTheChampionStrategy.h" #include "NearestNpcsValue.h" #include "ObjectAccessor.h" -#include "Timer.h" #include "Vehicle.h" #include "GenericSpellActions.h" #include "GenericActions.h" -#include -bool ToCLanceAction::Execute(Event event) +bool ToCLanceAction::Execute(Event /*event*/) { // If already has lance equipped, do nothing if (bot->HasItemOrGemWithIdEquipped(ITEM_LANCE, 1)) @@ -64,13 +61,11 @@ bool ToCLanceAction::Execute(Event event) // First unequip current weapon if it exists if (oldWeapon) - { bot->SwapItem(oldWeapon->GetPos(), lanceItem->GetPos()); - } + else - { bot->EquipItem(EQUIPMENT_SLOT_MAINHAND, lanceItem, true); - } + return true; } @@ -105,7 +100,7 @@ bool ToCUELanceAction::Execute(Event event) return false; } -bool ToCMountedAction::Execute(Event event) +bool ToCMountedAction::Execute(Event /*event*/) { Unit* vehicleBase = bot->GetVehicleBase(); Vehicle* vehicle = bot->GetVehicle(); @@ -179,7 +174,7 @@ bool ToCMountedAction::Execute(Event event) return false; } -bool ToCMountAction::Execute(Event event) +bool ToCMountAction::Execute(Event /*event*/) { // do not switch vehicles yet if (bot->GetVehicle()) @@ -244,7 +239,7 @@ bool ToCMountAction::EnterVehicle(Unit* vehicleBase, bool moveIfFar) return true; } -bool ToCEadricAction::Execute(Event event) +bool ToCEadricAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "eadric the pure"); if (!boss) diff --git a/src/Ai/Dungeon/TrialOfTheChampion/Strategy/TrialOfTheChampionStrategy.cpp b/src/Ai/Dungeon/TrialOfTheChampion/Strategy/TrialOfTheChampionStrategy.cpp index bbccca71f3..323970d52d 100644 --- a/src/Ai/Dungeon/TrialOfTheChampion/Strategy/TrialOfTheChampionStrategy.cpp +++ b/src/Ai/Dungeon/TrialOfTheChampion/Strategy/TrialOfTheChampionStrategy.cpp @@ -1,5 +1,4 @@ #include "TrialOfTheChampionStrategy.h" -#include "TrialOfTheChampionMultipliers.h" void WotlkDungeonToCStrategy::InitTriggers(std::vector &triggers) { diff --git a/src/Ai/Dungeon/UtgardeKeep/Action/UtgardeKeepActions.cpp b/src/Ai/Dungeon/UtgardeKeep/Action/UtgardeKeepActions.cpp index 20a5f27b49..85d8590ee2 100644 --- a/src/Ai/Dungeon/UtgardeKeep/Action/UtgardeKeepActions.cpp +++ b/src/Ai/Dungeon/UtgardeKeep/Action/UtgardeKeepActions.cpp @@ -1,9 +1,8 @@ #include "Playerbots.h" #include "UtgardeKeepActions.h" -#include "UtgardeKeepStrategy.h" bool AttackFrostTombAction::isUseful() { return !botAI->IsHeal(bot); } -bool AttackFrostTombAction::Execute(Event event) +bool AttackFrostTombAction::Execute(Event /*event*/) { Unit* frostTomb = nullptr; @@ -28,7 +27,7 @@ bool AttackFrostTombAction::Execute(Event event) } // TODO: Possibly add player stacking behaviour close to tank, to prevent Skarvald charging ranged -bool AttackDalronnAction::Execute(Event event) +bool AttackDalronnAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "dalronn the controller"); if (!boss) { return false; } @@ -40,7 +39,7 @@ bool AttackDalronnAction::Execute(Event event) return Attack(boss); } -bool IngvarStopCastingAction::Execute(Event event) +bool IngvarStopCastingAction::Execute(Event /*event*/) { // Doesn't work, this action gets queued behind the current spell instead of interrupting it Unit* boss = AI_VALUE2(Unit*, "find target", "ingvar the plunderer"); @@ -64,7 +63,7 @@ bool IngvarStopCastingAction::Execute(Event event) } bool IngvarDodgeSmashAction::isUseful() { return !AI_VALUE2(bool, "behind", "current target"); } -bool IngvarDodgeSmashAction::Execute(Event event) +bool IngvarDodgeSmashAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "ingvar the plunderer"); if (!boss) { return false; } @@ -78,7 +77,7 @@ bool IngvarDodgeSmashAction::Execute(Event event) } bool IngvarSmashReturnAction::isUseful() { return AI_VALUE2(bool, "behind", "current target"); } -bool IngvarSmashReturnAction::Execute(Event event) +bool IngvarSmashReturnAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "ingvar the plunderer"); if (!boss) { return false; } diff --git a/src/Ai/Dungeon/UtgardePinnacle/Action/UtgardePinnacleActions.cpp b/src/Ai/Dungeon/UtgardePinnacle/Action/UtgardePinnacleActions.cpp index f084ca15b6..8025f1aac0 100644 --- a/src/Ai/Dungeon/UtgardePinnacle/Action/UtgardePinnacleActions.cpp +++ b/src/Ai/Dungeon/UtgardePinnacle/Action/UtgardePinnacleActions.cpp @@ -1,8 +1,7 @@ #include "Playerbots.h" #include "UtgardePinnacleActions.h" -#include "UtgardePinnacleStrategy.h" -bool AvoidFreezingCloudAction::Execute(Event event) +bool AvoidFreezingCloudAction::Execute(Event /*event*/) { Unit* closestTrigger = nullptr; GuidVector objects = AI_VALUE(GuidVector, "nearest hostile npcs"); @@ -36,7 +35,7 @@ bool AvoidFreezingCloudAction::Execute(Event event) return false; } -bool AvoidSkadiWhirlwindAction::Execute(Event event) +bool AvoidSkadiWhirlwindAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "skadi the ruthless"); if (!boss) { return false; } diff --git a/src/Ai/Dungeon/VioletHold/Action/VioletHoldActions.cpp b/src/Ai/Dungeon/VioletHold/Action/VioletHoldActions.cpp index b590642d8b..b61f84bdfb 100644 --- a/src/Ai/Dungeon/VioletHold/Action/VioletHoldActions.cpp +++ b/src/Ai/Dungeon/VioletHold/Action/VioletHoldActions.cpp @@ -1,8 +1,7 @@ #include "Playerbots.h" #include "VioletHoldActions.h" -#include "VioletHoldStrategy.h" -bool AttackErekemAction::Execute(Event event) +bool AttackErekemAction::Execute(Event /*event*/) { // Focus boss first, adds after Unit* boss = AI_VALUE2(Unit*, "find target", "erekem"); @@ -15,7 +14,7 @@ bool AttackErekemAction::Execute(Event event) return false; } -bool AttackIchorGlobuleAction::Execute(Event event) +bool AttackIchorGlobuleAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "ichoron"); if (!boss) { return false; } @@ -60,7 +59,7 @@ bool AttackIchorGlobuleAction::Execute(Event event) return false; } -bool AttackVoidSentryAction::Execute(Event event) +bool AttackVoidSentryAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "zuramat the obliterator"); if (!boss) { return false; } @@ -96,7 +95,7 @@ bool AttackVoidSentryAction::Execute(Event event) return false; } -bool StopAttackAction::Execute(Event event) +bool StopAttackAction::Execute(Event /*event*/) { return bot->AttackStop(); } diff --git a/src/Ai/Raid/Aq20/Action/RaidAq20Actions.cpp b/src/Ai/Raid/Aq20/Action/RaidAq20Actions.cpp index 1bf33147f8..5d9b4aaa36 100644 --- a/src/Ai/Raid/Aq20/Action/RaidAq20Actions.cpp +++ b/src/Ai/Raid/Aq20/Action/RaidAq20Actions.cpp @@ -1,9 +1,8 @@ #include "RaidAq20Actions.h" -#include "Playerbots.h" #include "RaidAq20Utils.h" -bool Aq20UseCrystalAction::Execute(Event event) +bool Aq20UseCrystalAction::Execute(Event /*event*/) { if (Unit* boss = AI_VALUE2(Unit*, "find target", "ossirian the unscarred")) { diff --git a/src/Ai/Raid/Aq20/Trigger/RaidAq20Triggers.cpp b/src/Ai/Raid/Aq20/Trigger/RaidAq20Triggers.cpp index 1a580d6bd3..913cb26906 100644 --- a/src/Ai/Raid/Aq20/Trigger/RaidAq20Triggers.cpp +++ b/src/Ai/Raid/Aq20/Trigger/RaidAq20Triggers.cpp @@ -1,6 +1,5 @@ #include "RaidAq20Triggers.h" -#include "SharedDefines.h" #include "RaidAq20Utils.h" bool Aq20MoveToCrystalTrigger::IsActive() diff --git a/src/Ai/Raid/BlackwingLair/Action/RaidBwlActions.cpp b/src/Ai/Raid/BlackwingLair/Action/RaidBwlActions.cpp index 54b21791e8..9014345cc0 100644 --- a/src/Ai/Raid/BlackwingLair/Action/RaidBwlActions.cpp +++ b/src/Ai/Raid/BlackwingLair/Action/RaidBwlActions.cpp @@ -2,7 +2,7 @@ #include "Playerbots.h" -bool BwlOnyxiaScaleCloakAuraCheckAction::Execute(Event event) +bool BwlOnyxiaScaleCloakAuraCheckAction::Execute(Event /*event*/) { bot->AddAura(22683, bot); return true; @@ -10,7 +10,7 @@ bool BwlOnyxiaScaleCloakAuraCheckAction::Execute(Event event) bool BwlOnyxiaScaleCloakAuraCheckAction::isUseful() { return !bot->HasAura(22683); } -bool BwlTurnOffSuppressionDeviceAction::Execute(Event event) +bool BwlTurnOffSuppressionDeviceAction::Execute(Event /*event*/) { GuidVector gos = AI_VALUE(GuidVector, "nearest game objects"); for (GuidVector::iterator i = gos.begin(); i != gos.end(); i++) @@ -29,4 +29,7 @@ bool BwlTurnOffSuppressionDeviceAction::Execute(Event event) return true; } -bool BwlUseHourglassSandAction::Execute(Event event) { return botAI->CastSpell(23645, bot); } +bool BwlUseHourglassSandAction::Execute(Event /*event*/) +{ + return botAI->CastSpell(23645, bot); +} diff --git a/src/Ai/Raid/EyeOfEternity/Action/RaidEoEActions.cpp b/src/Ai/Raid/EyeOfEternity/Action/RaidEoEActions.cpp index 3547388426..b08db3f33e 100644 --- a/src/Ai/Raid/EyeOfEternity/Action/RaidEoEActions.cpp +++ b/src/Ai/Raid/EyeOfEternity/Action/RaidEoEActions.cpp @@ -2,7 +2,7 @@ #include "RaidEoEActions.h" #include "RaidEoETriggers.h" -bool MalygosPositionAction::Execute(Event event) +bool MalygosPositionAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "malygos"); if (!boss) { return false; } @@ -65,7 +65,7 @@ bool MalygosPositionAction::Execute(Event event) return false; } -bool MalygosTargetAction::Execute(Event event) +bool MalygosTargetAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "malygos"); if (!boss) { return false; } @@ -229,7 +229,7 @@ bool EoEFlyDrakeAction::isPossible() Unit* vehicleBase = bot->GetVehicleBase(); return (vehicleBase && vehicleBase->GetEntry() == NPC_WYRMREST_SKYTALON); } -bool EoEFlyDrakeAction::Execute(Event event) +bool EoEFlyDrakeAction::Execute(Event /*event*/) { Player* master = botAI->GetMaster(); if (!master) { return false; } @@ -279,7 +279,7 @@ bool EoEDrakeAttackAction::isPossible() return (vehicleBase && vehicleBase->GetEntry() == NPC_WYRMREST_SKYTALON); } -bool EoEDrakeAttackAction::Execute(Event event) +bool EoEDrakeAttackAction::Execute(Event /*event*/) { vehicleBase = bot->GetVehicleBase(); if (!vehicleBase) @@ -367,8 +367,6 @@ bool EoEDrakeAttackAction::DrakeDpsAction(Unit* target) Unit* vehicleBase = bot->GetVehicleBase(); if (!vehicleBase) { return false; } - Vehicle* veh = bot->GetVehicle(); - uint8 comboPoints = vehicleBase->GetComboPoints(target); if (comboPoints >= 2) { diff --git a/src/Ai/Raid/GruulsLair/Action/RaidGruulsLairActions.cpp b/src/Ai/Raid/GruulsLair/Action/RaidGruulsLairActions.cpp index 2af82ebe99..1a45b0c156 100644 --- a/src/Ai/Raid/GruulsLair/Action/RaidGruulsLairActions.cpp +++ b/src/Ai/Raid/GruulsLair/Action/RaidGruulsLairActions.cpp @@ -10,7 +10,7 @@ using namespace GruulsLairHelpers; // High King Maulgar Actions // Main tank on Maulgar -bool HighKingMaulgarMainTankAttackMaulgarAction::Execute(Event event) +bool HighKingMaulgarMainTankAttackMaulgarAction::Execute(Event /*event*/) { Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar"); if (!maulgar) @@ -44,7 +44,7 @@ bool HighKingMaulgarMainTankAttackMaulgarAction::Execute(Event event) } // First offtank on Olm -bool HighKingMaulgarFirstAssistTankAttackOlmAction::Execute(Event event) +bool HighKingMaulgarFirstAssistTankAttackOlmAction::Execute(Event /*event*/) { Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner"); if (!olm) @@ -79,7 +79,7 @@ bool HighKingMaulgarFirstAssistTankAttackOlmAction::Execute(Event event) } // Second offtank on Blindeye -bool HighKingMaulgarSecondAssistTankAttackBlindeyeAction::Execute(Event event) +bool HighKingMaulgarSecondAssistTankAttackBlindeyeAction::Execute(Event /*event*/) { Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer"); if (!blindeye) @@ -113,7 +113,7 @@ bool HighKingMaulgarSecondAssistTankAttackBlindeyeAction::Execute(Event event) } // Mage with highest max HP on Krosh -bool HighKingMaulgarMageTankAttackKroshAction::Execute(Event event) +bool HighKingMaulgarMageTankAttackKroshAction::Execute(Event /*event*/) { Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand"); if (!krosh) @@ -166,7 +166,7 @@ bool HighKingMaulgarMageTankAttackKroshAction::Execute(Event event) } // Moonkin with highest max HP on Kiggler -bool HighKingMaulgarMoonkinTankAttackKigglerAction::Execute(Event event) +bool HighKingMaulgarMoonkinTankAttackKigglerAction::Execute(Event /*event*/) { Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed"); if (!kiggler) @@ -188,7 +188,7 @@ bool HighKingMaulgarMoonkinTankAttackKigglerAction::Execute(Event event) return false; } -bool HighKingMaulgarAssignDPSPriorityAction::Execute(Event event) +bool HighKingMaulgarAssignDPSPriorityAction::Execute(Event /*event*/) { // Target priority 1: Blindeye Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer"); @@ -297,7 +297,7 @@ bool HighKingMaulgarAssignDPSPriorityAction::Execute(Event event) } // Avoid Whirlwind and Blast Wave and generally try to stay near the center of the room -bool HighKingMaulgarHealerFindSafePositionAction::Execute(Event event) +bool HighKingMaulgarHealerFindSafePositionAction::Execute(Event /*event*/) { const Position& center = MAULGAR_ROOM_CENTER; const float maxDistanceFromCenter = 50.0f; @@ -331,7 +331,7 @@ bool HighKingMaulgarHealerFindSafePositionAction::Execute(Event event) } // Run away from Maulgar during Whirlwind (logic for after all other ogres are dead) -bool HighKingMaulgarRunAwayFromWhirlwindAction::Execute(Event event) +bool HighKingMaulgarRunAwayFromWhirlwindAction::Execute(Event /*event*/) { Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar"); if (!maulgar) @@ -366,7 +366,7 @@ bool HighKingMaulgarRunAwayFromWhirlwindAction::Execute(Event event) return false; } -bool HighKingMaulgarBanishFelstalkerAction::Execute(Event event) +bool HighKingMaulgarBanishFelstalkerAction::Execute(Event /*event*/) { Group* group = bot->GetGroup(); if (!group) @@ -411,7 +411,7 @@ bool HighKingMaulgarBanishFelstalkerAction::Execute(Event event) // Hunter 1: Misdirect Olm to first offtank and have pet attack Blindeye // Hunter 2: Misdirect Blindeye to second offtank -bool HighKingMaulgarMisdirectOlmAndBlindeyeAction::Execute(Event event) +bool HighKingMaulgarMisdirectOlmAndBlindeyeAction::Execute(Event /*event*/) { Group* group = bot->GetGroup(); if (!group) @@ -491,7 +491,7 @@ bool HighKingMaulgarMisdirectOlmAndBlindeyeAction::Execute(Event event) // Gruul the Dragonkiller Actions // Position in center of the room -bool GruulTheDragonkillerTanksPositionBossAction::Execute(Event event) +bool GruulTheDragonkillerTanksPositionBossAction::Execute(Event /*event*/) { Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller"); if (!gruul) @@ -525,7 +525,7 @@ bool GruulTheDragonkillerTanksPositionBossAction::Execute(Event event) // Ranged will take initial positions around the middle of the room, 25-40 yards from center // Ranged should spread out 10 yards from each other -bool GruulTheDragonkillerSpreadRangedAction::Execute(Event event) +bool GruulTheDragonkillerSpreadRangedAction::Execute(Event /*event*/) { Group* group = bot->GetGroup(); if (!group) @@ -618,7 +618,7 @@ bool GruulTheDragonkillerSpreadRangedAction::Execute(Event event) } // Try to get away from other group members when Ground Slam is cast -bool GruulTheDragonkillerShatterSpreadAction::Execute(Event event) +bool GruulTheDragonkillerShatterSpreadAction::Execute(Event /*event*/) { Group* group = bot->GetGroup(); if (!group) diff --git a/src/Ai/Raid/Icecrown/Action/RaidIccActions.cpp b/src/Ai/Raid/Icecrown/Action/RaidIccActions.cpp index c3e3513e2a..95f5bc3bf6 100644 --- a/src/Ai/Raid/Icecrown/Action/RaidIccActions.cpp +++ b/src/Ai/Raid/Icecrown/Action/RaidIccActions.cpp @@ -1,19 +1,16 @@ #include "RaidIccActions.h" #include "NearestNpcsValue.h" #include "ObjectAccessor.h" -#include "RaidIccStrategy.h" #include "Playerbots.h" -#include "Timer.h" #include "Vehicle.h" #include "RtiValue.h" #include "GenericSpellActions.h" #include "GenericActions.h" -#include #include "RaidIccTriggers.h" #include "Multiplier.h" // Lord Marrowgwar -bool IccLmTankPositionAction::Execute(Event event) +bool IccLmTankPositionAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "lord marrowgar"); if (!boss) @@ -73,7 +70,7 @@ bool IccLmTankPositionAction::MoveTowardPosition(const Position& position, float MovementPriority::MOVEMENT_COMBAT); } -bool IccSpikeAction::Execute(Event event) +bool IccSpikeAction::Execute(Event /*event*/) { // If we're impaled, we can't do anything if (botAI->GetAura("Impaled", bot)) @@ -183,7 +180,7 @@ void IccSpikeAction::UpdateRaidTargetIcon(Unit* target) } // Lady Deathwhisper -bool IccDarkReckoningAction::Execute(Event event) +bool IccDarkReckoningAction::Execute(Event /*event*/) { constexpr float SAFE_DISTANCE_THRESHOLD = 2.0f; @@ -201,7 +198,7 @@ bool IccDarkReckoningAction::Execute(Event event) return false; } -bool IccRangedPositionLadyDeathwhisperAction::Execute(Event event) +bool IccRangedPositionLadyDeathwhisperAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "lady deathwhisper"); if (!boss) @@ -301,7 +298,7 @@ bool IccRangedPositionLadyDeathwhisperAction::MaintainRangedSpacing() return false; // Everyone is properly spaced } -bool IccAddsLadyDeathwhisperAction::Execute(Event event) +bool IccAddsLadyDeathwhisperAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "lady deathwhisper"); if (!boss) @@ -419,7 +416,7 @@ void IccAddsLadyDeathwhisperAction::UpdateRaidTargetIcon(Unit* target) } } -bool IccShadeLadyDeathwhisperAction::Execute(Event event) +bool IccShadeLadyDeathwhisperAction::Execute(Event /*event*/) { static constexpr uint32 VENGEFUL_SHADE_ID = NPC_SHADE; static constexpr float SAFE_DISTANCE = 12.0f; @@ -473,7 +470,7 @@ bool IccShadeLadyDeathwhisperAction::Execute(Event event) return false; } -bool IccRottingFrostGiantTankPositionAction::Execute(Event event) +bool IccRottingFrostGiantTankPositionAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "rotting frost giant"); if (!boss) @@ -696,7 +693,7 @@ bool IccRottingFrostGiantTankPositionAction::Execute(Event event) } //Gunship -bool IccCannonFireAction::Execute(Event event) +bool IccCannonFireAction::Execute(Event /*event*/) { Unit* vehicleBase = bot->GetVehicleBase(); Vehicle* vehicle = bot->GetVehicle(); @@ -755,7 +752,7 @@ bool IccCannonFireAction::TryCastCannonSpell(uint32 spellId, Unit* target, Unit* return false; } -bool IccGunshipEnterCannonAction::Execute(Event event) +bool IccGunshipEnterCannonAction::Execute(Event /*event*/) { // Do not switch vehicles if already in one if (bot->GetVehicle()) @@ -855,7 +852,7 @@ bool IccGunshipEnterCannonAction::EnterVehicle(Unit* vehicleBase, bool moveIfFar return true; } -bool IccGunshipTeleportAllyAction::Execute(Event event) +bool IccGunshipTeleportAllyAction::Execute(Event /*event*/) { static constexpr float MAX_WAITING_DISTANCE = 45.0f; static constexpr float MAX_ATTACK_DISTANCE = 15.0f; @@ -922,7 +919,7 @@ void IccGunshipTeleportAllyAction::UpdateBossSkullIcon(Unit* boss, uint8_t SKULL } } -bool IccGunshipTeleportHordeAction::Execute(Event event) +bool IccGunshipTeleportHordeAction::Execute(Event /*event*/) { static constexpr float MAX_WAITING_DISTANCE = 45.0f; static constexpr float MAX_ATTACK_DISTANCE = 15.0f; @@ -990,7 +987,7 @@ void IccGunshipTeleportHordeAction::UpdateBossSkullIcon(Unit* boss, uint8_t SKUL } //DBS -bool IccDbsTankPositionAction::Execute(Event event) +bool IccDbsTankPositionAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "deathbringer saurfang"); if (!boss) @@ -1231,7 +1228,7 @@ bool IccDbsTankPositionAction::PositionInRangedFormation() return false; } -bool IccAddsDbsAction::Execute(Event event) +bool IccAddsDbsAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "deathbringer saurfang"); if (!boss) @@ -1295,7 +1292,7 @@ void IccAddsDbsAction::UpdateSkullMarker(Unit* priorityTarget) } // Festergut -bool IccFestergutGroupPositionAction::Execute(Event event) +bool IccFestergutGroupPositionAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "festergut"); if (!boss) @@ -1511,7 +1508,7 @@ int IccFestergutGroupPositionAction::CalculatePositionIndex(Group* group) return -1; } -bool IccFestergutSporeAction::Execute(Event event) +bool IccFestergutSporeAction::Execute(Event /*event*/) { constexpr float POSITION_TOLERANCE = 4.0f; constexpr float SPREAD_RADIUS = 2.0f; @@ -1626,7 +1623,7 @@ bool IccFestergutSporeAction::CheckMainTankSpore() } // Rotface -bool IccRotfaceTankPositionAction::Execute(Event event) +bool IccRotfaceTankPositionAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "rotface"); if (!boss) @@ -1702,7 +1699,7 @@ bool IccRotfaceTankPositionAction::HandleAssistTankPositioning(Unit* boss) return HandleBigOozePositioning(boss); } -bool IccRotfaceTankPositionAction::HandleBigOozePositioning(Unit* boss) +bool IccRotfaceTankPositionAction::HandleBigOozePositioning(Unit*) { // Find all big oozes GuidVector bigOozes = AI_VALUE(GuidVector, "nearest hostile npcs"); @@ -1826,7 +1823,7 @@ bool IccRotfaceTankPositionAction::HandleBigOozePositioning(Unit* boss) return false; } -bool IccRotfaceGroupPositionAction::Execute(Event event) +bool IccRotfaceGroupPositionAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "rotface"); if (!boss) @@ -1893,7 +1890,7 @@ bool IccRotfaceGroupPositionAction::HandlePuddleAvoidance(Unit* boss) return false; } -bool IccRotfaceGroupPositionAction::MoveAwayFromPuddle(Unit* boss, Unit* puddle, float puddleDistance) +bool IccRotfaceGroupPositionAction::MoveAwayFromPuddle(Unit* boss, Unit* puddle, float) { if (!boss || !puddle) return false; @@ -2177,7 +2174,7 @@ bool IccRotfaceGroupPositionAction::FindAndMoveFromClosestMember(Unit* boss, Uni return false; // Everyone is properly spaced } -bool IccRotfaceMoveAwayFromExplosionAction::Execute(Event event) +bool IccRotfaceMoveAwayFromExplosionAction::Execute(Event /*event*/) { // Skip if main tank or ooze flood if (botAI->IsMainTank(bot)) @@ -2260,7 +2257,7 @@ bool IccRotfaceMoveAwayFromExplosionAction::MoveToRandomSafeLocation() } // Proffesor Putricide -bool IccPutricideGrowingOozePuddleAction::Execute(Event event) +bool IccPutricideGrowingOozePuddleAction::Execute(Event /*event*/) { Unit* closestPuddle = FindClosestThreateningPuddle(); if (!closestPuddle) @@ -2418,7 +2415,7 @@ bool IccPutricideGrowingOozePuddleAction::IsPositionTooCloseToOtherPuddles(float return false; } -bool IccPutricideVolatileOozeAction::Execute(Event event) +bool IccPutricideVolatileOozeAction::Execute(Event /*event*/) { static const float STACK_DISTANCE = 7.0f; @@ -2531,7 +2528,7 @@ Unit* IccPutricideVolatileOozeAction::FindAuraTarget() return nullptr; } -bool IccPutricideGasCloudAction::Execute(Event event) +bool IccPutricideGasCloudAction::Execute(Event /*event*/) { Unit* gasCloud = AI_VALUE2(Unit*, "find target", "gas cloud"); if (!gasCloud) @@ -2808,7 +2805,6 @@ Position IccPutricideGasCloudAction::CalculateEmergencyPosition(const Position& Position bestPos = Position(botPos.GetPositionX() + dx * 15.0f, botPos.GetPositionY() + dy * 15.0f, botPos.GetPositionZ()); float bestFreedom = 0.0f; - static const float MOVEMENT_INCREMENT = 5.0f; // Fixed movement increment // Try fewer directions for emergency but still avoid corners for (int i = 0; i < 8; i++) @@ -2924,9 +2920,7 @@ bool IccPutricideGasCloudAction::HandleGroupAuraSituation(Unit* gasCloud) gasCloud->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT); } else - { return Attack(gasCloud); - } } } @@ -2944,7 +2938,7 @@ bool IccPutricideGasCloudAction::GroupHasGaseousBloat(Group* group) return false; } -bool IccPutricideAvoidMalleableGooAction::Execute(Event event) +bool IccPutricideAvoidMalleableGooAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "professor putricide"); if (!boss) @@ -2966,7 +2960,7 @@ bool IccPutricideAvoidMalleableGooAction::Execute(Event event) return HandleBossPositioning(boss); } -bool IccPutricideAvoidMalleableGooAction::HandleTankPositioning(Unit* boss) +bool IccPutricideAvoidMalleableGooAction::HandleTankPositioning(Unit*) { if (!botAI->IsTank(bot)) return false; @@ -3231,7 +3225,7 @@ Position IccPutricideAvoidMalleableGooAction::CalculateIncrementalMove(const Pos } // BPC -bool IccBpcKelesethTankAction::Execute(Event event) +bool IccBpcKelesethTankAction::Execute(Event /*event*/) { if (!botAI->IsAssistTank(bot)) return false; @@ -3312,7 +3306,7 @@ bool IccBpcKelesethTankAction::Execute(Event event) return false; } -bool IccBpcMainTankAction::Execute(Event event) +bool IccBpcMainTankAction::Execute(Event /*event*/) { // Main tank specific behavior (higher priority) if (botAI->IsMainTank(bot)) @@ -3429,7 +3423,7 @@ void IccBpcMainTankAction::MarkEmpoweredPrince() } } -bool IccBpcEmpoweredVortexAction::Execute(Event event) +bool IccBpcEmpoweredVortexAction::Execute(Event /*event*/) { Unit* valanar = AI_VALUE2(Unit*, "find target", "prince valanar"); if (!valanar) @@ -3614,7 +3608,7 @@ bool IccBpcEmpoweredVortexAction::HandleEmpoweredVortexSpread() return false; // Everyone is properly spaced } -bool IccBpcKineticBombAction::Execute(Event event) +bool IccBpcKineticBombAction::Execute(Event /*event*/) { // Early exit if not ranged DPS if (!botAI->IsRangedDps(bot)) @@ -3760,7 +3754,7 @@ bool IccBpcKineticBombAction::IsBombAlreadyHandled(Unit* bomb, Group* group) return false; } -bool IccBpcBallOfFlameAction::Execute(Event event) +bool IccBpcBallOfFlameAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "prince taldaram"); if (!boss) @@ -3841,7 +3835,7 @@ bool IccBpcBallOfFlameAction::Execute(Event event) } // Blood Queen Lana'thel -bool IccBqlGroupPositionAction::Execute(Event event) +bool IccBqlGroupPositionAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "blood-queen lana'thel"); if (!boss) @@ -4603,7 +4597,7 @@ bool IccBqlGroupPositionAction::HandleGroupPosition(Unit* boss, Aura* frenzyAura return false; } -bool IccBqlPactOfDarkfallenAction::Execute(Event event) +bool IccBqlPactOfDarkfallenAction::Execute(Event /*event*/) { // Check if bot has Pact of the Darkfallen if (!botAI->GetAura("Pact of the Darkfallen", bot)) @@ -4722,7 +4716,7 @@ bool IccBqlPactOfDarkfallenAction::MoveToTargetPosition(const Position& targetPo return false; } -bool IccBqlVampiricBiteAction::Execute(Event event) +bool IccBqlVampiricBiteAction::Execute(Event /*event*/) { // Only act when bot has Frenzied Bloodthirst if (!botAI->GetAura("Frenzied Bloodthirst", bot)) @@ -4859,7 +4853,7 @@ bool IccBqlVampiricBiteAction::CastVampiricBite(Player* target) } // Sister Svalna -bool IccValkyreSpearAction::Execute(Event event) +bool IccValkyreSpearAction::Execute(Event /*event*/) { // Find the nearest spear Creature* spear = bot->FindNearestCreature(NPC_SPEAR, 100.0f); @@ -4885,7 +4879,7 @@ bool IccValkyreSpearAction::Execute(Event event) return false; } -bool IccSisterSvalnaAction::Execute(Event event) +bool IccSisterSvalnaAction::Execute(Event /*event*/) { Unit* svalna = AI_VALUE2(Unit*, "find target", "sister svalna"); if (!svalna || !svalna->HasAura(SPELL_AETHER_SHIELD)) // Check for Aether Shield aura @@ -4910,7 +4904,7 @@ bool IccSisterSvalnaAction::Execute(Event event) } // VDW -bool IccValithriaGroupAction::Execute(Event event) +bool IccValithriaGroupAction::Execute(Event /*event*/) { // Helper lambda to find nearest creature of given entries auto findNearestCreature = [this](std::initializer_list entries, float range) -> Creature* @@ -5320,7 +5314,7 @@ bool IccValithriaGroupAction::Handle10ManGroupLogic() return false; } -bool IccValithriaPortalAction::Execute(Event event) +bool IccValithriaPortalAction::Execute(Event /*event*/) { // Only healers should take portals, and not if already inside if (!botAI->IsHeal(bot) || bot->HasAura(SPELL_DREAM_STATE)) @@ -5453,7 +5447,7 @@ bool IccValithriaPortalAction::Execute(Event event) return false; } -bool IccValithriaHealAction::Execute(Event event) +bool IccValithriaHealAction::Execute(Event /*event*/) { // Early validation checks if (!botAI->IsHeal(bot) || bot->GetHealthPct() < 50.0f) @@ -5543,7 +5537,7 @@ bool IccValithriaHealAction::Execute(Event event) return false; } -bool IccValithriaDreamCloudAction::Execute(Event event) +bool IccValithriaDreamCloudAction::Execute(Event /*event*/) { // Only execute if we're in dream state if (!bot->HasAura(SPELL_DREAM_STATE)) @@ -5826,7 +5820,7 @@ bool IccValithriaDreamCloudAction::Execute(Event event) } // Sindragosa -bool IccSindragosaGroupPositionAction::Execute(Event event) +bool IccSindragosaGroupPositionAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa"); if (!boss || boss->HasUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY)) @@ -6085,7 +6079,7 @@ bool IccSindragosaGroupPositionAction::MoveIncrementallyToPosition(const Positio MovementPriority::MOVEMENT_COMBAT); } -bool IccSindragosaTankSwapPositionAction::Execute(Event event) +bool IccSindragosaTankSwapPositionAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa"); if (!boss) @@ -6109,7 +6103,7 @@ bool IccSindragosaTankSwapPositionAction::Execute(Event event) return false; } -bool IccSindragosaFrostBeaconAction::Execute(Event event) +bool IccSindragosaFrostBeaconAction::Execute(Event /*event*/) { const Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa"); if (!boss) @@ -6343,7 +6337,7 @@ bool IccSindragosaFrostBeaconAction::IsBossFlying(const Unit* boss) ICC_SINDRAGOSA_FLYING_POSITION.GetPositionY()) < 30.0f; } -bool IccSindragosaBlisteringColdAction::Execute(Event event) +bool IccSindragosaBlisteringColdAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa"); if (!boss) @@ -6391,7 +6385,7 @@ bool IccSindragosaBlisteringColdAction::Execute(Event event) return false; } -bool IccSindragosaUnchainedMagicAction::Execute(Event event) +bool IccSindragosaUnchainedMagicAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa"); if (!boss) @@ -6413,7 +6407,7 @@ bool IccSindragosaUnchainedMagicAction::Execute(Event event) return false; } -bool IccSindragosaChilledToTheBoneAction::Execute(Event event) +bool IccSindragosaChilledToTheBoneAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa"); if (!boss) @@ -6436,7 +6430,7 @@ bool IccSindragosaChilledToTheBoneAction::Execute(Event event) return false; } -bool IccSindragosaMysticBuffetAction::Execute(Event event) +bool IccSindragosaMysticBuffetAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa"); if (!boss || !bot || !bot->IsAlive()) @@ -6519,7 +6513,7 @@ bool IccSindragosaMysticBuffetAction::Execute(Event event) return false; } -bool IccSindragosaFrostBombAction::Execute(Event event) +bool IccSindragosaFrostBombAction::Execute(Event /*event*/) { if (!bot || !bot->IsAlive() || bot->HasAura(SPELL_ICE_TOMB)) // Skip if dead or in Ice Tomb return false; @@ -6804,7 +6798,7 @@ bool IccSindragosaFrostBombAction::Execute(Event event) } // The Lich King -bool IccLichKingShadowTrapAction::Execute(Event event) +bool IccLichKingShadowTrapAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king"); if (!boss || !botAI->IsTank(bot)) @@ -6931,7 +6925,7 @@ bool IccLichKingShadowTrapAction::Execute(Event event) return false; } -bool IccLichKingNecroticPlagueAction::Execute(Event event) +bool IccLichKingNecroticPlagueAction::Execute(Event /*event*/) { bool hasPlague = botAI->HasAura("Necrotic Plague", bot); // Only execute if we have the plague @@ -6983,7 +6977,7 @@ bool IccLichKingNecroticPlagueAction::Execute(Event event) return false; } -bool IccLichKingWinterAction::Execute(Event event) +bool IccLichKingWinterAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king"); if (!boss) @@ -7785,7 +7779,7 @@ void IccLichKingWinterAction::HandleAssistTankAddManagement(Unit* boss, const Po } } -bool IccLichKingAddsAction::Execute(Event event) +bool IccLichKingAddsAction::Execute(Event /*event*/) { if (bot->HasAura(SPELL_HARVEST_SOUL_VALKYR)) // Don't process actions if bot is picked up by Val'kyr return false; diff --git a/src/Ai/Raid/Icecrown/Multiplier/RaidIccMultipliers.cpp b/src/Ai/Raid/Icecrown/Multiplier/RaidIccMultipliers.cpp index fc02d56bea..c6a6fdee56 100644 --- a/src/Ai/Raid/Icecrown/Multiplier/RaidIccMultipliers.cpp +++ b/src/Ai/Raid/Icecrown/Multiplier/RaidIccMultipliers.cpp @@ -15,7 +15,6 @@ #include "RaidIccActions.h" #include "ReachTargetActions.h" #include "RogueActions.h" -#include "ScriptedCreature.h" #include "ShamanActions.h" #include "UseMeetingStoneAction.h" #include "WarriorActions.h" diff --git a/src/Ai/Raid/Icecrown/Trigger/RaidIccTriggers.cpp b/src/Ai/Raid/Icecrown/Trigger/RaidIccTriggers.cpp index b34fe003fc..75989ba1c4 100644 --- a/src/Ai/Raid/Icecrown/Trigger/RaidIccTriggers.cpp +++ b/src/Ai/Raid/Icecrown/Trigger/RaidIccTriggers.cpp @@ -4,14 +4,9 @@ #include "PlayerbotAIConfig.h" #include "ObjectAccessor.h" #include "GenericTriggers.h" -#include "DungeonStrategyUtils.h" -#include "EventMap.h" #include "Playerbots.h" -#include "ScriptedCreature.h" #include "Trigger.h" -#include "CellImpl.h" #include "GridNotifiers.h" -#include "GridNotifiersImpl.h" #include "Vehicle.h" //Lord Marrogwar diff --git a/src/Ai/Raid/Karazhan/Action/RaidKarazhanActions.cpp b/src/Ai/Raid/Karazhan/Action/RaidKarazhanActions.cpp index 4d40a9eedb..b04e09d0d5 100644 --- a/src/Ai/Raid/Karazhan/Action/RaidKarazhanActions.cpp +++ b/src/Ai/Raid/Karazhan/Action/RaidKarazhanActions.cpp @@ -10,7 +10,7 @@ using namespace KarazhanHelpers; // Mana Warps blow up when they die for massive raid damage // But they cannot cast the ability if they are stunned -bool ManaWarpStunCreatureBeforeWarpBreachAction::Execute(Event event) +bool ManaWarpStunCreatureBeforeWarpBreachAction::Execute(Event /*event*/) { Unit* manaWarp = GetFirstAliveUnitByEntry(botAI, NPC_MANA_WARP); if (!manaWarp) @@ -40,7 +40,7 @@ bool ManaWarpStunCreatureBeforeWarpBreachAction::Execute(Event event) // Attumen the Huntsman // Prioritize Midnight until Attumen is mounted -bool AttumenTheHuntsmanMarkTargetAction::Execute(Event event) +bool AttumenTheHuntsmanMarkTargetAction::Execute(Event /*event*/) { Unit* attumenMounted = GetFirstAliveUnitByEntry(botAI, NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED); if (attumenMounted) @@ -77,7 +77,7 @@ bool AttumenTheHuntsmanMarkTargetAction::Execute(Event event) } // Off tank should move Attumen out of the way so he doesn't cleave bots -bool AttumenTheHuntsmanSplitBossesAction::Execute(Event event) +bool AttumenTheHuntsmanSplitBossesAction::Execute(Event /*event*/) { Unit* midnight = AI_VALUE2(Unit*, "find target", "midnight"); if (!midnight) @@ -105,7 +105,7 @@ bool AttumenTheHuntsmanSplitBossesAction::Execute(Event event) } // Stack behind mounted Attumen (inside minimum range of Berserker Charge) -bool AttumenTheHuntsmanStackBehindAction::Execute(Event event) +bool AttumenTheHuntsmanStackBehindAction::Execute(Event /*event*/) { Unit* attumenMounted = GetFirstAliveUnitByEntry(botAI, NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED); if (!attumenMounted) @@ -126,7 +126,7 @@ bool AttumenTheHuntsmanStackBehindAction::Execute(Event event) } // Reset timer for bots to pause DPS when Attumen mounts Midnight -bool AttumenTheHuntsmanManageDpsTimerAction::Execute(Event event) +bool AttumenTheHuntsmanManageDpsTimerAction::Execute(Event /*event*/) { Unit* midnight = AI_VALUE2(Unit*, "find target", "midnight"); if (!midnight) @@ -153,7 +153,7 @@ bool AttumenTheHuntsmanManageDpsTimerAction::Execute(Event event) // Moroes -bool MoroesMainTankAttackBossAction::Execute(Event event) +bool MoroesMainTankAttackBossAction::Execute(Event /*event*/) { Unit* moroes = AI_VALUE2(Unit*, "find target", "moroes"); if (!moroes) @@ -169,7 +169,7 @@ bool MoroesMainTankAttackBossAction::Execute(Event event) } // Mark targets with skull in the recommended kill order -bool MoroesMarkTargetAction::Execute(Event event) +bool MoroesMarkTargetAction::Execute(Event /*event*/) { Unit* dorothea = AI_VALUE2(Unit*, "find target", "baroness dorothea millstipe"); Unit* catriona = AI_VALUE2(Unit*, "find target", "lady catriona von'indi"); @@ -194,7 +194,7 @@ bool MoroesMarkTargetAction::Execute(Event event) // Tank the boss in the center of the room // Move to healers after Repentenace to break the stun -bool MaidenOfVirtueMoveBossToHealerAction::Execute(Event event) +bool MaidenOfVirtueMoveBossToHealerAction::Execute(Event /*event*/) { Unit* maiden = AI_VALUE2(Unit*, "find target", "maiden of virtue"); if (!maiden) @@ -248,7 +248,7 @@ bool MaidenOfVirtueMoveBossToHealerAction::Execute(Event event) } // Spread out ranged DPS between the pillars -bool MaidenOfVirtuePositionRangedAction::Execute(Event event) +bool MaidenOfVirtuePositionRangedAction::Execute(Event /*event*/) { const uint8 maxIndex = 7; uint8 index = 0; @@ -288,7 +288,7 @@ bool MaidenOfVirtuePositionRangedAction::Execute(Event event) // The Big Bad Wolf // Tank the boss at the front left corner of the stage -bool BigBadWolfPositionBossAction::Execute(Event event) +bool BigBadWolfPositionBossAction::Execute(Event /*event*/) { Unit* wolf = AI_VALUE2(Unit*, "find target", "the big bad wolf"); if (!wolf) @@ -319,7 +319,7 @@ bool BigBadWolfPositionBossAction::Execute(Event event) } // Run away, little girl, run away -bool BigBadWolfRunAwayFromBossAction::Execute(Event event) +bool BigBadWolfRunAwayFromBossAction::Execute(Event /*event*/) { const ObjectGuid botGuid = bot->GetGUID(); uint8 index = bigBadWolfRunIndex.count(botGuid) ? bigBadWolfRunIndex[botGuid] : 0; @@ -342,7 +342,7 @@ bool BigBadWolfRunAwayFromBossAction::Execute(Event event) // Romulo and Julianne // Keep the couple within 10% HP of each other -bool RomuloAndJulianneMarkTargetAction::Execute(Event event) +bool RomuloAndJulianneMarkTargetAction::Execute(Event /*event*/) { Unit* romulo = AI_VALUE2(Unit*, "find target", "romulo"); if (!romulo) @@ -371,7 +371,7 @@ bool RomuloAndJulianneMarkTargetAction::Execute(Event event) // The Wizard of Oz // Mark targets with skull in the recommended kill order -bool WizardOfOzMarkTargetAction::Execute(Event event) +bool WizardOfOzMarkTargetAction::Execute(Event /*event*/) { Unit* dorothee = AI_VALUE2(Unit*, "find target", "dorothee"); Unit* tito = AI_VALUE2(Unit*, "find target", "tito"); @@ -388,7 +388,7 @@ bool WizardOfOzMarkTargetAction::Execute(Event event) } // Mages spam Scorch on Strawman to disorient him -bool WizardOfOzScorchStrawmanAction::Execute(Event event) +bool WizardOfOzScorchStrawmanAction::Execute(Event /*event*/) { Unit* strawman = AI_VALUE2(Unit*, "find target", "strawman"); if (strawman && botAI->CanCastSpell("scorch", strawman)) @@ -400,7 +400,7 @@ bool WizardOfOzScorchStrawmanAction::Execute(Event event) // The Curator // Prioritize destroying Astral Flares -bool TheCuratorMarkAstralFlareAction::Execute(Event event) +bool TheCuratorMarkAstralFlareAction::Execute(Event /*event*/) { Unit* flare = AI_VALUE2(Unit*, "find target", "astral flare"); if (!flare) @@ -416,7 +416,7 @@ bool TheCuratorMarkAstralFlareAction::Execute(Event event) // Tank the boss in the center of the hallway near the Guardian's Library // Main tank and off tank will attack the boss; others will focus on Astral Flares -bool TheCuratorPositionBossAction::Execute(Event event) +bool TheCuratorPositionBossAction::Execute(Event /*event*/) { Unit* curator = AI_VALUE2(Unit*, "find target", "the curator"); if (!curator) @@ -450,7 +450,7 @@ bool TheCuratorPositionBossAction::Execute(Event event) } // Spread out ranged DPS to avoid Arcing Sear damage -bool TheCuratorSpreadRangedAction::Execute(Event event) +bool TheCuratorSpreadRangedAction::Execute(Event /*event*/) { const float minDistance = 5.0f; Unit* nearestPlayer = GetNearestPlayerInRadius(bot, minDistance); @@ -468,7 +468,7 @@ bool TheCuratorSpreadRangedAction::Execute(Event event) // Terestian Illhoof // Prioritize (1) Demon Chains, (2) Kil'rek, (3) Illhoof -bool TerestianIllhoofMarkTargetAction::Execute(Event event) +bool TerestianIllhoofMarkTargetAction::Execute(Event /*event*/) { Unit* demonChains = GetFirstAliveUnitByEntry(botAI, NPC_DEMON_CHAINS); Unit* kilrek = GetFirstAliveUnitByEntry(botAI, NPC_KILREK); @@ -484,7 +484,7 @@ bool TerestianIllhoofMarkTargetAction::Execute(Event event) // Shade of Aran // Run to the edge of the room to avoid Arcane Explosion -bool ShadeOfAranRunAwayFromArcaneExplosionAction::Execute(Event event) +bool ShadeOfAranRunAwayFromArcaneExplosionAction::Execute(Event /*event*/) { Unit* aran = AI_VALUE2(Unit*, "find target", "shade of aran"); if (!aran) @@ -503,7 +503,7 @@ bool ShadeOfAranRunAwayFromArcaneExplosionAction::Execute(Event event) } // I will not move when Flame Wreath is cast or the raid blows up -bool ShadeOfAranStopMovingDuringFlameWreathAction::Execute(Event event) +bool ShadeOfAranStopMovingDuringFlameWreathAction::Execute(Event /*event*/) { AI_VALUE(LastMovement&, "last movement").Set(nullptr); @@ -518,7 +518,7 @@ bool ShadeOfAranStopMovingDuringFlameWreathAction::Execute(Event event) } // Mark Conjured Elementals with skull so DPS can burn them down -bool ShadeOfAranMarkConjuredElementalAction::Execute(Event event) +bool ShadeOfAranMarkConjuredElementalAction::Execute(Event /*event*/) { Unit* elemental = GetFirstAliveUnitByEntry(botAI, NPC_CONJURED_ELEMENTAL); @@ -530,7 +530,7 @@ bool ShadeOfAranMarkConjuredElementalAction::Execute(Event event) // Don't get closer than 11 yards to Aran to avoid counterspell // Don't get farther than 15 yards from Aran to avoid getting stuck in alcoves -bool ShadeOfAranRangedMaintainDistanceAction::Execute(Event event) +bool ShadeOfAranRangedMaintainDistanceAction::Execute(Event /*event*/) { Unit* aran = AI_VALUE2(Unit*, "find target", "shade of aran"); if (!aran) @@ -594,7 +594,7 @@ bool ShadeOfAranRangedMaintainDistanceAction::Execute(Event event) // One tank bot per phase will dance in and out of the red beam (5 seconds in, 5 seconds out) // Tank bots will ignore void zones--their positioning is too important to risk losing beam control -bool NetherspiteBlockRedBeamAction::Execute(Event event) +bool NetherspiteBlockRedBeamAction::Execute(Event /*event*/) { Unit* netherspite = AI_VALUE2(Unit*, "find target", "netherspite"); if (!netherspite) @@ -681,7 +681,7 @@ Position NetherspiteBlockRedBeamAction::GetPositionOnBeam(Unit* netherspite, Uni // Two non-Rogue/Warrior DPS bots will block the blue beam for each phase (swap at 25 debuff stacks) // When avoiding void zones, blocking bots will move along the beam to continue blocking -bool NetherspiteBlockBlueBeamAction::Execute(Event event) +bool NetherspiteBlockBlueBeamAction::Execute(Event /*event*/) { Unit* netherspite = AI_VALUE2(Unit*, "find target", "netherspite"); if (!netherspite) @@ -774,7 +774,7 @@ bool NetherspiteBlockBlueBeamAction::Execute(Event event) // Two healer bots will block the green beam for each phase (swap at 25 debuff stacks) // OR one rogue or DPS warrior bot will block the green beam for an entire phase (if they begin the phase as the blocker) // When avoiding void zones, blocking bots will move along the beam to continue blocking -bool NetherspiteBlockGreenBeamAction::Execute(Event event) +bool NetherspiteBlockGreenBeamAction::Execute(Event /*event*/) { Unit* netherspite = AI_VALUE2(Unit*, "find target", "netherspite"); if (!netherspite) @@ -864,7 +864,7 @@ bool NetherspiteBlockGreenBeamAction::Execute(Event event) } // All bots not currently blocking a beam will avoid beams and void zones -bool NetherspiteAvoidBeamAndVoidZoneAction::Execute(Event event) +bool NetherspiteAvoidBeamAndVoidZoneAction::Execute(Event /*event*/) { Unit* netherspite = AI_VALUE2(Unit*, "find target", "netherspite"); if (!netherspite) @@ -980,7 +980,7 @@ bool NetherspiteAvoidBeamAndVoidZoneAction::IsAwayFromBeams( return true; } -bool NetherspiteBanishPhaseAvoidVoidZoneAction::Execute(Event event) +bool NetherspiteBanishPhaseAvoidVoidZoneAction::Execute(Event /*event*/) { std::vector voidZones = GetAllVoidZones(botAI, bot); @@ -993,7 +993,7 @@ bool NetherspiteBanishPhaseAvoidVoidZoneAction::Execute(Event event) return false; } -bool NetherspiteManageTimersAndTrackersAction::Execute(Event event) +bool NetherspiteManageTimersAndTrackersAction::Execute(Event /*event*/) { Unit* netherspite = AI_VALUE2(Unit*, "find target", "netherspite"); if (!netherspite) @@ -1045,7 +1045,7 @@ bool NetherspiteManageTimersAndTrackersAction::Execute(Event event) // Move away from the boss to avoid Shadow Nova when Enfeebled // Do not cross within Infernal Hellfire radius while doing so -bool PrinceMalchezaarEnfeebledAvoidHazardAction::Execute(Event event) +bool PrinceMalchezaarEnfeebledAvoidHazardAction::Execute(Event /*event*/) { Unit* malchezaar = AI_VALUE2(Unit*, "find target", "prince malchezaar"); if (!malchezaar) @@ -1057,7 +1057,6 @@ bool PrinceMalchezaarEnfeebledAvoidHazardAction::Execute(Event event) const float minSafeBossDistanceSq = minSafeBossDistance * minSafeBossDistance; const float maxSafeBossDistance = 60.0f; const float safeInfernalDistance = 23.0f; - const float safeInfernalDistanceSq = safeInfernalDistance * safeInfernalDistance; const float distIncrement = 0.5f; const uint8 numAngles = 64; @@ -1122,7 +1121,7 @@ bool PrinceMalchezaarEnfeebledAvoidHazardAction::Execute(Event event) // Move away from infernals while staying within range of the boss // Prioritize finding a safe path to the new location, but will fallback to just finding a safe location if needed -bool PrinceMalchezaarNonTankAvoidInfernalAction::Execute(Event event) +bool PrinceMalchezaarNonTankAvoidInfernalAction::Execute(Event /*event*/) { Unit* malchezaar = AI_VALUE2(Unit*, "find target", "prince malchezaar"); if (!malchezaar) @@ -1189,7 +1188,7 @@ bool PrinceMalchezaarNonTankAvoidInfernalAction::Execute(Event event) // This is similar to the non-tank avoid infernal action, but the movement is based on the bot's location // And the safe distance from infernals is larger to give melee more room to maneuver -bool PrinceMalchezaarMainTankMovementAction::Execute(Event event) +bool PrinceMalchezaarMainTankMovementAction::Execute(Event /*event*/) { Unit* malchezaar = AI_VALUE2(Unit*, "find target", "prince malchezaar"); if (!malchezaar) @@ -1255,7 +1254,7 @@ bool PrinceMalchezaarMainTankMovementAction::Execute(Event event) // The tank position is near the Southeastern area of the Master's Terrace // The tank moves Nightbane into position in two steps to try to get Nightbane to face sideways to the raid -bool NightbaneGroundPhasePositionBossAction::Execute(Event event) +bool NightbaneGroundPhasePositionBossAction::Execute(Event /*event*/) { Unit* nightbane = AI_VALUE2(Unit*, "find target", "nightbane"); if (!nightbane) @@ -1301,7 +1300,7 @@ bool NightbaneGroundPhasePositionBossAction::Execute(Event event) // Ranged bots rotate between 3 positions to avoid standing in Charred Earth, which lasts for // 30s and has a minimum cooldown of 18s (so there can be 2 active at once) // Ranged positions are near the Northeastern door to the tower -bool NightbaneGroundPhaseRotateRangedPositionsAction::Execute(Event event) +bool NightbaneGroundPhaseRotateRangedPositionsAction::Execute(Event /*event*/) { const ObjectGuid botGuid = bot->GetGUID(); uint8 index = nightbaneRangedStep.count(botGuid) ? nightbaneRangedStep[botGuid] : 0; @@ -1345,7 +1344,7 @@ bool NightbaneGroundPhaseRotateRangedPositionsAction::Execute(Event event) } // For countering Bellowing Roars during the ground phase -bool NightbaneCastFearWardOnMainTankAction::Execute(Event event) +bool NightbaneCastFearWardOnMainTankAction::Execute(Event /*event*/) { Player* mainTank = nullptr; if (Group* group = bot->GetGroup()) @@ -1368,7 +1367,7 @@ bool NightbaneCastFearWardOnMainTankAction::Execute(Event event) } // Put pets on passive during the flight phase so they don't try to chase Nightbane off the map -bool NightbaneControlPetAggressionAction::Execute(Event event) +bool NightbaneControlPetAggressionAction::Execute(Event /*event*/) { Unit* nightbane = AI_VALUE2(Unit*, "find target", "nightbane"); if (!nightbane) @@ -1394,7 +1393,7 @@ bool NightbaneControlPetAggressionAction::Execute(Event event) // 2. Once Rain of Bones hits, the whole party moves to a new stack position // This action lasts for the first 35 seconds of the flight phase, after which Nightbane gets // ready to land, and the player will need to lead the bots over near the ground phase position -bool NightbaneFlightPhaseMovementAction::Execute(Event event) +bool NightbaneFlightPhaseMovementAction::Execute(Event /*event*/) { Unit* nightbane = AI_VALUE2(Unit*, "find target", "nightbane"); if (!nightbane || nightbane->GetPositionZ() <= NIGHTBANE_FLIGHT_Z) @@ -1440,7 +1439,7 @@ bool NightbaneFlightPhaseMovementAction::Execute(Event event) return false; } -bool NightbaneManageTimersAndTrackersAction::Execute(Event event) +bool NightbaneManageTimersAndTrackersAction::Execute(Event /*event*/) { Unit* nightbane = AI_VALUE2(Unit*, "find target", "nightbane"); if (!nightbane) diff --git a/src/Ai/Raid/Karazhan/Util/RaidKarazhanHelpers.cpp b/src/Ai/Raid/Karazhan/Util/RaidKarazhanHelpers.cpp index ea989dcf31..513920e111 100644 --- a/src/Ai/Raid/Karazhan/Util/RaidKarazhanHelpers.cpp +++ b/src/Ai/Raid/Karazhan/Util/RaidKarazhanHelpers.cpp @@ -1,5 +1,4 @@ #include "RaidKarazhanHelpers.h" -#include "RaidKarazhanActions.h" #include "Playerbots.h" namespace KarazhanHelpers @@ -304,10 +303,8 @@ namespace KarazhanHelpers { float sx = start.GetPositionX(); float sy = start.GetPositionY(); - float sz = start.GetPositionZ(); float tx = target.GetPositionX(); float ty = target.GetPositionY(); - float tz = target.GetPositionZ(); const float totalDist = start.GetExactDist2d(target.GetPositionX(), target.GetPositionY()); if (totalDist == 0.0f) @@ -318,7 +315,6 @@ namespace KarazhanHelpers float t = checkDist / totalDist; float checkX = sx + (tx - sx) * t; float checkY = sy + (ty - sy) * t; - float checkZ = sz + (tz - sz) * t; for (Unit* hazard : hazards) { const float hx = checkX - hazard->GetPositionX(); diff --git a/src/Ai/Raid/Magtheridon/Action/RaidMagtheridonActions.cpp b/src/Ai/Raid/Magtheridon/Action/RaidMagtheridonActions.cpp index 7f1ad5b859..ffe446965d 100644 --- a/src/Ai/Raid/Magtheridon/Action/RaidMagtheridonActions.cpp +++ b/src/Ai/Raid/Magtheridon/Action/RaidMagtheridonActions.cpp @@ -8,7 +8,7 @@ using namespace MagtheridonHelpers; -bool MagtheridonMainTankAttackFirstThreeChannelersAction::Execute(Event event) +bool MagtheridonMainTankAttackFirstThreeChannelersAction::Execute(Event /*event*/) { Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon"); if (!magtheridon) @@ -67,7 +67,7 @@ bool MagtheridonMainTankAttackFirstThreeChannelersAction::Execute(Event event) return false; } -bool MagtheridonFirstAssistTankAttackNWChannelerAction::Execute(Event event) +bool MagtheridonFirstAssistTankAttackNWChannelerAction::Execute(Event /*event*/) { Creature* channelerDiamond = GetChanneler(bot, NORTHWEST_CHANNELER); if (!channelerDiamond) @@ -100,7 +100,7 @@ bool MagtheridonFirstAssistTankAttackNWChannelerAction::Execute(Event event) return false; } -bool MagtheridonSecondAssistTankAttackNEChannelerAction::Execute(Event event) +bool MagtheridonSecondAssistTankAttackNEChannelerAction::Execute(Event /*event*/) { Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER); if (!channelerTriangle) @@ -134,7 +134,7 @@ bool MagtheridonSecondAssistTankAttackNEChannelerAction::Execute(Event event) } // Misdirect West & East Channelers to Main Tank -bool MagtheridonMisdirectHellfireChannelers::Execute(Event event) +bool MagtheridonMisdirectHellfireChannelers::Execute(Event /*event*/) { Group* group = bot->GetGroup(); if (!group) @@ -211,7 +211,7 @@ bool MagtheridonMisdirectHellfireChannelers::Execute(Event event) return false; } -bool MagtheridonAssignDPSPriorityAction::Execute(Event event) +bool MagtheridonAssignDPSPriorityAction::Execute(Event /*event*/) { // Listed in order of priority Creature* channelerSquare = GetChanneler(bot, SOUTH_CHANNELER); @@ -285,7 +285,7 @@ bool MagtheridonAssignDPSPriorityAction::Execute(Event event) // Assign Burning Abyssals to Warlocks to Banish // Burning Abyssals in excess of Warlocks in party will be Feared -bool MagtheridonWarlockCCBurningAbyssalAction::Execute(Event event) +bool MagtheridonWarlockCCBurningAbyssalAction::Execute(Event /*event*/) { Group* group = bot->GetGroup(); if (!group) @@ -338,7 +338,7 @@ bool MagtheridonWarlockCCBurningAbyssalAction::Execute(Event event) } // Main tank will back up to the Northern point of the room -bool MagtheridonMainTankPositionBossAction::Execute(Event event) +bool MagtheridonMainTankPositionBossAction::Execute(Event /*event*/) { Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon"); if (!magtheridon) @@ -376,7 +376,7 @@ bool MagtheridonMainTankPositionBossAction::Execute(Event event) std::unordered_map MagtheridonSpreadRangedAction::initialPositions; std::unordered_map MagtheridonSpreadRangedAction::hasReachedInitialPosition; -bool MagtheridonSpreadRangedAction::Execute(Event event) +bool MagtheridonSpreadRangedAction::Execute(Event /*event*/) { Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon"); if (!magtheridon) @@ -486,7 +486,7 @@ bool MagtheridonSpreadRangedAction::Execute(Event event) // For bots that are assigned to click cubes // Magtheridon casts Blast Nova every 54.35 to 55.40s, with a 2s cast time -bool MagtheridonUseManticronCubeAction::Execute(Event event) +bool MagtheridonUseManticronCubeAction::Execute(Event /*event*/) { Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon"); if (!magtheridon) @@ -627,7 +627,7 @@ bool MagtheridonUseManticronCubeAction::HandleCubeInteraction(const CubeInfo& cu // is not interrupted or takes too long to interrupt, the timer will be thrown off for the rest of the encounter. // Correcting this issue is complicated and probably would need some rewriting--I have not done so and // and view the current solution as sufficient since in TBC a missed Blast Nova would be a guaranteed wipe anyway. -bool MagtheridonManageTimersAndAssignmentsAction::Execute(Event event) +bool MagtheridonManageTimersAndAssignmentsAction::Execute(Event /*event*/) { Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon"); if (!magtheridon) diff --git a/src/Ai/Raid/MoltenCore/Action/RaidMcActions.cpp b/src/Ai/Raid/MoltenCore/Action/RaidMcActions.cpp index b18c8b8534..d2bc2edc1b 100644 --- a/src/Ai/Raid/MoltenCore/Action/RaidMcActions.cpp +++ b/src/Ai/Raid/MoltenCore/Action/RaidMcActions.cpp @@ -2,7 +2,6 @@ #include "Playerbots.h" #include "RtiTargetValue.h" -#include "RaidMcTriggers.h" #include "RaidMcHelpers.h" static constexpr float LIVING_BOMB_DISTANCE = 20.0f; @@ -20,12 +19,12 @@ static constexpr float CORE_RAGER_STEP_DISTANCE = 5.0f; using namespace MoltenCoreHelpers; -bool McMoveFromGroupAction::Execute(Event event) +bool McMoveFromGroupAction::Execute(Event /*event*/) { return MoveFromGroup(LIVING_BOMB_DISTANCE); } -bool McMoveFromBaronGeddonAction::Execute(Event event) +bool McMoveFromBaronGeddonAction::Execute(Event /*event*/) { if (Unit* boss = AI_VALUE2(Unit*, "find target", "baron geddon")) { @@ -42,7 +41,7 @@ bool McMoveFromBaronGeddonAction::Execute(Event event) return false; } -bool McShazzrahMoveAwayAction::Execute(Event event) +bool McShazzrahMoveAwayAction::Execute(Event /*event*/) { if (Unit* boss = AI_VALUE2(Unit*, "find target", "shazzrah")) { @@ -53,7 +52,7 @@ bool McShazzrahMoveAwayAction::Execute(Event event) return false; } -bool McGolemaggMarkBossAction::Execute(Event event) +bool McGolemaggMarkBossAction::Execute(Event /*event*/) { if (Unit* boss = AI_VALUE2(Unit*, "find target", "golemagg the incinerator")) { @@ -115,7 +114,7 @@ bool McGolemaggTankAction::FindCoreRagers(Unit*& coreRager1, Unit*& coreRager2) return coreRager1 != nullptr && coreRager2 != nullptr; } -bool McGolemaggMainTankAttackGolemaggAction::Execute(Event event) +bool McGolemaggMainTankAttackGolemaggAction::Execute(Event /*event*/) { // At this point, we know we are not the last living tank in the group. if (Unit* boss = AI_VALUE2(Unit*, "find target", "golemagg the incinerator")) diff --git a/src/Ai/Raid/ObsidianSanctum/Action/RaidOsActions.cpp b/src/Ai/Raid/ObsidianSanctum/Action/RaidOsActions.cpp index 2ee68bca4b..ee9dfb0f75 100644 --- a/src/Ai/Raid/ObsidianSanctum/Action/RaidOsActions.cpp +++ b/src/Ai/Raid/ObsidianSanctum/Action/RaidOsActions.cpp @@ -3,7 +3,7 @@ #include "Playerbots.h" -bool SartharionTankPositionAction::Execute(Event event) +bool SartharionTankPositionAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); if (!boss) { return false; } @@ -84,7 +84,7 @@ bool SartharionTankPositionAction::Execute(Event event) return false; } -bool AvoidTwilightFissureAction::Execute(Event event) +bool AvoidTwilightFissureAction::Execute(Event /*event*/) { const float radius = 5.0f; @@ -104,7 +104,7 @@ bool AvoidTwilightFissureAction::Execute(Event event) return false; } -bool AvoidFlameTsunamiAction::Execute(Event event) +bool AvoidFlameTsunamiAction::Execute(Event /*event*/) { // Adjustable, this is the acceptable distance to stack point that will be accepted as "safe" float looseDistance = 4.0f; @@ -167,7 +167,7 @@ bool AvoidFlameTsunamiAction::Execute(Event event) return false; } -bool SartharionAttackPriorityAction::Execute(Event event) +bool SartharionAttackPriorityAction::Execute(Event /*event*/) { Unit* sartharion = AI_VALUE2(Unit*, "find target", "sartharion"); Unit* shadron = AI_VALUE2(Unit*, "find target", "shadron"); @@ -206,7 +206,7 @@ bool SartharionAttackPriorityAction::Execute(Event event) return false; } -bool EnterTwilightPortalAction::Execute(Event event) +bool EnterTwilightPortalAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); if (!boss || !boss->HasAura(SPELL_GIFT_OF_TWILIGHT_FIRE)) { return false; } @@ -227,7 +227,7 @@ bool EnterTwilightPortalAction::Execute(Event event) return true; } -bool ExitTwilightPortalAction::Execute(Event event) +bool ExitTwilightPortalAction::Execute(Event /*event*/) { GameObject* portal = bot->FindNearestGameObject(GO_NORMAL_PORTAL, 100.0f); if (!portal) { return false; } diff --git a/src/Ai/Raid/Onyxia/Action/RaidOnyxiaActions.cpp b/src/Ai/Raid/Onyxia/Action/RaidOnyxiaActions.cpp index 9fa4612bf6..e2fe3cbe6d 100644 --- a/src/Ai/Raid/Onyxia/Action/RaidOnyxiaActions.cpp +++ b/src/Ai/Raid/Onyxia/Action/RaidOnyxiaActions.cpp @@ -1,4 +1,3 @@ -// RaidOnyxiaActions.cpp #include "RaidOnyxiaActions.h" #include "GenericSpellActions.h" @@ -7,7 +6,7 @@ #include "Playerbots.h" #include "PositionAction.h" -bool RaidOnyxiaMoveToSideAction::Execute(Event event) +bool RaidOnyxiaMoveToSideAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "onyxia"); if (!boss) @@ -38,7 +37,7 @@ bool RaidOnyxiaMoveToSideAction::Execute(Event event) return false; } -bool RaidOnyxiaSpreadOutAction::Execute(Event event) +bool RaidOnyxiaSpreadOutAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "onyxia"); @@ -64,7 +63,7 @@ bool RaidOnyxiaSpreadOutAction::Execute(Event event) return MoveFromGroup(9.0f); // move 9 yards } -bool RaidOnyxiaMoveToSafeZoneAction::Execute(Event event) +bool RaidOnyxiaMoveToSafeZoneAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "onyxia"); if (!boss) @@ -105,7 +104,7 @@ bool RaidOnyxiaMoveToSafeZoneAction::Execute(Event event) false, false, false, false, MovementPriority::MOVEMENT_COMBAT); } -bool RaidOnyxiaKillWhelpsAction::Execute(Event event) +bool RaidOnyxiaKillWhelpsAction::Execute(Event /*event*/) { Unit* currentTarget = AI_VALUE(Unit*, "current target"); // If already attacking a whelp, don't swap targets @@ -129,7 +128,7 @@ bool RaidOnyxiaKillWhelpsAction::Execute(Event event) return false; } -bool OnyxiaAvoidEggsAction::Execute(Event event) +bool OnyxiaAvoidEggsAction::Execute(Event /*event*/) { Position botPos = Position(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ()); diff --git a/src/Ai/Raid/Ulduar/Action/RaidUlduarActions.cpp b/src/Ai/Raid/Ulduar/Action/RaidUlduarActions.cpp index f7eca38db1..15db518a73 100644 --- a/src/Ai/Raid/Ulduar/Action/RaidUlduarActions.cpp +++ b/src/Ai/Raid/Ulduar/Action/RaidUlduarActions.cpp @@ -47,7 +47,7 @@ const Position yoggPortalLoc[] = { {1960.62f, -32.00f, 325.5f}, {1981.98f, -5.69f, 325.5f}, {1982.78f, -45.73f, 325.5f}, {2000.66f, -29.68f, 325.5f}, {1999.88f, -19.61f, 325.5f}, {1961.37f, -19.54f, 325.5f}}; -bool FlameLeviathanVehicleAction::Execute(Event event) +bool FlameLeviathanVehicleAction::Execute(Event /*event*/) { vehicleBase_ = bot->GetVehicleBase(); vehicle_ = bot->GetVehicle(); @@ -269,7 +269,7 @@ bool FlameLeviathanVehicleAction::ChopperAction(Unit* target) return false; } -bool FlameLeviathanEnterVehicleAction::Execute(Event event) +bool FlameLeviathanEnterVehicleAction::Execute(Event /*event*/) { // do not switch vehicles yet if (bot->GetVehicle()) @@ -414,7 +414,7 @@ bool FlameLeviathanEnterVehicleAction::AllMainVehiclesOnUse() return demolisher >= maxC && siege >= maxC; } -bool RazorscaleAvoidDevouringFlameAction::Execute(Event event) +bool RazorscaleAvoidDevouringFlameAction::Execute(Event /*event*/) { RazorscaleBossHelper razorscaleHelper(botAI); @@ -495,7 +495,7 @@ bool RazorscaleAvoidDevouringFlameAction::isUseful() return false; // No nearby flames or bot is at a safe distance } -bool RazorscaleAvoidSentinelAction::Execute(Event event) +bool RazorscaleAvoidSentinelAction::Execute(Event /*event*/) { bool isMainTank = botAI->IsMainTank(bot); bool isRanged = botAI->IsRanged(bot); @@ -617,7 +617,7 @@ bool RazorscaleAvoidSentinelAction::isUseful() return false; } -bool RazorscaleAvoidWhirlwindAction::Execute(Event event) +bool RazorscaleAvoidWhirlwindAction::Execute(Event /*event*/) { if (botAI->IsTank(bot)) { @@ -734,24 +734,18 @@ bool RazorscaleIgnoreBossAction::isUseful() return false; } -bool RazorscaleIgnoreBossAction::Execute(Event event) +bool RazorscaleIgnoreBossAction::Execute(Event /*event*/) { if (!bot) - { return false; - } Unit* boss = AI_VALUE2(Unit*, "find target", "razorscale"); if (!boss) - { return false; - } Group* group = bot->GetGroup(); if (!group) - { return false; - } // Check if the bot is outside the designated area and move inside first if (bot->GetDistance2d(RazorscaleBossHelper::RAZORSCALE_ARENA_CENTER_X, @@ -764,17 +758,13 @@ bool RazorscaleIgnoreBossAction::Execute(Event event) } if (!botAI->IsTank(bot)) - { return false; - } // Check if the boss is already set as the moon marker int8 moonIndex = 4; ObjectGuid currentMoonTarget = group->GetTargetIcon(moonIndex); if (currentMoonTarget == boss->GetGUID()) - { return false; // Moon marker is already correctly set - } // Get the main tank and determine role Unit* mainTankUnit = AI_VALUE(Unit*, "main tank"); @@ -809,9 +799,7 @@ bool RazorscaleGroundedAction::isUseful() { Unit* boss = AI_VALUE2(Unit*, "find target", "razorscale"); if (!boss || !boss->IsAlive() || boss->GetPositionZ() > RazorscaleBossHelper::RAZORSCALE_FLYING_Z_THRESHOLD) - { return false; - } if (botAI->IsMainTank(bot)) { @@ -854,9 +842,7 @@ bool RazorscaleGroundedAction::isUseful() } if (botAI->IsMelee(bot)) - { return false; - } if (botAI->IsRanged(bot)) { @@ -891,7 +877,7 @@ bool RazorscaleGroundedAction::isUseful() return false; } -bool RazorscaleGroundedAction::Execute(Event event) +bool RazorscaleGroundedAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "razorscale"); if (!boss || !boss->IsAlive() || boss->GetPositionZ() > RazorscaleBossHelper::RAZORSCALE_FLYING_Z_THRESHOLD) @@ -974,12 +960,10 @@ bool RazorscaleGroundedAction::Execute(Event event) return false; } -bool RazorscaleHarpoonAction::Execute(Event event) +bool RazorscaleHarpoonAction::Execute(Event /*event*/) { if (!bot) - { return false; - } RazorscaleBossHelper razorscaleHelper(botAI); @@ -1142,7 +1126,7 @@ bool RazorscaleFuseArmorAction::isUseful() return false; } -bool RazorscaleFuseArmorAction::Execute(Event event) +bool RazorscaleFuseArmorAction::Execute(Event /*event*/) { // We already know from isUseful() that: // 1) This bot can tank, AND @@ -1161,7 +1145,7 @@ bool IronAssemblyLightningTendrilsAction::isUseful() return ironAssemblyLightningTendrilsTrigger.IsActive(); } -bool IronAssemblyLightningTendrilsAction::Execute(Event event) +bool IronAssemblyLightningTendrilsAction::Execute(Event /*event*/) { const float radius = 18.0f + 10.0f; // 18 yards + 10 yards for safety @@ -1185,7 +1169,7 @@ bool IronAssemblyOverloadAction::isUseful() return ironAssemblyOverloadTrigger.IsActive(); } -bool IronAssemblyOverloadAction::Execute(Event event) +bool IronAssemblyOverloadAction::Execute(Event /*event*/) { const float radius = 20.0f + 5.0f; // 20 yards + 5 yards for safety @@ -1209,7 +1193,7 @@ bool IronAssemblyRuneOfPowerAction::isUseful() return ironAssemblyRuneOfPowerTrigger.IsActive(); } -bool IronAssemblyRuneOfPowerAction::Execute(Event event) +bool IronAssemblyRuneOfPowerAction::Execute(Event /*event*/) { Unit* target = botAI->GetUnit(bot->GetTarget()); if (!target || !target->IsAlive()) @@ -1224,7 +1208,7 @@ bool KologarnMarkDpsTargetAction::isUseful() return kologarnMarkDpsTargetTrigger.IsActive(); } -bool KologarnMarkDpsTargetAction::Execute(Event event) +bool KologarnMarkDpsTargetAction::Execute(Event /*event*/) { Unit* targetToMark = nullptr; Unit* additionalTargetToMark = nullptr; @@ -1359,7 +1343,7 @@ bool KologarnMarkDpsTargetAction::Execute(Event event) return false; } -bool KologarnFallFromFloorAction::Execute(Event event) +bool KologarnFallFromFloorAction::Execute(Event /*event*/) { return bot->TeleportTo(bot->GetMapId(), ULDUAR_KOLOGARN_RESTORE_POSITION.GetPositionX(), ULDUAR_KOLOGARN_RESTORE_POSITION.GetPositionY(), @@ -1373,7 +1357,7 @@ bool KologarnFallFromFloorAction::isUseful() return kologarnFallFromFloorTrigger.IsActive(); } -bool KologarnRubbleSlowdownAction::Execute(Event event) +bool KologarnRubbleSlowdownAction::Execute(Event /*event*/) { Group* group = bot->GetGroup(); if (!group) @@ -1388,7 +1372,7 @@ bool KologarnRubbleSlowdownAction::Execute(Event event) return botAI->CastSpell("frost trap", currentSkullUnit); } -bool KologarnEyebeamAction::Execute(Event event) +bool KologarnEyebeamAction::Execute(Event /*event*/) { float distanceToLeftPoint = bot->GetExactDist(ULDUAR_KOLOGARN_EYEBEAM_LEFT_POSITION); float distanceToRightPoint = bot->GetExactDist(ULDUAR_KOLOGARN_EYEBEAM_RIGHT_POSITION); @@ -1447,7 +1431,7 @@ bool KologarnRtiTargetAction::isUseful() return kologarnRtiTargetTrigger.IsActive(); } -bool KologarnRtiTargetAction::Execute(Event event) +bool KologarnRtiTargetAction::Execute(Event /*event*/) { if (botAI->IsMainTank(bot) || botAI->IsAssistTankOfIndex(bot, 0)) { @@ -1470,13 +1454,13 @@ bool KologarnCrunchArmorAction::isUseful() return botAI->HasCheat(BotCheatMask::raid); } -bool KologarnCrunchArmorAction::Execute(Event event) +bool KologarnCrunchArmorAction::Execute(Event /*event*/) { bot->RemoveAura(SPELL_CRUNCH_ARMOR); return true; } -bool AuriayaFallFromFloorAction::Execute(Event event) +bool AuriayaFallFromFloorAction::Execute(Event /*event*/) { Player* master = botAI->GetMaster(); @@ -1522,7 +1506,7 @@ bool HodirMoveSnowpackedIcicleAction::isUseful() return true; } -bool HodirMoveSnowpackedIcicleAction::Execute(Event event) +bool HodirMoveSnowpackedIcicleAction::Execute(Event /*event*/) { Creature* target = bot->FindNearestCreature(NPC_SNOWPACKED_ICICLE, 100.0f); if (!target) @@ -1532,7 +1516,7 @@ bool HodirMoveSnowpackedIcicleAction::Execute(Event event) false, false, true, MovementPriority::MOVEMENT_NORMAL, true); } -bool HodirBitingColdJumpAction::Execute(Event event) +bool HodirBitingColdJumpAction::Execute(Event /*event*/) { bot->RemoveAurasDueToSpell(SPELL_BITING_COLD_PLAYER_AURA); @@ -1588,7 +1572,7 @@ bool FreyaMoveAwayNatureBombAction::isUseful() return true; } -bool FreyaMoveAwayNatureBombAction::Execute(Event event) +bool FreyaMoveAwayNatureBombAction::Execute(Event /*event*/) { GameObject* target = bot->FindNearestGameObject(GOBJECT_NATURE_BOMB, 12.0f); if (!target) @@ -1603,7 +1587,7 @@ bool FreyaMarkDpsTargetAction::isUseful() return freyaMarkDpsTargetTrigger.IsActive(); } -bool FreyaMarkDpsTargetAction::Execute(Event event) +bool FreyaMarkDpsTargetAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "freya"); if (!boss || !boss->IsAlive()) @@ -1759,7 +1743,7 @@ bool FreyaMoveToHealingSporeAction::isUseful() return freyaMoveToHealingSporeTrigger.IsActive(); } -bool FreyaMoveToHealingSporeAction::Execute(Event event) +bool FreyaMoveToHealingSporeAction::Execute(Event /*event*/) { GuidVector targets = AI_VALUE(GuidVector, "nearest npcs"); Creature* nearestSpore = nullptr; @@ -1801,7 +1785,7 @@ bool ThorimUnbalancingStrikeAction::isUseful() return botAI->HasCheat(BotCheatMask::raid); } -bool ThorimUnbalancingStrikeAction::Execute(Event event) +bool ThorimUnbalancingStrikeAction::Execute(Event /*event*/) { bot->RemoveAura(SPELL_UNBALANCING_STRIKE); return true; @@ -1813,7 +1797,7 @@ bool ThorimMarkDpsTargetAction::isUseful() return thorimMarkDpsTargetTrigger.IsActive(); } -bool ThorimMarkDpsTargetAction::Execute(Event event) +bool ThorimMarkDpsTargetAction::Execute(Event /*event*/) { Unit* targetToMark = nullptr; @@ -1914,7 +1898,7 @@ bool ThorimArenaPositioningAction::isUseful() return thorimArenaPositioningTrigger.IsActive(); } -bool ThorimArenaPositioningAction::Execute(Event event) +bool ThorimArenaPositioningAction::Execute(Event /*event*/) { FollowMasterStrategy followMasterStrategy(botAI); @@ -1936,7 +1920,7 @@ bool ThorimGauntletPositioningAction::isUseful() return thorimGauntletPositioningTrigger.IsActive(); } -bool ThorimGauntletPositioningAction::Execute(Event event) +bool ThorimGauntletPositioningAction::Execute(Event /*event*/) { FollowMasterStrategy followMasterStrategy(botAI); @@ -2110,7 +2094,7 @@ bool ThorimGauntletPositioningAction::Execute(Event event) return false; } -bool ThorimFallFromFloorAction::Execute(Event event) +bool ThorimFallFromFloorAction::Execute(Event /*event*/) { Player* master = botAI->GetMaster(); @@ -2127,7 +2111,7 @@ bool ThorimFallFromFloorAction::isUseful() return thorimFallFromFloorTrigger.IsActive(); } -bool ThorimPhase2PositioningAction::Execute(Event event) +bool ThorimPhase2PositioningAction::Execute(Event /*event*/) { Position targetPosition; bool backward = false; @@ -2187,7 +2171,7 @@ bool ThorimPhase2PositioningAction::isUseful() return thorimPhase2PositioningTrigger.IsActive(); } -bool MimironShockBlastAction::Execute(Event event) +bool MimironShockBlastAction::Execute(Event /*event*/) { Unit* leviathanMkII = nullptr; Unit* vx001 = nullptr; @@ -2261,7 +2245,7 @@ bool MimironShockBlastAction::isUseful() return mimironShockBlastTrigger.IsActive(); } -bool MimironPhase1PositioningAction::Execute(Event event) +bool MimironPhase1PositioningAction::Execute(Event /*event*/) { SET_AI_VALUE(float, "disperse distance", 6.0f); return true; @@ -2273,7 +2257,7 @@ bool MimironPhase1PositioningAction::isUseful() return mimironPhase1PositioningTrigger.IsActive(); } -bool MimironP3Wx2LaserBarrageAction::Execute(Event event) +bool MimironP3Wx2LaserBarrageAction::Execute(Event /*event*/) { auto master = botAI->GetMaster(); if (!master || !master->IsAlive()) @@ -2297,7 +2281,7 @@ bool MimironRapidBurstAction::isUseful() return mimironRapidBurstTrigger.IsActive(); } -bool MimironRapidBurstAction::Execute(Event event) +bool MimironRapidBurstAction::Execute(Event /*event*/) { Unit* leviathanMkII = nullptr; @@ -2404,7 +2388,7 @@ bool MimironRapidBurstAction::Execute(Event event) return true; } -bool MimironAerialCommandUnitAction::Execute(Event event) +bool MimironAerialCommandUnitAction::Execute(Event /*event*/) { Unit* boss = nullptr; Unit* bombBot = nullptr; @@ -2476,7 +2460,7 @@ bool MimironRocketStrikeAction::isUseful() return mimironRocketStrikeTrigger.IsActive(); } -bool MimironRocketStrikeAction::Execute(Event event) +bool MimironRocketStrikeAction::Execute(Event /*event*/) { Unit* leviathanMkII = nullptr; Unit* vx001 = nullptr; @@ -2491,25 +2475,19 @@ bool MimironRocketStrikeAction::Execute(Event event) continue; if (target->GetEntry() == NPC_LEVIATHAN_MKII) - { leviathanMkII = target; - } + else if (target->GetEntry() == NPC_VX001) - { vx001 = target; - } + else if (target->GetEntry() == NPC_AERIAL_COMMAND_UNIT) - { aerialCommandUnit = target; - } } Creature* rocketStrikeN = bot->FindNearestCreature(NPC_ROCKET_STRIKE_N, 100.0f); if (!rocketStrikeN) - { return false; - } if (!vx001 && !aerialCommandUnit) { @@ -2537,7 +2515,7 @@ bool MimironRocketStrikeAction::Execute(Event event) } } -bool MimironPhase4MarkDpsAction::Execute(Event event) +bool MimironPhase4MarkDpsAction::Execute(Event /*event*/) { Unit* leviathanMkII = nullptr; Unit* vx001 = nullptr; @@ -2626,7 +2604,7 @@ bool MimironPhase4MarkDpsAction::Execute(Event event) } } -bool MimironCheatAction::Execute(Event event) +bool MimironCheatAction::Execute(Event /*event*/) { GuidVector targets = AI_VALUE(GuidVector, "nearest npcs"); for (const ObjectGuid& guid : targets) @@ -2648,7 +2626,7 @@ bool MimironCheatAction::Execute(Event event) return true; } -bool VezaxCheatAction::Execute(Event event) +bool VezaxCheatAction::Execute(Event /*event*/) { // Restore bot's mana to full uint32 maxMana = bot->GetMaxPower(POWER_MANA); @@ -2660,7 +2638,7 @@ bool VezaxCheatAction::Execute(Event event) return true; } -bool VezaxShadowCrashAction::Execute(Event event) +bool VezaxShadowCrashAction::Execute(Event /*event*/) { // Find General Vezax boss Unit* boss = AI_VALUE2(Unit*, "find target", "general vezax"); @@ -2704,7 +2682,7 @@ bool VezaxShadowCrashAction::Execute(Event event) true); } -bool VezaxMarkOfTheFacelessAction::Execute(Event event) +bool VezaxMarkOfTheFacelessAction::Execute(Event /*event*/) { return MoveTo(bot->GetMapId(), ULDUAR_VEZAX_MARK_OF_THE_FACELESS_SPOT.GetPositionX(), ULDUAR_VEZAX_MARK_OF_THE_FACELESS_SPOT.GetPositionY(), @@ -2712,7 +2690,7 @@ bool VezaxMarkOfTheFacelessAction::Execute(Event event) MovementPriority::MOVEMENT_FORCED, true, false); } -bool YoggSaronOminousCloudCheatAction::Execute(Event event) +bool YoggSaronOminousCloudCheatAction::Execute(Event /*event*/) { YoggSaronTrigger yoggSaronTrigger(botAI); @@ -2732,14 +2710,14 @@ bool YoggSaronOminousCloudCheatAction::Execute(Event event) return true; } -bool YoggSaronGuardianPositioningAction::Execute(Event event) +bool YoggSaronGuardianPositioningAction::Execute(Event /*event*/) { return MoveTo(bot->GetMapId(), ULDUAR_YOGG_SARON_MIDDLE.GetPositionX(), ULDUAR_YOGG_SARON_MIDDLE.GetPositionY(), ULDUAR_YOGG_SARON_MIDDLE.GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_FORCED, true, false); } -bool YoggSaronSanityAction::Execute(Event event) +bool YoggSaronSanityAction::Execute(Event /*event*/) { Creature* sanityWell = bot->FindNearestCreature(NPC_SANITY_WELL, 200.0f); @@ -2748,7 +2726,7 @@ bool YoggSaronSanityAction::Execute(Event event) true, false); } -bool YoggSaronMarkTargetAction::Execute(Event event) +bool YoggSaronMarkTargetAction::Execute(Event /*event*/) { Group* group = bot->GetGroup(); if (!group) @@ -2877,7 +2855,7 @@ bool YoggSaronMarkTargetAction::Execute(Event event) return false; } -bool YoggSaronBrainLinkAction::Execute(Event event) +bool YoggSaronBrainLinkAction::Execute(Event /*event*/) { Group* group = bot->GetGroup(); if (!group) @@ -2897,7 +2875,7 @@ bool YoggSaronBrainLinkAction::Execute(Event event) return false; } -bool YoggSaronMoveToEnterPortalAction::Execute(Event event) +bool YoggSaronMoveToEnterPortalAction::Execute(Event /*event*/) { Group* group = bot->GetGroup(); if (!group) @@ -2966,7 +2944,7 @@ bool YoggSaronMoveToEnterPortalAction::Execute(Event event) } } -bool YoggSaronFallFromFloorAction::Execute(Event event) +bool YoggSaronFallFromFloorAction::Execute(Event /*event*/) { std::string rtiMark = AI_VALUE(std::string, "rti"); if (rtiMark == "skull") @@ -2998,7 +2976,7 @@ bool YoggSaronFallFromFloorAction::Execute(Event event) return false; } -bool YoggSaronBossRoomMovementCheatAction::Execute(Event event) +bool YoggSaronBossRoomMovementCheatAction::Execute(Event /*event*/) { FollowMasterStrategy followMasterStrategy(botAI); if (botAI->HasStrategy(followMasterStrategy.getName(), BotState::BOT_STATE_NON_COMBAT)) @@ -3034,7 +3012,7 @@ bool YoggSaronBossRoomMovementCheatAction::Execute(Event event) currentSkullUnit->GetPositionZ(), bot->GetOrientation()); } -bool YoggSaronUsePortalAction::Execute(Event event) +bool YoggSaronUsePortalAction::Execute(Event /*event*/) { Creature* assignedPortal = bot->FindNearestCreature(NPC_DESCEND_INTO_MADNESS, 2.0f, true); if (!assignedPortal) @@ -3051,7 +3029,7 @@ bool YoggSaronUsePortalAction::Execute(Event event) return assignedPortal->HandleSpellClick(bot); } -bool YoggSaronIllusionRoomAction::Execute(Event event) +bool YoggSaronIllusionRoomAction::Execute(Event /*event*/) { YoggSaronTrigger yoggSaronTrigger(botAI); @@ -3173,30 +3151,23 @@ bool YoggSaronIllusionRoomAction::SetBrainRtiTarget(YoggSaronTrigger yoggSaronTr return true; } -bool YoggSaronMoveToExitPortalAction::Execute(Event event) +bool YoggSaronMoveToExitPortalAction::Execute(Event /*event*/) { GameObject* portal = bot->FindNearestGameObject(GO_FLEE_TO_THE_SURFACE_PORTAL, 100.0f); if (!portal) - { return false; - } if (botAI->HasCheat(BotCheatMask::raid)) - { bot->TeleportTo(bot->GetMapId(), portal->GetPositionX(), portal->GetPositionY(), portal->GetPositionZ(), bot->GetOrientation()); - } + else - { MoveTo(bot->GetMapId(), portal->GetPositionX(), portal->GetPositionY(), portal->GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_FORCED, true, false); - } if (bot->GetDistance2d(portal) > 2.0f) - { return false; - } portal->Use(bot); @@ -3204,13 +3175,12 @@ bool YoggSaronMoveToExitPortalAction::Execute(Event event) return true; } -bool YoggSaronLunaticGazeAction::Execute(Event event) +bool YoggSaronLunaticGazeAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "yogg-saron"); if (!boss || !boss->IsAlive()) - { return false; - } + float angle = bot->GetAngle(boss); float newAngle = Position::NormalizeOrientation(angle + M_PI); // Add 180 degrees (PI radians) bot->SetFacingTo(newAngle); @@ -3218,15 +3188,14 @@ bool YoggSaronLunaticGazeAction::Execute(Event event) if (botAI->IsRangedDps(bot)) { if (AI_VALUE(std::string, "rti") != "cross") - { botAI->GetAiObjectContext()->GetValue("rti")->Set("cross"); - } + } return true; } -bool YoggSaronPhase3PositioningAction::Execute(Event event) +bool YoggSaronPhase3PositioningAction::Execute(Event /*event*/) { if (botAI->IsRanged(bot)) { diff --git a/src/Ai/Raid/Ulduar/Trigger/RaidUlduarTriggers.cpp b/src/Ai/Raid/Ulduar/Trigger/RaidUlduarTriggers.cpp index f14a513118..a99ea8dbce 100644 --- a/src/Ai/Raid/Ulduar/Trigger/RaidUlduarTriggers.cpp +++ b/src/Ai/Raid/Ulduar/Trigger/RaidUlduarTriggers.cpp @@ -1,6 +1,5 @@ #include "RaidUlduarTriggers.h" -#include "EventMap.h" #include "GameObject.h" #include "Object.h" #include "PlayerbotAI.h" @@ -350,7 +349,6 @@ bool KologarnMarkDpsTargetTrigger::IsActive() if (!target) continue; - uint32 creatureId = target->GetEntry(); if (target->GetEntry() == NPC_RUBBLE && target->IsAlive()) { return true; // Found a rubble to mark @@ -1094,7 +1092,7 @@ bool ThorimPhase2PositioningTrigger::IsActive() Unit* boss = AI_VALUE2(Unit*, "find target", "thorim"); if (!boss || !boss->IsInWorld() || boss->IsDuringRemoveFromWorld()) - return false; + return false; if (!boss->IsAlive()) return false; @@ -1193,17 +1191,14 @@ bool MimironPhase1PositioningTrigger::IsActive() continue; if (target->GetEntry() == NPC_LEVIATHAN_MKII) - { leviathanMkII = target; - } + else if (target->GetEntry() == NPC_VX001) - { return false; - } + else if (target->GetEntry() == NPC_AERIAL_COMMAND_UNIT) - { return false; - } + } if (!leviathanMkII || !leviathanMkII->IsAlive()) @@ -1796,7 +1791,7 @@ Unit* YoggSaronTrigger::GetIllusionRoomRtiTarget() return nullptr; } - uint8 rtiIndex = RtiTargetValue::GetRtiIndex(AI_VALUE(std::string, "rti")); + int32_t rtiIndex = RtiTargetValue::GetRtiIndex(AI_VALUE(std::string, "rti")); if (rtiIndex == -1) { return nullptr; // Invalid RTI mark @@ -1816,21 +1811,16 @@ Unit* YoggSaronTrigger::GetNextIllusionRoomRtiTarget() { float detectionRadius = 0.0f; if (IsInStormwindKeeperIllusion()) - { detectionRadius = ULDUAR_YOGG_SARON_STORMWIND_KEEPER_RADIUS; - } + else if (IsInIcecrownKeeperIllusion()) - { detectionRadius = ULDUAR_YOGG_SARON_ICECROWN_CITADEL_RADIUS; - } + else if (IsInChamberOfTheAspectsIllusion()) - { detectionRadius = ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_RADIUS; - } + else - { return nullptr; - } GuidVector targets = AI_VALUE(GuidVector, "nearest npcs"); @@ -1840,9 +1830,7 @@ Unit* YoggSaronTrigger::GetNextIllusionRoomRtiTarget() { Unit* unit = botAI->GetUnit(guid); if (unit && unit->IsAlive() && unit->GetEntry() == NPC_LAUGHING_SKULL) - { return unit; - } } } @@ -1875,9 +1863,7 @@ Unit* YoggSaronTrigger::GetNextIllusionRoomRtiTarget() { Creature* target = bot->FindNearestCreature(NPC_SUIT_OF_ARMOR, detectionRadius, true); if (target) - { return target; - } } return nullptr; @@ -1886,25 +1872,17 @@ Unit* YoggSaronTrigger::GetNextIllusionRoomRtiTarget() bool YoggSaronOminousCloudCheatTrigger::IsActive() { if (!botAI->HasCheat(BotCheatMask::raid)) - { return false; - } Unit* boss = GetSaraIfAlive(); if (!boss) - { return false; - } if (!botAI->IsBotMainTank(bot)) - { return false; - } if (bot->GetDistance2d(boss->GetPositionX(), boss->GetPositionY()) > 50.0f) - { return false; - } Creature* target = boss->FindNearestCreature(NPC_OMINOUS_CLOUD, 25.0f, true); @@ -1914,14 +1892,10 @@ bool YoggSaronOminousCloudCheatTrigger::IsActive() bool YoggSaronGuardianPositioningTrigger::IsActive() { if (!GetSaraIfAlive()) - { return false; - } if (!botAI->IsTank(bot)) - { return false; - } GuidVector targets = AI_VALUE(GuidVector, "nearest npcs"); bool thereIsAnyGuardian = false; @@ -1930,9 +1904,7 @@ bool YoggSaronGuardianPositioningTrigger::IsActive() { Unit* unit = botAI->GetUnit(guid); if (!unit || !unit->IsAlive()) - { continue; - } if (unit->GetEntry() == NPC_GUARDIAN_OF_YS) { @@ -1940,9 +1912,7 @@ bool YoggSaronGuardianPositioningTrigger::IsActive() ObjectGuid unitTargetGuid = unit->GetTarget(); Player* targetedPlayer = botAI->GetPlayer(unitTargetGuid); if (!targetedPlayer || !botAI->IsTank(targetedPlayer)) - { return false; - } } } @@ -1955,18 +1925,14 @@ bool YoggSaronSanityTrigger::IsActive() Aura* sanityAura = bot->GetAura(SPELL_SANITY); if (!sanityAura) - { return false; - } int sanityAuraStacks = sanityAura->GetStackAmount(); Creature* sanityWell = bot->FindNearestCreature(NPC_SANITY_WELL, 200.0f); if (!sanityWell) - { return false; - } float distanceToSanityWell = bot->GetDistance(sanityWell); @@ -1989,20 +1955,14 @@ bool YoggSaronMaladyOfTheMindTrigger::IsActive() bool YoggSaronMarkTargetTrigger::IsActive() { if (!IsYoggSaronFight()) - { return false; - } if (!botAI->IsBotMainTank(bot)) - { return false; - } Group* group = bot->GetGroup(); if (!group) - { return false; - } if (IsPhase2()) { @@ -2020,9 +1980,7 @@ bool YoggSaronMarkTargetTrigger::IsActive() { nextPossibleTarget = bot->FindNearestCreature(NPC_CORRUPTOR_TENTACLE, 200.0f, true); if (!nextPossibleTarget) - { return false; - } } if (currentSkullTarget) @@ -2030,14 +1988,10 @@ bool YoggSaronMarkTargetTrigger::IsActive() Unit* currentSkullUnit = botAI->GetUnit(currentSkullTarget); if (!currentSkullUnit) - { return true; - } if (currentSkullUnit->IsAlive() && currentSkullUnit->GetGUID() == nextPossibleTarget->GetGUID()) - { return false; - } } return true; @@ -2047,9 +2001,7 @@ bool YoggSaronMarkTargetTrigger::IsActive() ObjectGuid currentSkullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex); Unit* currentSkullUnit = nullptr; if (currentSkullTarget) - { currentSkullUnit = botAI->GetUnit(currentSkullTarget); - } if (currentSkullUnit && (currentSkullUnit->GetEntry() == NPC_IMMORTAL_GUARDIAN || @@ -2064,21 +2016,15 @@ bool YoggSaronMarkTargetTrigger::IsActive() { Unit* unit = botAI->GetUnit(guid); if (!unit || !unit->IsAlive()) - { continue; - } if ((unit->GetEntry() == NPC_IMMORTAL_GUARDIAN || unit->GetEntry() == NPC_MARKED_IMMORTAL_GUARDIAN) && unit->GetHealthPct() > 10) - { return true; - } } if (!currentSkullUnit || currentSkullUnit->GetEntry() != NPC_YOGG_SARON) - { return true; - } return false; } @@ -2096,61 +2042,41 @@ bool YoggSaronBrainLinkTrigger::IsActive() bool YoggSaronMoveToEnterPortalTrigger::IsActive() { if (!IsPhase2()) - { return false; - } Creature* portal = bot->FindNearestCreature(NPC_DESCEND_INTO_MADNESS, 100.0f, true); if (!portal) - { return false; - } if (bot->GetDistance2d(portal->GetPositionX(), portal->GetPositionY()) < 2.0f) - { return false; - } if (AI_VALUE(std::string, "rti") != "skull") - { return false; - } Group* group = bot->GetGroup(); if (!group) - { return false; - } int brainRoomTeamCount = 10; if (bot->GetRaidDifficulty() == Difficulty::RAID_DIFFICULTY_10MAN_NORMAL) - { brainRoomTeamCount = 4; - } if (IsMasterIsInIllusionGroup()) - { brainRoomTeamCount--; - } for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next()) { Player* member = gref->GetSource(); if (!member || !member->IsAlive() || botAI->IsTank(member)) - { continue; - } if (member->GetGUID() == bot->GetGUID()) - { return true; - } brainRoomTeamCount--; if (brainRoomTeamCount == 0) - { break; - } } return false; @@ -2159,20 +2085,15 @@ bool YoggSaronMoveToEnterPortalTrigger::IsActive() bool YoggSaronFallFromFloorTrigger::IsActive() { if (!IsYoggSaronFight()) - { return false; - } std::string rtiMark = AI_VALUE(std::string, "rti"); if (rtiMark == "skull" && bot->GetPositionZ() < ULDUAR_YOGG_SARON_BOSS_ROOM_AXIS_Z_PATHING_ISSUE_DETECT) - { return true; - } + if ((rtiMark == "cross" || rtiMark == "circle" || rtiMark == "star") && bot->GetPositionZ() < ULDUAR_YOGG_SARON_BRAIN_ROOM_AXIS_Z_PATHING_ISSUE_DETECT) - { return true; - } return false; } @@ -2180,44 +2101,31 @@ bool YoggSaronFallFromFloorTrigger::IsActive() bool YoggSaronBossRoomMovementCheatTrigger::IsActive() { if (!IsYoggSaronFight() || !IsPhase2()) - { return false; - } FollowMasterStrategy followMasterStrategy(botAI); if (botAI->HasStrategy(followMasterStrategy.getName(), BotState::BOT_STATE_NON_COMBAT)) - { return true; - } if (!botAI->HasCheat(BotCheatMask::raid)) - { return false; - } if (AI_VALUE(std::string, "rti") != "skull") - { return false; - } Group* group = bot->GetGroup(); if (!group) - { return false; - } + ObjectGuid currentSkullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex); if (!currentSkullTarget) - { return false; - } Unit* currentSkullUnit = botAI->GetUnit(currentSkullTarget); if (!currentSkullUnit || !currentSkullUnit->IsAlive() || bot->GetDistance2d(currentSkullUnit->GetPositionX(), currentSkullUnit->GetPositionY()) < 40.0f) - { return false; - } return true; } @@ -2225,14 +2133,10 @@ bool YoggSaronBossRoomMovementCheatTrigger::IsActive() bool YoggSaronUsePortalTrigger::IsActive() { if (!IsPhase2()) - { return false; - } if (AI_VALUE(std::string, "rti") != "diamond") - { return false; - } return bot->FindNearestCreature(NPC_DESCEND_INTO_MADNESS, 2.0f, true) != nullptr; } @@ -2240,24 +2144,16 @@ bool YoggSaronUsePortalTrigger::IsActive() bool YoggSaronIllusionRoomTrigger::IsActive() { if (!IsYoggSaronFight() || !IsInIllusionRoom() || AI_VALUE(std::string, "rti") == "square") - { return false; - } if (SetRtiMarkRequired()) - { return true; - } if (SetRtiTargetRequired()) - { return true; - } if (GoToBrainRoomRequired()) - { return true; - } return false; } @@ -2265,9 +2161,7 @@ bool YoggSaronIllusionRoomTrigger::IsActive() bool YoggSaronIllusionRoomTrigger::GoToBrainRoomRequired() { if (AI_VALUE(std::string, "rti") == "square") - { return false; - } return IsMasterIsInBrainRoom(); } @@ -2281,9 +2175,7 @@ bool YoggSaronIllusionRoomTrigger::SetRtiTargetRequired() { Unit const* currentRtiTarget = GetIllusionRoomRtiTarget(); if (currentRtiTarget != nullptr) - { return false; - } return GetNextIllusionRoomRtiTarget() != nullptr; } @@ -2291,15 +2183,11 @@ bool YoggSaronIllusionRoomTrigger::SetRtiTargetRequired() bool YoggSaronMoveToExitPortalTrigger::IsActive() { if (!IsYoggSaronFight() || !IsInBrainLevel()) - { return false; - } Creature const* brain = bot->FindNearestCreature(NPC_BRAIN, 60.0f, true); if (!brain || !brain->IsAlive()) - { return false; - } if (brain->HasUnitState(UNIT_STATE_CASTING)) { @@ -2316,9 +2204,7 @@ bool YoggSaronMoveToExitPortalTrigger::IsActive() } } else if (brain->GetHealth() < brain->GetMaxHealth() * 0.3f) - { return true; - } return false; } @@ -2331,9 +2217,7 @@ bool YoggSaronLunaticGazeTrigger::IsActive() { Spell* currentSpell = yoggsaron->GetCurrentSpell(CURRENT_CHANNELED_SPELL); if (currentSpell && currentSpell->m_spellInfo->Id == SPELL_LUNATIC_GAZE_YS) - { return true; - } } return false; @@ -2342,15 +2226,11 @@ bool YoggSaronLunaticGazeTrigger::IsActive() bool YoggSaronPhase3PositioningTrigger::IsActive() { if (!IsYoggSaronFight() || !IsPhase3()) - { return false; - } YoggSaronSanityTrigger sanityTrigger(botAI); if (sanityTrigger.IsActive()) - { return false; - } if (botAI->IsRanged(bot) && bot->GetDistance2d(ULDUAR_YOGG_SARON_PHASE_3_RANGED_SPOT.GetPositionX(), ULDUAR_YOGG_SARON_PHASE_3_RANGED_SPOT.GetPositionY()) > 15.0f) @@ -2368,9 +2248,7 @@ bool YoggSaronPhase3PositioningTrigger::IsActive() if (botAI->IsTank(bot)) { if (bot->GetDistance(ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT) > 30.0f) - { return true; - } GuidVector targets = AI_VALUE(GuidVector, "nearest npcs"); bool thereIsAnyGuardian = false; @@ -2379,9 +2257,7 @@ bool YoggSaronPhase3PositioningTrigger::IsActive() { Unit* unit = botAI->GetUnit(guid); if (!unit || !unit->IsAlive()) - { continue; - } if (unit->GetEntry() == NPC_IMMORTAL_GUARDIAN || unit->GetEntry() == NPC_MARKED_IMMORTAL_GUARDIAN) { @@ -2389,9 +2265,7 @@ bool YoggSaronPhase3PositioningTrigger::IsActive() ObjectGuid unitTargetGuid = unit->GetTarget(); Player* targetedPlayer = botAI->GetPlayer(unitTargetGuid); if (!targetedPlayer || !botAI->IsTank(targetedPlayer)) - { return false; - } } } diff --git a/src/Ai/Raid/VaultOfArchavon/Action/RaidVoAActions.cpp b/src/Ai/Raid/VaultOfArchavon/Action/RaidVoAActions.cpp index 05d6328e5c..892ddf3eb4 100644 --- a/src/Ai/Raid/VaultOfArchavon/Action/RaidVoAActions.cpp +++ b/src/Ai/Raid/VaultOfArchavon/Action/RaidVoAActions.cpp @@ -10,7 +10,7 @@ const Position VOA_EMALON_RESTORE_POSITION = Position(-221.8f, -243.8f, 96.8f, 4.7f); -bool EmalonMarkBossAction::Execute(Event event) +bool EmalonMarkBossAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "emalon the storm watcher"); if (!boss || !boss->IsAlive()) @@ -72,7 +72,7 @@ bool EmalonMarkBossAction::isUseful() return emalonMarkBossTrigger.IsActive(); } -bool EmalonLightingNovaAction::Execute(Event event) +bool EmalonLightingNovaAction::Execute(Event /*event*/) { const float radius = 25.0f; // 20 yards + 5 yard for safety for 10 man. For 25man there is no maximum range but 25 yards should be ok @@ -96,7 +96,7 @@ bool EmalonLightingNovaAction::isUseful() return emalonLightingNovaTrigger.IsActive(); } -bool EmalonOverchargeAction::Execute(Event event) +bool EmalonOverchargeAction::Execute(Event /*event*/) { // Check if there is any overcharged minion Unit* minion = nullptr; @@ -173,7 +173,7 @@ bool EmalonOverchargeAction::isUseful() return emalonOverchargeTrigger.IsActive(); } -bool EmalonFallFromFloorAction::Execute(Event event) +bool EmalonFallFromFloorAction::Execute(Event /*event*/) { return bot->TeleportTo(bot->GetMapId(), VOA_EMALON_RESTORE_POSITION.GetPositionX(), VOA_EMALON_RESTORE_POSITION.GetPositionY(), VOA_EMALON_RESTORE_POSITION.GetPositionZ(), diff --git a/src/Ai/World/Rpg/Action/NewRpgAction.cpp b/src/Ai/World/Rpg/Action/NewRpgAction.cpp index 2c42741c88..6ccb845481 100644 --- a/src/Ai/World/Rpg/Action/NewRpgAction.cpp +++ b/src/Ai/World/Rpg/Action/NewRpgAction.cpp @@ -1,12 +1,10 @@ #include "NewRpgAction.h" #include -#include #include #include "BroadcastHelper.h" #include "ChatHelper.h" -#include "DBCStores.h" #include "G3D/Vector2.h" #include "GossipDef.h" #include "IVMapMgr.h" @@ -20,16 +18,11 @@ #include "PathGenerator.h" #include "Player.h" #include "PlayerbotAI.h" -#include "Playerbots.h" -#include "Position.h" #include "QuestDef.h" #include "Random.h" -#include "RandomPlayerbotMgr.h" #include "SharedDefines.h" -#include "StatsWeightCalculator.h" #include "Timer.h" #include "TravelMgr.h" -#include "World.h" bool TellRpgStatusAction::Execute(Event event) { @@ -61,7 +54,7 @@ bool StartRpgDoQuestAction::Execute(Event event) return false; } -bool NewRpgStatusUpdateAction::Execute(Event event) +bool NewRpgStatusUpdateAction::Execute(Event /*event*/) { NewRpgInfo& info = botAI->rpgInfo; NewRpgStatus status = info.GetStatus(); @@ -153,7 +146,7 @@ bool NewRpgStatusUpdateAction::Execute(Event event) return false; } -bool NewRpgGoGrindAction::Execute(Event event) +bool NewRpgGoGrindAction::Execute(Event /*event*/) { if (SearchQuestGiverAndAcceptOrReward()) return true; @@ -163,7 +156,7 @@ bool NewRpgGoGrindAction::Execute(Event event) return false; } -bool NewRpgGoCampAction::Execute(Event event) +bool NewRpgGoCampAction::Execute(Event /*event*/) { if (SearchQuestGiverAndAcceptOrReward()) return true; @@ -174,7 +167,7 @@ bool NewRpgGoCampAction::Execute(Event event) return false; } -bool NewRpgWanderRandomAction::Execute(Event event) +bool NewRpgWanderRandomAction::Execute(Event /*event*/) { if (SearchQuestGiverAndAcceptOrReward()) return true; @@ -182,7 +175,7 @@ bool NewRpgWanderRandomAction::Execute(Event event) return MoveRandomNear(); } -bool NewRpgWanderNpcAction::Execute(Event event) +bool NewRpgWanderNpcAction::Execute(Event /*event*/) { NewRpgInfo& info = botAI->rpgInfo; auto* dataPtr = std::get_if(&info.data); @@ -227,7 +220,7 @@ bool NewRpgWanderNpcAction::Execute(Event event) return true; } -bool NewRpgDoQuestAction::Execute(Event event) +bool NewRpgDoQuestAction::Execute(Event /*event*/) { if (SearchQuestGiverAndAcceptOrReward()) return true; @@ -423,7 +416,7 @@ bool NewRpgDoQuestAction::DoCompletedQuest(NewRpgInfo::DoQuest& data) return false; } -bool NewRpgTravelFlightAction::Execute(Event event) +bool NewRpgTravelFlightAction::Execute(Event /*event*/) { NewRpgInfo& info = botAI->rpgInfo; auto* dataPtr = std::get_if(&info.data); diff --git a/src/Ai/World/Rpg/Strategy/NewRpgStrategy.cpp b/src/Ai/World/Rpg/Strategy/NewRpgStrategy.cpp index 030ad6b4fc..f0d0ce5a92 100644 --- a/src/Ai/World/Rpg/Strategy/NewRpgStrategy.cpp +++ b/src/Ai/World/Rpg/Strategy/NewRpgStrategy.cpp @@ -5,8 +5,6 @@ #include "NewRpgStrategy.h" -#include "Playerbots.h" - NewRpgStrategy::NewRpgStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} std::vector NewRpgStrategy::getDefaultActions() @@ -69,7 +67,6 @@ void NewRpgStrategy::InitTriggers(std::vector& triggers) ); } -void NewRpgStrategy::InitMultipliers(std::vector& multipliers) +void NewRpgStrategy::InitMultipliers(std::vector&) { - } diff --git a/src/Bot/Cmd/PlayerbotCommandServer.cpp b/src/Bot/Cmd/PlayerbotCommandServer.cpp index 1532cc0543..7f3de21a2d 100644 --- a/src/Bot/Cmd/PlayerbotCommandServer.cpp +++ b/src/Bot/Cmd/PlayerbotCommandServer.cpp @@ -10,10 +10,9 @@ #include #include #include -#include +#include "RandomPlayerbotMgr.h" #include "IoContext.h" -#include "Playerbots.h" using boost::asio::ip::tcp; typedef boost::shared_ptr socket_ptr; @@ -48,7 +47,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 = RandomPlayerbotMgr::instance().HandleRemoteCommand(request) + "\n"; boost::asio::write(*sock, boost::asio::buffer(response.c_str(), response.size())); request = ""; } diff --git a/src/Bot/Engine/Action/Action.h b/src/Bot/Engine/Action/Action.h index 6c54d24b97..084a40a552 100644 --- a/src/Bot/Engine/Action/Action.h +++ b/src/Bot/Engine/Action/Action.h @@ -6,7 +6,6 @@ #pragma once #include "AiObject.h" -#include "Common.h" #include "Event.h" #include "Value.h" diff --git a/src/Bot/Engine/Strategy/CustomStrategy.cpp b/src/Bot/Engine/Strategy/CustomStrategy.cpp index d4dd3e514e..65bf411d92 100644 --- a/src/Bot/Engine/Strategy/CustomStrategy.cpp +++ b/src/Bot/Engine/Strategy/CustomStrategy.cpp @@ -36,9 +36,7 @@ std::vector toNextActionArray(const std::string actions) std::vector res = {}; for (const std::string token : tokens) - { res.push_back(toNextAction(token)); - } return res; } diff --git a/src/Bot/Engine/Trigger/Trigger.cpp b/src/Bot/Engine/Trigger/Trigger.cpp index 07105be299..bfd8eeaed5 100644 --- a/src/Bot/Engine/Trigger/Trigger.cpp +++ b/src/Bot/Engine/Trigger/Trigger.cpp @@ -5,9 +5,8 @@ #include "Trigger.h" +#include "AiObjectContext.h" #include "Event.h" -#include "Playerbots.h" -#include "Timer.h" Trigger::Trigger(PlayerbotAI* botAI, std::string const name, int32 checkInterval) : AiNamedObject(botAI, name), diff --git a/src/Bot/Engine/Trigger/Trigger.h b/src/Bot/Engine/Trigger/Trigger.h index d32845dc52..8878530f8d 100644 --- a/src/Bot/Engine/Trigger/Trigger.h +++ b/src/Bot/Engine/Trigger/Trigger.h @@ -6,7 +6,6 @@ #pragma once #include "Action.h" -#include "Common.h" class PlayerbotAI; class Unit; diff --git a/src/Bot/Factory/AiFactory.h b/src/Bot/Factory/AiFactory.h index 3d11d80aeb..b853bf6705 100644 --- a/src/Bot/Factory/AiFactory.h +++ b/src/Bot/Factory/AiFactory.h @@ -7,7 +7,6 @@ #define _PLAYERBOT_AIFACTORY_H #include - #include "Common.h" class AiObjectContext; diff --git a/src/Bot/Factory/PlayerbotFactory.cpp b/src/Bot/Factory/PlayerbotFactory.cpp index ca1f4d1800..f502605773 100644 --- a/src/Bot/Factory/PlayerbotFactory.cpp +++ b/src/Bot/Factory/PlayerbotFactory.cpp @@ -5,7 +5,6 @@ #include "PlayerbotFactory.h" -#include #include #include "AccountMgr.h" @@ -20,9 +19,7 @@ #include "ItemTemplate.h" #include "ItemVisitors.h" #include "Log.h" -#include "LogCommon.h" #include "LootMgr.h" -#include "MapMgr.h" #include "ObjectMgr.h" #include "PerfMonitor.h" #include "PetDefines.h" @@ -37,7 +34,6 @@ #include "RandomPlayerbotFactory.h" #include "ReputationMgr.h" #include "SharedDefines.h" -#include "SpellAuraDefines.h" #include "StatsWeightCalculator.h" #include "World.h" #include "AiObjectContext.h" @@ -241,19 +237,17 @@ void PlayerbotFactory::Randomize(bool incremental) Prepare(); LOG_DEBUG("playerbots", "Resetting player..."); PerfMonitorOperation* pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "PlayerbotFactory_Reset"); - if (!sPlayerbotAIConfig.equipmentPersistence || level < sPlayerbotAIConfig.equipmentPersistenceLevel) - { + + if (!PlayerbotAIConfig::instance().equipmentPersistence || level < PlayerbotAIConfig::instance().equipmentPersistenceLevel) bot->resetTalents(true); - } + if (!incremental) { ClearSkills(); ClearSpells(); ResetQuests(); - if (!sPlayerbotAIConfig.equipmentPersistence || level < sPlayerbotAIConfig.equipmentPersistenceLevel) - { + if (!PlayerbotAIConfig::instance().equipmentPersistence || level < PlayerbotAIConfig::instance().equipmentPersistenceLevel) ClearAllItems(); - } } ClearInventory(); bot->RemoveAllSpellCooldown(); @@ -1787,9 +1781,8 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) int32 desiredQuality = itemQuality; if (urand(0, 100) < 100 * sPlayerbotAIConfig.randomGearLoweringChance && desiredQuality > ITEM_QUALITY_NORMAL) - { desiredQuality--; - } + do { for (uint32 requiredLevel = bot->GetLevel(); requiredLevel > std::max((int32)bot->GetLevel() - delta, 0); @@ -1973,14 +1966,12 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) } if (bestItemForSlot == 0) - { continue; - } + uint16 dest; if (!CanEquipUnseenItem(slot, dest, bestItemForSlot)) - { continue; - } + Item* newItem = bot->EquipNewItem(dest, bestItemForSlot, true); bot->AutoUnequipOffhandIfNeed(); // if (newItem) @@ -2143,21 +2134,18 @@ void PlayerbotFactory::InitBags(bool destroyOld) uint32 newItemId = 51809; Item* old_bag = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); if (old_bag && old_bag->GetTemplate()->ItemId == newItemId) - { continue; - } + uint16 dest; if (!CanEquipUnseenItem(slot, dest, newItemId)) continue; if (old_bag && destroyOld) - { bot->DestroyItem(INVENTORY_SLOT_BAG_0, slot, true); - } + if (old_bag) - { continue; - } + Item* newItem = bot->EquipNewItem(dest, newItemId, true); // if (newItem) // { diff --git a/src/Bot/Factory/RandomPlayerbotFactory.cpp b/src/Bot/Factory/RandomPlayerbotFactory.cpp index e2e6ffb11a..908bd38993 100644 --- a/src/Bot/Factory/RandomPlayerbotFactory.cpp +++ b/src/Bot/Factory/RandomPlayerbotFactory.cpp @@ -8,17 +8,12 @@ #include "AccountMgr.h" #include "ArenaTeamMgr.h" #include "DatabaseEnv.h" -#include "GuildMgr.h" -#include "PlayerbotFactory.h" -#include "Playerbots.h" -#include "PlayerbotGuildMgr.h" +#include "PlayerbotAI.h" #include "ScriptMgr.h" #include "SharedDefines.h" #include "SocialMgr.h" #include "Timer.h" -#include "Guild.h" // EmblemInfo::SaveToDB #include "Log.h" -#include "GuildMgr.h" constexpr RandomPlayerbotFactory::NameRaceAndGender RandomPlayerbotFactory::CombineRaceAndGender(uint8 race, uint8 gender) diff --git a/src/Bot/PlayerbotAI.cpp b/src/Bot/PlayerbotAI.cpp index c8fc4cb303..b6afb1c0a8 100644 --- a/src/Bot/PlayerbotAI.cpp +++ b/src/Bot/PlayerbotAI.cpp @@ -24,7 +24,6 @@ #include "GameObjectData.h" #include "GameTime.h" #include "GuildMgr.h" -#include "GuildTaskMgr.h" #include "LFGMgr.h" #include "LastMovementValue.h" #include "LastSpellCastValue.h" @@ -39,7 +38,6 @@ #include "PerfMonitor.h" #include "Player.h" #include "PlayerbotAIConfig.h" -#include "PlayerbotRepository.h" #include "PlayerbotMgr.h" #include "PlayerbotGuildMgr.h" #include "Playerbots.h" diff --git a/src/Bot/PlayerbotAI.h b/src/Bot/PlayerbotAI.h index 3364b31ac5..05710548d1 100644 --- a/src/Bot/PlayerbotAI.h +++ b/src/Bot/PlayerbotAI.h @@ -6,13 +6,11 @@ #ifndef _PLAYERBOT_PLAYERbotAI_H #define _PLAYERBOT_PLAYERbotAI_H -#include #include #include "Chat.h" #include "ChatFilter.h" #include "ChatHelper.h" -#include "Common.h" #include "CreatureData.h" #include "Event.h" #include "Item.h" diff --git a/src/Bot/PlayerbotMgr.cpp b/src/Bot/PlayerbotMgr.cpp index 79850ab24e..03ee1a8fc9 100644 --- a/src/Bot/PlayerbotMgr.cpp +++ b/src/Bot/PlayerbotMgr.cpp @@ -7,7 +7,6 @@ #include #include -#include #include #include #include @@ -20,11 +19,9 @@ #include "Common.h" #include "Define.h" #include "Group.h" -#include "GroupMgr.h" #include "GuildMgr.h" #include "ObjectAccessor.h" #include "ObjectGuid.h" -#include "ObjectMgr.h" #include "PlayerbotAIConfig.h" #include "PlayerbotRepository.h" #include "PlayerbotFactory.h" @@ -126,7 +123,7 @@ void PlayerbotHolder::AddPlayerBot(ObjectGuid playerGuid, uint32 masterAccountId return; } uint32 count = mgr->GetPlayerbotsCount() + botLoading.size(); - if (count >= sPlayerbotAIConfig.maxAddedBots) + if (count >= PlayerbotAIConfig::instance().maxAddedBots) { allowed = false; out << "Failure: You have added too many bots (more than " << sPlayerbotAIConfig.maxAddedBots << ")"; @@ -710,12 +707,11 @@ 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) + bool admin, uint32 masterAccountId, uint32) { 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 isMasterAccount = (masterAccountId == botAccount); //not used, line marked for removal. @@ -730,13 +726,15 @@ std::string const PlayerbotHolder::ProcessBotCommand(std::string const cmd, Obje { uint32 accountId = sCharacterCache->GetCharacterAccountIdByGuid(guid); if (!accountId) + { return "character not found"; + } - if (!sPlayerbotAIConfig.allowAccountBots && accountId != masterAccountId && - !(sPlayerbotAIConfig.allowTrustedAccountBots && IsAccountLinked(accountId, masterAccountId))) - { - return "you can only add bots from your own account or linked accounts"; - } + if (!sPlayerbotAIConfig.allowAccountBots && accountId != masterAccountId && + !(sPlayerbotAIConfig.allowTrustedAccountBots && IsAccountLinked(accountId, masterAccountId))) + { + return "you can only add bots from your own account or linked accounts"; + } } AddPlayerBot(guid, masterAccountId); @@ -1462,7 +1460,7 @@ std::string const PlayerbotHolder::ListBots(Player* master) return out.str(); } -std::string const PlayerbotHolder::LookupBots(Player* master) +std::string const PlayerbotHolder::LookupBots(Player*) { std::list messages; messages.push_back("Classes Available:"); diff --git a/src/Bot/RandomPlayerbotMgr.cpp b/src/Bot/RandomPlayerbotMgr.cpp index 6cdf1fcaeb..412c27a960 100644 --- a/src/Bot/RandomPlayerbotMgr.cpp +++ b/src/Bot/RandomPlayerbotMgr.cpp @@ -25,7 +25,6 @@ #include "FleeManager.h" #include "FlightMasterCache.h" #include "GridNotifiers.h" -#include "GuildTaskMgr.h" #include "LFGMgr.h" #include "MapMgr.h" #include "NewRpgInfo.h" @@ -3211,40 +3210,38 @@ void RandomPlayerbotMgr::PrintStats() //++revive; } if (bot->IsInCombat()) - { ++combat; - } + if (bot->isMoving()) - { ++moving; - } + if (bot->IsInFlight()) - { ++inFlight; - } + if (bot->IsMounted()) - { ++mounted; - } + if (bot->InBattleground() || bot->InArena()) - { ++inBg; - } + if (bot->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING)) - { ++rest; - } + if (botAI->GetState() == BOT_STATE_NON_COMBAT) ++engine_noncombat; + else if (botAI->GetState() == BOT_STATE_COMBAT) ++engine_combat; + else ++engine_dead; if (botAI->IsHeal(bot, true)) ++heal; + else if (botAI->IsTank(bot, true)) ++tank; + else ++dps; diff --git a/src/Db/PlayerbotRepository.cpp b/src/Db/PlayerbotRepository.cpp index 886cebb833..731534edd6 100644 --- a/src/Db/PlayerbotRepository.cpp +++ b/src/Db/PlayerbotRepository.cpp @@ -4,10 +4,7 @@ */ #include "PlayerbotRepository.h" - -#include - -#include "Playerbots.h" +#include "AiObjectContext.h" void PlayerbotRepository::Load(PlayerbotAI* botAI) { diff --git a/src/Mgr/Item/RandomItemMgr.cpp b/src/Mgr/Item/RandomItemMgr.cpp index 4abd2eaa9d..71fbb9f79a 100644 --- a/src/Mgr/Item/RandomItemMgr.cpp +++ b/src/Mgr/Item/RandomItemMgr.cpp @@ -2771,9 +2771,8 @@ inline bool IsCraftedBySpellInfo(ItemTemplate const* proto, SpellInfo const* spe } if (proto->ItemId == spellInfo->Reagent[x]) - { return true; - } + } for (uint8 i = 0; i < 3; ++i) @@ -2781,9 +2780,7 @@ inline bool IsCraftedBySpellInfo(ItemTemplate const* proto, SpellInfo const* spe if (spellInfo->Effects[i].Effect == SPELL_EFFECT_CREATE_ITEM) { if (spellInfo->Effects[i].ItemType == proto->ItemId) - { return true; - } } } diff --git a/src/Mgr/Item/StatsCollector.cpp b/src/Mgr/Item/StatsCollector.cpp index 4f719fea4e..7818dc7386 100644 --- a/src/Mgr/Item/StatsCollector.cpp +++ b/src/Mgr/Item/StatsCollector.cpp @@ -1,18 +1,13 @@ #include "StatsCollector.h" -#include - #include "DBCStores.h" -#include "ItemEnchantmentMgr.h" #include "ItemTemplate.h" -#include "ObjectMgr.h" #include "PlayerbotAI.h" #include "PlayerbotAIAware.h" #include "SharedDefines.h" #include "SpellAuraDefines.h" #include "SpellInfo.h" #include "SpellMgr.h" -#include "UpdateFields.h" #include "Util.h" StatsCollector::StatsCollector(CollectorType type, int32 cls) : type_(type), cls_(cls) { Reset(); } diff --git a/src/Mgr/Talent/Talentspec.cpp b/src/Mgr/Talent/Talentspec.cpp index df647baf10..7ee760ed46 100644 --- a/src/Mgr/Talent/Talentspec.cpp +++ b/src/Mgr/Talent/Talentspec.cpp @@ -6,7 +6,9 @@ #include "Talentspec.h" #include "Event.h" -#include "Playerbots.h" +#include "Player.h" +#include "SpellMgr.h" +#include "World.h" uint32 TalentSpec::TalentListEntry::tabPage() const { diff --git a/src/Mgr/Talent/Talentspec.h b/src/Mgr/Talent/Talentspec.h index 8cb63e44d9..83243be036 100644 --- a/src/Mgr/Talent/Talentspec.h +++ b/src/Mgr/Talent/Talentspec.h @@ -6,7 +6,9 @@ #ifndef _PLAYERBOT_TALENTSPEC_H #define _PLAYERBOT_TALENTSPEC_H -#include "Action.h" +#include +#include +#include "Player.h" struct TalentEntry; struct TalentTabEntry; diff --git a/src/Mgr/Travel/TravelMgr.cpp b/src/Mgr/Travel/TravelMgr.cpp index 1a390d5041..2909df5e50 100644 --- a/src/Mgr/Travel/TravelMgr.cpp +++ b/src/Mgr/Travel/TravelMgr.cpp @@ -1246,9 +1246,8 @@ bool RpgTravelDestination::isActive(Player* bot) for (ObjectGuid const guid : ignoreList) { if (guid.GetEntry() == getEntry()) - { return false; - } + } FactionTemplateEntry const* factionEntry = sFactionTemplateStore.LookupEntry(cInfo->faction); diff --git a/src/Script/Playerbots.cpp b/src/Script/Playerbots.cpp index c4487f8986..94b8eb1b09 100644 --- a/src/Script/Playerbots.cpp +++ b/src/Script/Playerbots.cpp @@ -71,9 +71,7 @@ class PlayerbotsDatabaseScript : public DatabaseScript } if (revision.empty()) - { revision = "Unknown Playerbots Database Revision"; - } } }; @@ -217,16 +215,12 @@ class PlayerbotsPlayerScript : public PlayerScript Player* const member = itr->GetSource(); if (member == nullptr) - { continue; - } PlayerbotAI* const botAI = PlayerbotsMgr::instance().GetPlayerbotAI(member); if (botAI == nullptr) - { continue; - } botAI->HandleCommand(type, msg, player); } @@ -237,30 +231,22 @@ class PlayerbotsPlayerScript : public PlayerScript bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Guild* guild) override { if (type != CHAT_MSG_GUILD) - { 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) - { continue; - } if (bot->GetGuildId() != player->GetGuildId()) - { continue; - } PlayerbotsMgr::instance().GetPlayerbotAI(bot)->HandleCommand(type, msg, player); } @@ -273,9 +259,7 @@ class PlayerbotsPlayerScript : public PlayerScript PlayerbotMgr* const playerbotMgr = PlayerbotsMgr::instance().GetPlayerbotMgr(player); if (playerbotMgr != nullptr && channel->GetFlags() & 0x18) - { playerbotMgr->HandleCommand(type, msg); - } sRandomPlayerbotMgr.HandleCommand(type, msg, player); @@ -310,14 +294,10 @@ class PlayerbotsPlayerScript : public PlayerScript { Player* member = gref->GetSource(); if (!member) - { continue; - } if (!member->GetSession()->IsBot()) - { return; - } } } @@ -336,14 +316,10 @@ class PlayerbotsMiscScript : public MiscScript PlayerbotAI* botAI = PlayerbotsMgr::instance().GetPlayerbotAI(player); if (botAI != nullptr) - { delete botAI; - } if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player)) - { delete playerbotMgr; - } } }; @@ -441,14 +417,10 @@ class PlayerbotsScript : public PlayerbotScript void OnPlayerbotCheckPetitionAccount(Player* player, bool& found) override { if (!found) - { return; - } if (PlayerbotsMgr::instance().GetPlayerbotAI(player) != nullptr) - { found = false; - } } bool OnPlayerbotCheckUpdatesToSend(Player* player) override @@ -456,9 +428,7 @@ class PlayerbotsScript : public PlayerbotScript PlayerbotAI* botAI = PlayerbotsMgr::instance().GetPlayerbotAI(player); if (botAI == nullptr) - { return true; - } return botAI->IsRealPlayer(); } @@ -466,21 +436,15 @@ class PlayerbotsScript : public PlayerbotScript void OnPlayerbotPacketSent(Player* player, WorldPacket const* packet) override { if (player == nullptr) - { return; - } PlayerbotAI* botAI = PlayerbotsMgr::instance().GetPlayerbotAI(player); if (botAI != nullptr) - { botAI->HandleBotOutgoingPacket(*packet); - } if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player)) - { playerbotMgr->HandleMasterOutgoingPacket(*packet); - } } void OnPlayerbotUpdate(uint32 diff) override diff --git a/src/Script/WorldThr/PlayerbotWorldThreadProcessor.h b/src/Script/WorldThr/PlayerbotWorldThreadProcessor.h index e975b8fbdb..bd489829ee 100644 --- a/src/Script/WorldThr/PlayerbotWorldThreadProcessor.h +++ b/src/Script/WorldThr/PlayerbotWorldThreadProcessor.h @@ -11,7 +11,6 @@ #include #include "Log.h" - #include "PlayerbotOperation.h" /** From 3c62a45fad0827b0d0279e9f92f18ab05f9ede4a Mon Sep 17 00:00:00 2001 From: bash Date: Sun, 15 Feb 2026 20:47:26 +0100 Subject: [PATCH 23/25] Revert "Convert PlayerBots tables to InnoDB (#2083)" This reverts commit c86032f43b7bccb861c33769385db7a522e75513. --- .../updates/2026_01_30_00_change_to_InnoDB.sql | 9 --------- .../updates/2026_01_30_00_change_to_InnoDB.sql | 18 ------------------ 2 files changed, 27 deletions(-) delete mode 100644 data/sql/characters/updates/2026_01_30_00_change_to_InnoDB.sql delete 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 deleted file mode 100644 index 8c2c226b51..0000000000 --- a/data/sql/characters/updates/2026_01_30_00_change_to_InnoDB.sql +++ /dev/null @@ -1,9 +0,0 @@ --- 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 deleted file mode 100644 index c7f9c51f0a..0000000000 --- a/data/sql/playerbots/updates/2026_01_30_00_change_to_InnoDB.sql +++ /dev/null @@ -1,18 +0,0 @@ --- 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 d1cac8d0273f763d2a576293920cc71231b0b408 Mon Sep 17 00:00:00 2001 From: Keleborn <22352763+Celandriel@users.noreply.github.com> Date: Sun, 15 Feb 2026 16:29:20 -0800 Subject: [PATCH 24/25] Bug fix. Equip Action triggered action (#2142) # Pull Request Brighton caught a mistake I made changeing the action registry, so the correct action was no longer triggering. I cleaned that up, and renamed the action. ## How to Test the Changes - This was tested by adding logging to both equip actions. But to test this without that, the best way to verify the fix is to stop alts from auto upgrading via config. Then they should correctly follow the configured behavior. ## Complexity & Impact Does this change add new decision branches? - - [x ] No - - [ ] Yes (**explain below**) Does this change increase per-bot or per-tick processing? - - [x] No - - [ ] Yes (**describe and justify impact**) Could this logic scale poorly under load? - - [x] No - - [ ] Yes (**explain why**) --- ## Defaults & Configuration Does this change modify default bot behavior? - - [x] No - - [ ] Yes (**explain why**) If this introduces more advanced or AI-heavy logic: - - [x] Lightweight mode remains the default - - [ ] More complex behavior is optional and thereby configurable --- ## AI Assistance Was AI assistance (e.g. ChatGPT or similar tools) used while working on this change? - - [x] No - - [ ] Yes (**explain below**) If yes, please specify: - 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 - - [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. --- src/Ai/Base/Actions/BuyAction.cpp | 4 ++-- src/Ai/Base/Actions/EquipAction.cpp | 8 ++++---- src/Ai/Base/Actions/EquipAction.h | 4 ++-- src/Ai/Base/ChatActionContext.h | 1 - src/Ai/Base/Strategy/NonCombatStrategy.cpp | 2 +- src/Ai/Base/Strategy/WorldPacketHandlerStrategy.cpp | 6 +++--- src/Ai/Base/WorldPacketActionContext.h | 2 ++ .../Hunter/Strategy/GenericHunterNonCombatStrategy.cpp | 2 +- src/Ai/Class/Hunter/Strategy/GenericHunterStrategy.cpp | 2 +- 9 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/Ai/Base/Actions/BuyAction.cpp b/src/Ai/Base/Actions/BuyAction.cpp index 1e8ec7e5c4..f0729f2518 100644 --- a/src/Ai/Base/Actions/BuyAction.cpp +++ b/src/Ai/Base/Actions/BuyAction.cpp @@ -175,7 +175,7 @@ bool BuyAction::Execute(Event event) if (needMoneyFor == NeedMoneyFor::gear) { - botAI->DoSpecificAction("equip upgrades"); + botAI->DoSpecificAction("equip upgrades packet action"); } } } @@ -206,7 +206,7 @@ bool BuyAction::Execute(Event event) if (usage == ITEM_USAGE_REPLACE || usage == ITEM_USAGE_EQUIP || usage == ITEM_USAGE_BAD_EQUIP || usage == ITEM_USAGE_BROKEN_EQUIP) { - botAI->DoSpecificAction("equip upgrades"); + botAI->DoSpecificAction("equip upgrades packet action"); break; } } diff --git a/src/Ai/Base/Actions/EquipAction.cpp b/src/Ai/Base/Actions/EquipAction.cpp index 5c91c13c8a..d6900391f0 100644 --- a/src/Ai/Base/Actions/EquipAction.cpp +++ b/src/Ai/Base/Actions/EquipAction.cpp @@ -364,12 +364,12 @@ ItemIds EquipAction::SelectInventoryItemsToEquip() return items; } -bool EquipUpgradesTriggeredAction::Execute(Event event) +bool EquipUpgradesPacketAction::Execute(Event event) { if (!sPlayerbotAIConfig.autoEquipUpgradeLoot && !sRandomPlayerbotMgr.IsRandomBot(bot)) return false; - - if (event.GetSource() == "trade status") + std::string const source = event.GetSource(); + if (source == "trade status") { WorldPacket p(event.getPacket()); p.rpos(0); @@ -380,7 +380,7 @@ bool EquipUpgradesTriggeredAction::Execute(Event event) return false; } - if (event.GetSource() == "item push result") + else if (source == "item push result") { WorldPacket p(event.getPacket()); p.rpos(0); diff --git a/src/Ai/Base/Actions/EquipAction.h b/src/Ai/Base/Actions/EquipAction.h index 4f84f942bd..bf609d8611 100644 --- a/src/Ai/Base/Actions/EquipAction.h +++ b/src/Ai/Base/Actions/EquipAction.h @@ -29,10 +29,10 @@ class EquipAction : public InventoryAction void EquipItem(Item* item); }; -class EquipUpgradesTriggeredAction : public EquipAction +class EquipUpgradesPacketAction : public EquipAction { public: - explicit EquipUpgradesTriggeredAction(PlayerbotAI* botAI, std::string const name = "equip upgrades") : EquipAction(botAI, name) {} + explicit EquipUpgradesPacketAction(PlayerbotAI* botAI, std::string const name = "equip upgrades packet action") : EquipAction(botAI, name) {} bool Execute(Event event) override; }; diff --git a/src/Ai/Base/ChatActionContext.h b/src/Ai/Base/ChatActionContext.h index 8e4ed9ccf8..4bfdcb043a 100644 --- a/src/Ai/Base/ChatActionContext.h +++ b/src/Ai/Base/ChatActionContext.h @@ -120,7 +120,6 @@ class ChatActionContext : public NamedObjectContext creators["use"] = &ChatActionContext::use; creators["item count"] = &ChatActionContext::item_count; creators["equip"] = &ChatActionContext::equip; - creators["equip upgrades"] = &ChatActionContext::equip_upgrade; creators["unequip"] = &ChatActionContext::unequip; creators["sell"] = &ChatActionContext::sell; creators["buy"] = &ChatActionContext::buy; diff --git a/src/Ai/Base/Strategy/NonCombatStrategy.cpp b/src/Ai/Base/Strategy/NonCombatStrategy.cpp index 136dcc53e7..cb32233f46 100644 --- a/src/Ai/Base/Strategy/NonCombatStrategy.cpp +++ b/src/Ai/Base/Strategy/NonCombatStrategy.cpp @@ -56,7 +56,7 @@ void MasterFishingStrategy::InitTriggers(std::vector& triggers) "random", { NextAction("end master fishing", 12.0f), - NextAction("equip upgrades", 6.0f) + NextAction("equip upgrades packet action", 6.0f) } ) ); diff --git a/src/Ai/Base/Strategy/WorldPacketHandlerStrategy.cpp b/src/Ai/Base/Strategy/WorldPacketHandlerStrategy.cpp index 2debc44cbd..ad31c2c390 100644 --- a/src/Ai/Base/Strategy/WorldPacketHandlerStrategy.cpp +++ b/src/Ai/Base/Strategy/WorldPacketHandlerStrategy.cpp @@ -32,7 +32,7 @@ void WorldPacketHandlerStrategy::InitTriggers(std::vector& trigger triggers.push_back(new TriggerNode("activate taxi", { NextAction("remember taxi", relevance), NextAction("taxi", relevance) })); triggers.push_back(new TriggerNode("taxi done", { NextAction("taxi", relevance) })); - triggers.push_back(new TriggerNode("trade status", { NextAction("accept trade", relevance), NextAction("equip upgrades", relevance) })); + triggers.push_back(new TriggerNode("trade status", { NextAction("accept trade", relevance), NextAction("equip upgrades packet action", relevance) })); triggers.push_back(new TriggerNode("trade status extended", { NextAction("trade status extended", relevance) })); triggers.push_back(new TriggerNode("area trigger", { NextAction("reach area trigger", relevance) })); triggers.push_back(new TriggerNode("within area trigger", { NextAction("area trigger", relevance) })); @@ -40,9 +40,9 @@ void WorldPacketHandlerStrategy::InitTriggers(std::vector& trigger triggers.push_back(new TriggerNode("item push result", { NextAction("unlock items", relevance), NextAction("open items", relevance), NextAction("query item usage", relevance), - NextAction("equip upgrades", relevance) })); + NextAction("equip upgrades packet action", relevance) })); triggers.push_back(new TriggerNode("item push result", { NextAction("quest item push result", relevance) })); - triggers.push_back(new TriggerNode("loot roll won", { NextAction("equip upgrades", relevance) })); + triggers.push_back(new TriggerNode("loot roll won", { NextAction("equip upgrades packet action", relevance) })); triggers.push_back(new TriggerNode("ready check finished", { NextAction("finish ready check", relevance) })); // triggers.push_back(new TriggerNode("often", { NextAction("security check", relevance), NextAction("check mail", relevance) })); triggers.push_back(new TriggerNode("guild invite", { NextAction("guild accept", relevance) })); diff --git a/src/Ai/Base/WorldPacketActionContext.h b/src/Ai/Base/WorldPacketActionContext.h index 59c0550368..839a886554 100644 --- a/src/Ai/Base/WorldPacketActionContext.h +++ b/src/Ai/Base/WorldPacketActionContext.h @@ -72,6 +72,7 @@ class WorldPacketActionContext : public NamedObjectContext creators["store loot"] = &WorldPacketActionContext::store_loot; creators["self resurrect"] = &WorldPacketActionContext::self_resurrect; creators["pet"] = &WorldPacketActionContext::pet; + creators["equip upgrades packet action"] = &WorldPacketActionContext::equip_upgrades_packet_action; // quest creators["talk to quest giver"] = &WorldPacketActionContext::turn_in_quest; @@ -142,6 +143,7 @@ class WorldPacketActionContext : public NamedObjectContext static Action* tell_cannot_equip(PlayerbotAI* botAI) { return new InventoryChangeFailureAction(botAI); } static Action* self_resurrect(PlayerbotAI* botAI) { return new SelfResurrectAction(botAI); } static Action* pet(PlayerbotAI* botAI) { return new PetsAction(botAI); } + static Action* equip_upgrades_packet_action(PlayerbotAI* botAI) { return new EquipUpgradesPacketAction(botAI); } // quest static Action* quest_update_add_kill(PlayerbotAI* ai) { return new QuestUpdateAddKillAction(ai); } diff --git a/src/Ai/Class/Hunter/Strategy/GenericHunterNonCombatStrategy.cpp b/src/Ai/Class/Hunter/Strategy/GenericHunterNonCombatStrategy.cpp index 151feadf16..00c35dc2a4 100644 --- a/src/Ai/Class/Hunter/Strategy/GenericHunterNonCombatStrategy.cpp +++ b/src/Ai/Class/Hunter/Strategy/GenericHunterNonCombatStrategy.cpp @@ -51,7 +51,7 @@ void GenericHunterNonCombatStrategy::InitTriggers(std::vector& tri })); triggers.push_back(new TriggerNode("low ammo", { NextAction("say::low ammo", ACTION_NORMAL)})); triggers.push_back(new TriggerNode("no track", { NextAction("track humanoids", ACTION_NORMAL)})); - triggers.push_back(new TriggerNode("no ammo", { NextAction("equip upgrades", ACTION_HIGH + 1)})); + triggers.push_back(new TriggerNode("no ammo", { NextAction("equip upgrades packet action", ACTION_HIGH + 1)})); } void HunterPetStrategy::InitTriggers(std::vector& triggers) diff --git a/src/Ai/Class/Hunter/Strategy/GenericHunterStrategy.cpp b/src/Ai/Class/Hunter/Strategy/GenericHunterStrategy.cpp index 16a22eaeb9..0a9e88a540 100644 --- a/src/Ai/Class/Hunter/Strategy/GenericHunterStrategy.cpp +++ b/src/Ai/Class/Hunter/Strategy/GenericHunterStrategy.cpp @@ -98,7 +98,7 @@ void GenericHunterStrategy::InitTriggers(std::vector& triggers) CombatStrategy::InitTriggers(triggers); // Mark/Ammo/Mana Triggers - triggers.push_back(new TriggerNode("no ammo", { NextAction("equip upgrades", 30.0f) })); + triggers.push_back(new TriggerNode("no ammo", { NextAction("equip upgrades packet action", 30.0f) })); triggers.push_back(new TriggerNode("hunter's mark", { NextAction("hunter's mark", 29.5f) })); triggers.push_back(new TriggerNode("rapid fire", { NextAction("rapid fire", 29.0f) })); triggers.push_back(new TriggerNode("aspect of the viper", { NextAction("aspect of the viper", 28.0f) })); From 403ca4150b85d8733f76b9132873e85e7966e9e5 Mon Sep 17 00:00:00 2001 From: dillyns <49765217+dillyns@users.noreply.github.com> Date: Mon, 16 Feb 2026 01:32:02 -0500 Subject: [PATCH 25/25] Move combat check to action isUseful Use config aggroDistance --- src/Ai/Base/Actions/ChooseTargetActions.cpp | 8 ++++++++ src/Ai/Base/Actions/ChooseTargetActions.h | 1 + src/Ai/Base/Value/AggressiveTargetValue.cpp | 10 +--------- src/Ai/Base/Value/AggressiveTargetValue.h | 3 --- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/Ai/Base/Actions/ChooseTargetActions.cpp b/src/Ai/Base/Actions/ChooseTargetActions.cpp index 200094c900..79b649aa89 100644 --- a/src/Ai/Base/Actions/ChooseTargetActions.cpp +++ b/src/Ai/Base/Actions/ChooseTargetActions.cpp @@ -30,6 +30,14 @@ bool AttackEnemyFlagCarrierAction::isUseful() PlayerHasFlag::IsCapturingFlag(bot); } +bool AggressiveTargetAction::isUseful() +{ + if (bot->IsInCombat()) + return false; + + return true; +} + bool AttackAnythingAction::isUseful() { if (!bot || !botAI) // Prevents invalid accesses diff --git a/src/Ai/Base/Actions/ChooseTargetActions.h b/src/Ai/Base/Actions/ChooseTargetActions.h index f09a429b60..5822543efb 100644 --- a/src/Ai/Base/Actions/ChooseTargetActions.h +++ b/src/Ai/Base/Actions/ChooseTargetActions.h @@ -41,6 +41,7 @@ class AggressiveTargetAction : public AttackAction AggressiveTargetAction(PlayerbotAI* botAI) : AttackAction(botAI, "aggressive target") {} std::string const GetTargetName() override { return "aggressive target"; } + bool isUseful() override; }; class AttackAnythingAction : public AttackAction diff --git a/src/Ai/Base/Value/AggressiveTargetValue.cpp b/src/Ai/Base/Value/AggressiveTargetValue.cpp index 7f3e171fdb..bbf393487e 100644 --- a/src/Ai/Base/Value/AggressiveTargetValue.cpp +++ b/src/Ai/Base/Value/AggressiveTargetValue.cpp @@ -10,14 +10,6 @@ #include "SharedDefines.h" Unit* AggressiveTargetValue::Calculate() -{ - if (bot->IsInCombat()) - return nullptr; - - return FindTargetForAggressive(); -} - -Unit* AggressiveTargetValue::FindTargetForAggressive() { Player* master = GetMaster(); @@ -29,7 +21,7 @@ Unit* AggressiveTargetValue::FindTargetForAggressive() if (targets.empty()) return nullptr; - float aggroRange = 30.0f; + float aggroRange = sPlayerbotAIConfig.aggroDistance; float distance = 0; Unit* result = nullptr; diff --git a/src/Ai/Base/Value/AggressiveTargetValue.h b/src/Ai/Base/Value/AggressiveTargetValue.h index 22bf5577a9..61b4d69515 100644 --- a/src/Ai/Base/Value/AggressiveTargetValue.h +++ b/src/Ai/Base/Value/AggressiveTargetValue.h @@ -17,9 +17,6 @@ class AggressiveTargetValue : public TargetValue AggressiveTargetValue(PlayerbotAI* botAI, std::string const name = "aggressive target") : TargetValue(botAI, name) {} Unit* Calculate() override; - -private: - Unit* FindTargetForAggressive(); }; #endif