From af0c5e7c4b98548ed1bab7d78c7a171189867372 Mon Sep 17 00:00:00 2001 From: Keleborn <22352763+Celandriel@users.noreply.github.com> Date: Sat, 6 Jun 2026 23:58:05 -0700 Subject: [PATCH 1/3] Fix/TellMaster crash (#2434) ## Pull Request Description If you add +debug to a randombot in the world without the randomBotSayWithoutMaster it will crash. ## Feature Evaluation - Describe the **minimum logic** required to achieve the intended behavior. - Describe the **processing cost** when this logic executes across many bots. ## How to Test the Changes ## Impact Assessment - Does this change increase per-bot/per-tick processing or risk scaling poorly with thousands of bots? - - [x] No, not at all - - [ ] Minimal impact (**explain below**) - - [ ] Moderate impact (**explain below**) - Does this change modify default bot behavior? - - [x] No - - [ ] Yes (**explain why**) - Does this change add new decision branches or increase maintenance complexity? - - [x] No - - [ ] Yes (**explain below**) ## AI Assistance Was AI assistance 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] Any new bot dialogue lines are translated. - - [x] Documentation updated if needed (Conf comments, WiKi commands). ## Notes for Reviewers --- src/Bot/PlayerbotAI.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Bot/PlayerbotAI.cpp b/src/Bot/PlayerbotAI.cpp index 8bd1f2c2f..eeadbbc55 100644 --- a/src/Bot/PlayerbotAI.cpp +++ b/src/Bot/PlayerbotAI.cpp @@ -3027,6 +3027,8 @@ bool PlayerbotAI::TellMaster(std::string const text, PlayerbotSecurityLevel secu { if (sPlayerbotAIConfig.randomBotSayWithoutMaster) return TellMasterNoFacing(text, securityLevel); + + return false; } if (!TellMasterNoFacing(text, securityLevel)) return false; From dda9ff0d40348855895c420746277d24b9a30cdb Mon Sep 17 00:00:00 2001 From: NoxMax <50133316+NoxMax@users.noreply.github.com> Date: Sun, 7 Jun 2026 00:58:25 -0600 Subject: [PATCH 2/3] Fix: Dismount cleanup (#2413) ## Pull Request Description Cleans some stale flight flags, and gets rid of the parachute given in forced flight dismount. Refactors some dismount code, and get rid of the unused mountData parameter in CalculateMasterMountSpeed as well `MOVEMENTFLAG_WATERWALKING` was removed because it might've been added to protect MOVEMENTFLAG_CAN_FLY, but if the bot remounts over water, MOVEMENTFLAG_CAN_FLY would be reapplied anyway. However if it has a function that I missed, we can re-add it. ## Feature Evaluation - Describe the **minimum logic** required to achieve the intended behavior. - Describe the **processing cost** when this logic executes across many bots. Feature is currently at minimum cost. ## How to Test the Changes 1. As GM hover high above Dalaran, but not high enough that you are outside the resting zone. 2. .summon some lvl +70 bots until you get some that are on a flying mount. 3. Watch as they lose their mount after 10 seconds, and be given a parachute as they fall. 4. When they reach the ground, they should be stuck with a parachute forever, and they shouldn't be stuck hovering just above the ground forever. ## Impact Assessment - Does this change increase per-bot/per-tick processing or risk scaling poorly with thousands of bots? - - [x] No, not at all - - [ ] Minimal impact (**explain below**) - - [ ] Moderate impact (**explain below**) - Does this change modify default bot behavior? - - [x] No - - [ ] Yes (**explain why**) - Does this change add new decision branches or increase maintenance complexity? - - [ ] No - - [x] Yes (**explain below**) Checks if bot has `HasFeatherFallAura` to remove any stale flight flags. ## AI Assistance Was AI assistance 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] Any new bot dialogue lines are translated. - - [x] Documentation updated if needed (Conf comments, WiKi commands). ## Notes for Reviewers Comment paragraph might be verbose, but worth it for whomever is looking at the code years from now trying to figure out why bots need special treatment with dismounting anyway. --- src/Ai/Base/Actions/CheckMountStateAction.cpp | 37 ++++++++++++++----- src/Ai/Base/Actions/CheckMountStateAction.h | 3 +- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/Ai/Base/Actions/CheckMountStateAction.cpp b/src/Ai/Base/Actions/CheckMountStateAction.cpp index 0e3abb724..5da250790 100644 --- a/src/Ai/Base/Actions/CheckMountStateAction.cpp +++ b/src/Ai/Base/Actions/CheckMountStateAction.cpp @@ -17,6 +17,7 @@ #include "SpellAuraEffects.h" static constexpr uint32 SPELL_COLD_WEATHER_FLYING = 54197; +static constexpr float PARACHUTE_LAND_THRESHOLD = 15.0f; // Define the static map / init bool for caching bot preferred mount data globally std::unordered_map CheckMountStateAction::mountCache; @@ -61,6 +62,21 @@ MountData CollectMountData(const Player* bot) bool CheckMountStateAction::Execute(Event /*event*/) { + // Forced flight dismount: + // Bots get stale flight movement flags after a forced dismount (e.g: Dalaran) because the post landing dismount cleanup + // needs MSG_MOVE_FALL_LAND (a client opcode) and client movement packets. The stale flags cause the bot to be stuck with + // the parachute, or even keep the bot hovering indefinitely and block MMAP routing. + // Note: Without MSG_MOVE_FALL_LAND, HandleFall doesn't trigger, meaning bots don't get fall damage in forced dismounts anyway, + // so the parachute usage here is more of an immersion feature. + if (bot->HasFeatherFallAura()) + { + float floorZ = bot->GetMapHeight(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ()); + if (floorZ != INVALID_HEIGHT && floorZ != VMAP_INVALID_HEIGHT_VALUE && + bot->GetPositionZ() - floorZ <= PARACHUTE_LAND_THRESHOLD) + bot->RemoveAurasByType(SPELL_AURA_FEATHER_FALL); + } + ClearStaleFlightFlags(); + // Determine if there are no attackers bool noAttackers = !AI_VALUE2(bool, "combat", "self target") || !AI_VALUE(uint8, "attacker count"); bool enemy = AI_VALUE(Unit*, "enemy player target"); @@ -204,7 +220,7 @@ bool CheckMountStateAction::Mount() // Get bot mount data MountData mountData = CollectMountData(bot); int32 masterMountType = GetMountType(master); - int32 masterSpeed = CalculateMasterMountSpeed(master, mountData); + int32 masterSpeed = CalculateMasterMountSpeed(master); // Try shapeshift if (TryForms(master, masterMountType, masterSpeed)) @@ -234,14 +250,17 @@ void CheckMountStateAction::Dismount() WorldPacket emptyPacket; bot->GetSession()->HandleCancelMountAuraOpcode(emptyPacket); - bool const wantsFly = bot->HasIncreaseMountedFlightSpeedAura() || bot->HasFlyAura(); - bool const isWaterWalking = bot->HasUnitMovementFlag(MOVEMENTFLAG_WATERWALKING); - bool const isFlying = bot->HasUnitMovementFlag(MOVEMENTFLAG_FLYING); - bool const hasGravityDisabled = bot->HasUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY); - if (!wantsFly && !isWaterWalking && (isFlying || hasGravityDisabled)) + ClearStaleFlightFlags(); +} + +void CheckMountStateAction::ClearStaleFlightFlags() +{ + if (bot->HasIncreaseMountedFlightSpeedAura() || bot->HasFlyAura()) + return; + + if (bot->HasUnitMovementFlag(MOVEMENTFLAG_FLYING | MOVEMENTFLAG_DISABLE_GRAVITY)) { - bot->RemoveUnitMovementFlag( - MOVEMENTFLAG_FLYING | MOVEMENTFLAG_CAN_FLY | MOVEMENTFLAG_DISABLE_GRAVITY); + bot->RemoveUnitMovementFlag(MOVEMENTFLAG_FLYING | MOVEMENTFLAG_DISABLE_GRAVITY | MOVEMENTFLAG_CAN_FLY); if (!bot->IsRooted()) bot->SendMovementFlagUpdate(); } @@ -490,7 +509,7 @@ static bool BotCanUseFlyingMount(Player const* bot) return true; } -int32 CheckMountStateAction::CalculateMasterMountSpeed(Player* master, const MountData& mountData) const +int32 CheckMountStateAction::CalculateMasterMountSpeed(Player* master) const { // Check riding skill and level requirements int32 ridingSkill = bot->GetPureSkillValue(SKILL_RIDING); diff --git a/src/Ai/Base/Actions/CheckMountStateAction.h b/src/Ai/Base/Actions/CheckMountStateAction.h index d1faa3798..0bf83cc5e 100644 --- a/src/Ai/Base/Actions/CheckMountStateAction.h +++ b/src/Ai/Base/Actions/CheckMountStateAction.h @@ -53,9 +53,10 @@ private: float CalculateDismountDistance() const; float CalculateMountDistance() const; void Dismount(); + void ClearStaleFlightFlags(); bool ShouldFollowMasterMountState(Player* master, bool noAttackers, bool shouldMount) const; bool ShouldDismountForMaster(Player* master) const; - int32 CalculateMasterMountSpeed(Player* master, const MountData& mountData) const; + int32 CalculateMasterMountSpeed(Player* master) const; bool CheckForSwiftMount() const; std::map>> GetAllMountSpells() const; bool TryForms(Player* master, int32 masterMountType, int32 masterSpeed) const; From a76f2ca268adc0c4732bb0b774372cc9d5fce7cd Mon Sep 17 00:00:00 2001 From: Crow Date: Sun, 7 Jun 2026 10:14:53 -0500 Subject: [PATCH 3/3] Refactor HasSpell/HasAura and convert spellIds to constants (#2435) ## Pull Request Description This is a PR to follow-up on a discussion with @kadeshar during the recent mage armor PR. Main changes: - Add new PlayerbotAI::HasSpell() overload that accepts a string for the spell name as an argument, in order to replace repeated bot->HasSpell(spellId) checks for places where multiple ranks of a spell are checked, and to replace a few calls to AI_VALUE(uint32, "spell id", ...) where it existed only to check for the spell being known. - Removed PlayerbotAI::HasAura() overload that takes a spell Id as an argument. It is rarely used, and the few instances of its usage are easily replaced with AC's Unit::HasAura(), which is the predominant usage for spell Id aura checks already anyway. - When modifying DruidPullStrategy to use the new HasSpell() method, I went ahead and also simplified the file overall to pare down duplication (for example, the first check in CanCastSpell is for the spell Id so we don't need to separately check it before calling CanCastSpell()). I then did the same for other pull strategies so they can be consistent. - Changed many magic numbers for spell Ids to be defined as compile-time constants. I didn't do this throughout the code, but I did in the files where I was making changes anyway due to the HasSpell() and/or HasAura() changes. ## Feature Evaluation - Describe the **minimum logic** required to achieve the intended behavior. - Describe the **processing cost** when this logic executes across many bots. - Minimum logic is described above. - Processing cost shouldn't change much. See Impact Assessment below. ## How to Test the Changes ## Impact Assessment - Does this change increase per-bot/per-tick processing or risk scaling poorly with thousands of bots? - - [ ] No, not at all - - [x] Minimal impact (**explain below**) - - [ ] Moderate impact (**explain below**) - Passing a string for the spell to check for it being known or the aura being present is more taxing than looking up a spell Id, although the usefulness of the string-based overload is for situations with spells that have multiple ranks. - Does this change modify default bot behavior? - - [x] No - - [ ] Yes (**explain why**) - Does this change add new decision branches or increase maintenance complexity? - - [x] No - - [ ] Yes (**explain below**) - I think these changes should simplify maintenance complexity. ## AI Assistance Was AI assistance used while working on this change? - - [ ] No - - [x] Yes (**explain below**) - I had AI make many of the changes and then review since they were pretty mundane (for example, I'd tell it to just take all the magic numbers from a file and define them in an anonymous namespace, then replace the magic numbers with the spell constants). There isn't anything complicated in this PR; it was mostly busywork. ## Final Checklist - - [x] Stability is not compromised. - - [x] Performance impact is understood, tested, and acceptable. - - [x] Added logic complexity is justified and explained. - - [x] Any new bot dialogue lines are translated. - - [x] Documentation updated if needed (Conf comments, WiKi commands). ## Notes for Reviewers --- src/Ai/Base/Actions/TameAction.cpp | 7 +- .../Actions/TradeStatusExtendedAction.cpp | 5 +- src/Ai/Base/Trigger/BossAuraTriggers.cpp | 17 +-- .../Dk/Strategy/DeathKnightPullStrategy.cpp | 22 +--- .../Druid/Action/DruidShapeshiftActions.cpp | 5 +- src/Ai/Class/Druid/DruidTriggers.cpp | 13 +- src/Ai/Class/Druid/DruidTriggers.h | 4 +- .../Druid/Strategy/DruidPullStrategy.cpp | 31 ++--- src/Ai/Class/Hunter/HunterActions.cpp | 23 +--- src/Ai/Class/Mage/MageActions.cpp | 4 +- src/Ai/Class/Mage/MageTriggers.cpp | 31 ++--- .../Mage/Strategy/GenericMageStrategy.cpp | 26 +++- .../Actions/PaladinGreaterBlessingAction.cpp | 3 +- .../Paladin/Strategy/PaladinPullStrategy.cpp | 17 +-- src/Ai/Class/Rogue/Action/RogueActions.cpp | 16 ++- src/Ai/Class/Rogue/RogueTriggers.cpp | 16 ++- .../Shaman/Strategy/TotemsShamanStrategy.cpp | 27 ++-- src/Ai/Class/Warlock/WarlockActions.cpp | 6 +- src/Ai/Class/Warlock/WarlockTriggers.cpp | 6 +- .../Warrior/Strategy/WarriorPullStrategy.cpp | 11 +- src/Ai/Class/Warrior/WarriorActions.cpp | 42 +++--- src/Ai/Class/Warrior/WarriorTriggers.cpp | 24 ++-- .../Naxx/Action/NaxxActions_Razuvious.cpp | 26 ++-- .../Naxx/Action/NaxxActions_Sapphiron.cpp | 2 +- .../Raid/Naxx/Action/NaxxActions_Thaddius.cpp | 6 +- src/Ai/Raid/Naxx/NaxxBossHelper.h | 2 +- src/Ai/Raid/Naxx/NaxxMultipliers.cpp | 2 +- src/Ai/Raid/Naxx/NaxxSpellIds.h | 6 +- src/Ai/Raid/Uld/UldTriggers.cpp | 4 +- src/Bot/Factory/AiFactory.cpp | 17 ++- src/Bot/Factory/PlayerbotFactory.cpp | 122 ++++++++++++------ src/Bot/PlayerbotAI.cpp | 18 +-- src/Bot/PlayerbotAI.h | 2 +- src/Mgr/Item/StatsWeightCalculator.cpp | 55 +++++--- 34 files changed, 329 insertions(+), 289 deletions(-) diff --git a/src/Ai/Base/Actions/TameAction.cpp b/src/Ai/Base/Actions/TameAction.cpp index dfeb61a47..57ae24e8c 100644 --- a/src/Ai/Base/Actions/TameAction.cpp +++ b/src/Ai/Base/Actions/TameAction.cpp @@ -422,10 +422,9 @@ bool TameAction::RenamePet(const std::string& newName) // Remove the current pet and (re-)cast Call Pet spell if the bot is a hunter bot->RemovePet(nullptr, PET_SAVE_AS_CURRENT, true); - if (bot->getClass() == CLASS_HUNTER && bot->HasSpell(883)) - { - bot->CastSpell(bot, 883, true); - } + constexpr uint32 SPELL_CALL_PET = 883; + if (bot->getClass() == CLASS_HUNTER && bot->HasSpell(SPELL_CALL_PET)) + bot->CastSpell(bot, SPELL_CALL_PET, true); return true; } diff --git a/src/Ai/Base/Actions/TradeStatusExtendedAction.cpp b/src/Ai/Base/Actions/TradeStatusExtendedAction.cpp index ada779e36..f081c3365 100644 --- a/src/Ai/Base/Actions/TradeStatusExtendedAction.cpp +++ b/src/Ai/Base/Actions/TradeStatusExtendedAction.cpp @@ -67,9 +67,10 @@ bool TradeStatusExtendedAction::Execute(Event event) return false; } - if (bot->getClass() == CLASS_ROGUE && bot->HasSpell(1804) && lockbox->IsLocked()) // Pick Lock spell + constexpr uint32 SPELL_PICK_LOCK = 1804; + if (bot->getClass() == CLASS_ROGUE && bot->HasSpell(SPELL_PICK_LOCK) && lockbox->IsLocked()) { - // botAI->CastSpell(1804, bot, lockbox); // Attempt to cast Pick Lock on the lockbox + // botAI->CastSpell(SPELL_PICK_LOCK, bot, lockbox); // Attempt to cast Pick Lock on the lockbox botAI->DoSpecificAction("unlock traded item"); botAI->SetNextCheckDelay(4000); // Delay before accepting trade } diff --git a/src/Ai/Base/Trigger/BossAuraTriggers.cpp b/src/Ai/Base/Trigger/BossAuraTriggers.cpp index 8d2520cf6..4c8dd5b33 100644 --- a/src/Ai/Base/Trigger/BossAuraTriggers.cpp +++ b/src/Ai/Base/Trigger/BossAuraTriggers.cpp @@ -23,9 +23,7 @@ bool BossFireResistanceTrigger::IsActive() return false; // Check if bot have fire resistance aura - if (bot->HasAura(SPELL_FIRE_RESISTANCE_AURA_RANK_5) || bot->HasAura(SPELL_FIRE_RESISTANCE_AURA_RANK_4) || - bot->HasAura(SPELL_FIRE_RESISTANCE_AURA_RANK_3) || bot->HasAura(SPELL_FIRE_RESISTANCE_AURA_RANK_2) || - bot->HasAura(SPELL_FIRE_RESISTANCE_AURA_RANK_1)) + if (botAI->HasAura("fire resistance aura", bot)) return false; // Check if bot dont have already have fire resistance strategy @@ -76,9 +74,7 @@ bool BossFrostResistanceTrigger::IsActive() return false; // Check if bot have frost resistance aura - if (bot->HasAura(SPELL_FROST_RESISTANCE_AURA_RANK_5) || bot->HasAura(SPELL_FROST_RESISTANCE_AURA_RANK_4) || - bot->HasAura(SPELL_FROST_RESISTANCE_AURA_RANK_3) || bot->HasAura(SPELL_FROST_RESISTANCE_AURA_RANK_2) || - bot->HasAura(SPELL_FROST_RESISTANCE_AURA_RANK_1)) + if (botAI->HasAura("frost resistance aura", bot)) return false; // Check if bot dont have already have frost resistance strategy @@ -133,8 +129,7 @@ bool BossNatureResistanceTrigger::IsActive() return false; // Check if bot have nature resistance aura - if (bot->HasAura(SPELL_ASPECT_OF_THE_WILD_RANK_4) || bot->HasAura(SPELL_ASPECT_OF_THE_WILD_RANK_3) || - bot->HasAura(SPELL_ASPECT_OF_THE_WILD_RANK_2) || bot->HasAura(SPELL_ASPECT_OF_THE_WILD_RANK_1)) + if (botAI->HasAura("aspect of the wild", bot)) return false; // Check if bot dont have already setted nature resistance aura @@ -184,11 +179,7 @@ bool BossShadowResistanceTrigger::IsActive() return false; // Check if bot have shadow resistance aura - if (bot->HasAura(SPELL_SHADOW_RESISTANCE_AURA_RANK_5) || - bot->HasAura(SPELL_SHADOW_RESISTANCE_AURA_RANK_4) || - bot->HasAura(SPELL_SHADOW_RESISTANCE_AURA_RANK_3) || - bot->HasAura(SPELL_SHADOW_RESISTANCE_AURA_RANK_2) || - bot->HasAura(SPELL_SHADOW_RESISTANCE_AURA_RANK_1)) + if (botAI->HasAura("shadow resistance aura", bot)) return false; // Check if bot dont have already have shadow resistance strategy diff --git a/src/Ai/Class/Dk/Strategy/DeathKnightPullStrategy.cpp b/src/Ai/Class/Dk/Strategy/DeathKnightPullStrategy.cpp index be643b50c..db58d3135 100644 --- a/src/Ai/Class/Dk/Strategy/DeathKnightPullStrategy.cpp +++ b/src/Ai/Class/Dk/Strategy/DeathKnightPullStrategy.cpp @@ -5,38 +5,26 @@ #include "DeathKnightPullStrategy.h" -#include "AiObjectContext.h" #include "Player.h" #include "PlayerbotAI.h" #include "Playerbots.h" std::string DeathKnightPullStrategy::GetPullActionName() const { - Player* bot = botAI->GetBot(); Unit* target = GetTarget(); - if (!bot || !target || + if (!target || (!botAI->HasStrategy("blood", BOT_STATE_COMBAT) && !botAI->HasStrategy("blood", BOT_STATE_NON_COMBAT))) { return PullStrategy::GetPullActionName(); } - uint32 const deathGripSpellId = botAI->GetAiObjectContext()->GetValue("spell id", "death grip")->Get(); - if (deathGripSpellId && bot->HasSpell(deathGripSpellId) && - botAI->CanCastSpell(deathGripSpellId, target)) - { + if (botAI->CanCastSpell("death grip", target)) return "death grip"; - } - uint32 const icyTouchSpellId = botAI->GetAiObjectContext()->GetValue("spell id", "icy touch")->Get(); - if (!icyTouchSpellId || !bot->HasSpell(icyTouchSpellId) || - !botAI->CanCastSpell(icyTouchSpellId, target)) + if (!botAI->CanCastSpell("icy touch", target) && + botAI->CanCastSpell("dark command", target)) { - uint32 const darkCommandSpellId = botAI->GetAiObjectContext()->GetValue("spell id", "dark command")->Get(); - if (darkCommandSpellId && bot->HasSpell(darkCommandSpellId) && - botAI->CanCastSpell(darkCommandSpellId, target)) - { - return "dark command"; - } + return "dark command"; } return PullStrategy::GetPullActionName(); diff --git a/src/Ai/Class/Druid/Action/DruidShapeshiftActions.cpp b/src/Ai/Class/Druid/Action/DruidShapeshiftActions.cpp index f30d4ec03..97cdcf9d8 100644 --- a/src/Ai/Class/Druid/Action/DruidShapeshiftActions.cpp +++ b/src/Ai/Class/Druid/Action/DruidShapeshiftActions.cpp @@ -50,9 +50,10 @@ bool CastCancelDruidAction::Execute(Event /*event*/) return true; } -bool CastCancelDruidAction::isUseful() { return botAI->HasAura(auraId, bot); } +bool CastCancelDruidAction::isUseful() { return bot->HasAura(auraId); } bool CastTreeFormAction::isUseful() { - return GetTarget() && CastSpellAction::isUseful() && !botAI->HasAura(33891, bot); + constexpr uint32 SPELL_TREE_OF_LIFE = 33891; + return GetTarget() && CastSpellAction::isUseful() && !bot->HasAura(SPELL_TREE_OF_LIFE); } diff --git a/src/Ai/Class/Druid/DruidTriggers.cpp b/src/Ai/Class/Druid/DruidTriggers.cpp index 03b5accc9..22b850329 100644 --- a/src/Ai/Class/Druid/DruidTriggers.cpp +++ b/src/Ai/Class/Druid/DruidTriggers.cpp @@ -28,7 +28,11 @@ bool ThornsTrigger::IsActive() { return BuffTrigger::IsActive() && !botAI->HasAu bool BearFormTrigger::IsActive() { return !botAI->HasAnyAuraOf(bot, "bear form", "dire bear form", nullptr); } -bool TreeFormTrigger::IsActive() { return !botAI->HasAura(33891, bot); } +bool TreeFormTrigger::IsActive() +{ + constexpr uint32 SPELL_TREE_OF_LIFE = 33891; + return !bot->HasAura(SPELL_TREE_OF_LIFE); +} bool CatFormTrigger::IsActive() { return !botAI->HasAura("cat form", bot); } @@ -43,8 +47,11 @@ bool ProwlTrigger::IsActive() if (botAI->HasAura("prowl", bot) || bot->IsInCombat()) return false; - uint32 prowlId = botAI->GetAiObjectContext()->GetValue("spell id", "prowl")->Get(); - if (!prowlId || !bot->HasSpell(prowlId) || bot->HasSpellCooldown(prowlId)) + if (!botAI->HasSpell("prowl")) + return false; + + uint32 const prowlId = AI_VALUE2(uint32, "spell id", "prowl"); + if (bot->HasSpellCooldown(prowlId)) return false; float distance = 30.f; diff --git a/src/Ai/Class/Druid/DruidTriggers.h b/src/Ai/Class/Druid/DruidTriggers.h index 990024685..fa0f21132 100644 --- a/src/Ai/Class/Druid/DruidTriggers.h +++ b/src/Ai/Class/Druid/DruidTriggers.h @@ -393,14 +393,14 @@ public: class FerociousBiteExecuteTrigger : public Trigger { public: - FerociousBiteExecuteTrigger(PlayerbotAI* ai) : Trigger(ai, "ferocious bite execute") {} + FerociousBiteExecuteTrigger(PlayerbotAI* botAI) : Trigger(botAI, "ferocious bite execute") {} bool IsActive() override { Unit* target = AI_VALUE(Unit*, "current target"); if (!target || !target->IsAlive()) return false; - if (!AI_VALUE2(uint32, "spell id", "ferocious bite")) + if (!botAI->HasSpell("ferocious bite")) return false; if (AI_VALUE2(uint8, "combo", "current target") < 1) diff --git a/src/Ai/Class/Druid/Strategy/DruidPullStrategy.cpp b/src/Ai/Class/Druid/Strategy/DruidPullStrategy.cpp index dc72b9e82..7c3727057 100644 --- a/src/Ai/Class/Druid/Strategy/DruidPullStrategy.cpp +++ b/src/Ai/Class/Druid/Strategy/DruidPullStrategy.cpp @@ -5,34 +5,23 @@ #include "DruidPullStrategy.h" -#include "AiObjectContext.h" -#include "Player.h" #include "PlayerbotAI.h" #include "Playerbots.h" std::string DruidPullStrategy::GetPullActionName() const { - Player* bot = botAI->GetBot(); - std::string actionName = PullStrategy::GetPullActionName(); - if (!bot) - return actionName; - - uint32 const faerieFireFeralId = botAI->GetAiObjectContext()->GetValue("spell id", "faerie fire (feral)")->Get(); - if (faerieFireFeralId && bot->HasSpell(faerieFireFeralId) && - (botAI->HasStrategy("bear", BOT_STATE_COMBAT) || botAI->HasStrategy("cat", BOT_STATE_COMBAT))) - { - actionName = "faerie fire (feral)"; - } + std::string const pullActionName = PullStrategy::GetPullActionName(); + std::string const actionName = + botAI->HasSpell("faerie fire (feral)") && + (botAI->HasStrategy("bear", BOT_STATE_COMBAT) || botAI->HasStrategy("cat", BOT_STATE_COMBAT)) + ? "faerie fire (feral)" : pullActionName; Unit* target = GetTarget(); - uint32 const faerieFireSpellId = botAI->GetAiObjectContext()->GetValue("spell id", actionName)->Get(); - if (target && (!faerieFireSpellId || !bot->HasSpell(faerieFireSpellId) || - !botAI->CanCastSpell(faerieFireSpellId, target))) - { - uint32 const growlSpellId = botAI->GetAiObjectContext()->GetValue("spell id", "growl")->Get(); - if (growlSpellId && bot->HasSpell(growlSpellId) && botAI->CanCastSpell(growlSpellId, target)) - return "growl"; - } + if (!target) + return actionName; + + if (!botAI->CanCastSpell(actionName, target) && botAI->CanCastSpell("growl", target)) + return "growl"; return actionName; } diff --git a/src/Ai/Class/Hunter/HunterActions.cpp b/src/Ai/Class/Hunter/HunterActions.cpp index a49f9a10e..c3116d1f8 100644 --- a/src/Ai/Class/Hunter/HunterActions.cpp +++ b/src/Ai/Class/Hunter/HunterActions.cpp @@ -19,23 +19,13 @@ bool CastViperStingAction::isUseful() bool CastAspectOfTheHawkAction::isUseful() { Unit* target = GetTarget(); - if (!target) - return false; - - if (bot->HasSpell(61846) || bot->HasSpell(61847)) // Aspect of the Dragonhawk spell IDs - return false; - - return true; + return target && !botAI->HasSpell("aspect of the dragonhawk"); } bool CastArcaneShotAction::isUseful() { Unit* target = GetTarget(); - if (!target) - return false; - - if (bot->HasSpell(53301) || bot->HasSpell(60051) || - bot->HasSpell(60052) || bot->HasSpell(60053)) // Explosive Shot spell IDs + if (!target || !botAI->HasSpell("explosive shot")) return false; // Armor Penetration rating check - will not cast Arcane Shot above 435 ArP @@ -50,14 +40,7 @@ bool CastArcaneShotAction::isUseful() bool CastImmolationTrapAction::isUseful() { Unit* target = GetTarget(); - if (!target) - return false; - - if (bot->HasSpell(13813) || bot->HasSpell(14316) || bot->HasSpell(14317) || bot->HasSpell(27025) || - bot->HasSpell(49066) || bot->HasSpell(49067)) // Explosive Trap spell IDs - return false; - - return true; + return target && !botAI->HasSpell("explosive trap"); } Value* CastFreezingTrap::GetTargetValue() diff --git a/src/Ai/Class/Mage/MageActions.cpp b/src/Ai/Class/Mage/MageActions.cpp index 9c190d54c..13b40fa55 100644 --- a/src/Ai/Class/Mage/MageActions.cpp +++ b/src/Ai/Class/Mage/MageActions.cpp @@ -13,7 +13,7 @@ std::vector CastMoltenArmorAction::getAlternatives() { - if (!AI_VALUE2(uint32, "spell id", "molten armor")) + if (!botAI->HasSpell("molten armor")) return NextAction::merge({ NextAction("mage armor") }, CastBuffSpellAction::getAlternatives()); return CastBuffSpellAction::getAlternatives(); @@ -21,7 +21,7 @@ std::vector CastMoltenArmorAction::getAlternatives() std::vector CastMageArmorAction::getAlternatives() { - if (!AI_VALUE2(uint32, "spell id", "mage armor")) + if (!botAI->HasSpell("mage armor")) return NextAction::merge({ NextAction("ice armor") }, CastBuffSpellAction::getAlternatives()); return CastBuffSpellAction::getAlternatives(); diff --git a/src/Ai/Class/Mage/MageTriggers.cpp b/src/Ai/Class/Mage/MageTriggers.cpp index 34babc81e..0be6ff13b 100644 --- a/src/Ai/Class/Mage/MageTriggers.cpp +++ b/src/Ai/Class/Mage/MageTriggers.cpp @@ -39,26 +39,16 @@ bool ArcaneIntellectTrigger::IsActive() bool MageArmorTrigger::IsActive() { Unit* target = GetTarget(); - if (botAI->HasAura("mage armor", target)) - return false; - - if (AI_VALUE2(uint32, "spell id", "mage armor")) - return true; - - return !botAI->HasAura("ice armor", target) && !botAI->HasAura("frost armor", target) && + return botAI->HasSpell("mage armor") && !botAI->HasAura("mage armor", target) && + !botAI->HasAura("ice armor", target) && !botAI->HasAura("frost armor", target) && !botAI->HasAura("molten armor", target); } bool MoltenArmorTrigger::IsActive() { Unit* target = GetTarget(); - if (botAI->HasAura("molten armor", target)) - return false; - - if (AI_VALUE2(uint32, "spell id", "molten armor")) - return true; - - return !botAI->HasAura("ice armor", target) && !botAI->HasAura("frost armor", target) && + return botAI->HasSpell("molten armor") && !botAI->HasAura("molten armor", target) && + !botAI->HasAura("ice armor", target) && !botAI->HasAura("frost armor", target) && !botAI->HasAura("mage armor", target); } @@ -82,7 +72,8 @@ bool FrostbiteOnTargetTrigger::IsActive() bool NoFocusMagicTrigger::IsActive() { - if (!bot->HasSpell(54646)) // Focus Magic + constexpr uint32 SPELL_FOCUS_MAGIC = 54646; + if (!bot->HasSpell(SPELL_FOCUS_MAGIC)) return false; Group* group = bot->GetGroup(); @@ -95,7 +86,7 @@ bool NoFocusMagicTrigger::IsActive() if (!member || member == bot || !member->IsAlive()) continue; - if (member->HasAura(54646, bot->GetGUID())) + if (member->HasAura(SPELL_FOCUS_MAGIC, bot->GetGUID())) return false; } return true; @@ -103,11 +94,9 @@ bool NoFocusMagicTrigger::IsActive() bool DeepFreezeCooldownTrigger::IsActive() { - // If the bot does NOT have Deep Freeze, treat as "on cooldown" - if (!bot->HasSpell(44572)) // Deep Freeze - return true; - - return SpellCooldownTrigger::IsActive(); + constexpr uint32 SPELL_DEEP_FREEZE = 44572; + return !bot->HasSpell(SPELL_DEEP_FREEZE) || + SpellCooldownTrigger::IsActive(); } const std::unordered_set FlamestrikeNearbyTrigger::FLAMESTRIKE_SPELL_IDS = { diff --git a/src/Ai/Class/Mage/Strategy/GenericMageStrategy.cpp b/src/Ai/Class/Mage/Strategy/GenericMageStrategy.cpp index 69389a8c4..36b594ce9 100644 --- a/src/Ai/Class/Mage/Strategy/GenericMageStrategy.cpp +++ b/src/Ai/Class/Mage/Strategy/GenericMageStrategy.cpp @@ -8,6 +8,18 @@ #include "Playerbots.h" #include "RangedCombatStrategy.h" +namespace +{ +constexpr uint32 SPELL_CONJURE_MANA_SAPPHIRE = 42985; +constexpr uint32 SPELL_CONJURE_MANA_EMERALD = 27101; +constexpr uint32 SPELL_CONJURE_MANA_RUBY = 10054; +constexpr uint32 SPELL_CONJURE_MANA_CITRINE = 10053; +constexpr uint32 SPELL_CONJURE_MANA_JADE = 3552; +constexpr uint32 SPELL_CONJURE_MANA_AGATE = 759; +constexpr uint32 SPELL_FROSTFIRE_BOLT = 44614; +constexpr uint32 SPELL_ICE_SHARDS = 15047; +} + class GenericMageStrategyActionNodeFactory : public NamedObjectFactory { public: @@ -102,17 +114,17 @@ void GenericMageStrategy::InitTriggers(std::vector& triggers) // Mana Threshold Triggers Player* bot = botAI->GetBot(); - if (bot->HasSpell(42985)) // Mana Sapphire + if (bot->HasSpell(SPELL_CONJURE_MANA_SAPPHIRE)) triggers.push_back(new TriggerNode("high mana", { NextAction("use mana sapphire", 90.0f) })); - else if (bot->HasSpell(27101)) // Mana Emerald + else if (bot->HasSpell(SPELL_CONJURE_MANA_EMERALD)) triggers.push_back(new TriggerNode("high mana", { NextAction("use mana emerald", 90.0f) })); - else if (bot->HasSpell(10054)) // Mana Ruby + else if (bot->HasSpell(SPELL_CONJURE_MANA_RUBY)) triggers.push_back(new TriggerNode("high mana", { NextAction("use mana ruby", 90.0f) })); - else if (bot->HasSpell(10053)) // Mana Citrine + else if (bot->HasSpell(SPELL_CONJURE_MANA_CITRINE)) triggers.push_back(new TriggerNode("high mana", { NextAction("use mana citrine", 90.0f) })); - else if (bot->HasSpell(3552)) // Mana Jade + else if (bot->HasSpell(SPELL_CONJURE_MANA_JADE)) triggers.push_back(new TriggerNode("high mana", { NextAction("use mana jade", 90.0f) })); - else if (bot->HasSpell(759)) // Mana Agate + else if (bot->HasSpell(SPELL_CONJURE_MANA_AGATE)) triggers.push_back(new TriggerNode("high mana", { NextAction("use mana agate", 90.0f) })); triggers.push_back(new TriggerNode("medium mana", { NextAction("mana potion", 90.0f) })); @@ -142,7 +154,7 @@ void MageBoostStrategy::InitTriggers(std::vector& triggers) } else if (tab == MAGE_TAB_FIRE) { - if (bot->HasSpell(44614) /*Frostfire Bolt*/ && bot->HasAura(15047) /*Ice Shards*/) + if (bot->HasSpell(SPELL_FROSTFIRE_BOLT) && bot->HasAura(SPELL_ICE_SHARDS)) { // Frostfire triggers.push_back(new TriggerNode("combustion", { NextAction("combustion", 18.0f) })); triggers.push_back(new TriggerNode("icy veins", { NextAction("icy veins", 17.5f) })); diff --git a/src/Ai/Class/Paladin/Actions/PaladinGreaterBlessingAction.cpp b/src/Ai/Class/Paladin/Actions/PaladinGreaterBlessingAction.cpp index 6a174d300..85f7f894f 100644 --- a/src/Ai/Class/Paladin/Actions/PaladinGreaterBlessingAction.cpp +++ b/src/Ai/Class/Paladin/Actions/PaladinGreaterBlessingAction.cpp @@ -1145,8 +1145,7 @@ bool CastGreaterBlessingAssignmentAction::Execute(Event /*event*/) if (!FindPendingAssignment(assignment, spellName)) return false; - uint32 finalId = AI_VALUE2(uint32, "spell id", spellName); - if (!finalId) + if (!botAI->HasSpell(spellName)) return false; return botAI->CastSpell(spellName, assignment.player); diff --git a/src/Ai/Class/Paladin/Strategy/PaladinPullStrategy.cpp b/src/Ai/Class/Paladin/Strategy/PaladinPullStrategy.cpp index ba0381b5a..988d9a987 100644 --- a/src/Ai/Class/Paladin/Strategy/PaladinPullStrategy.cpp +++ b/src/Ai/Class/Paladin/Strategy/PaladinPullStrategy.cpp @@ -5,34 +5,23 @@ #include "PaladinPullStrategy.h" -#include "AiObjectContext.h" -#include "Player.h" #include "PlayerbotAI.h" #include "Playerbots.h" std::string PaladinPullStrategy::GetPullActionName() const { - Player* bot = botAI->GetBot(); Unit* target = GetTarget(); - if (!bot || !target || + if (!target || (!botAI->HasStrategy("tank", BOT_STATE_COMBAT) && !botAI->HasStrategy("tank", BOT_STATE_NON_COMBAT))) { return PullStrategy::GetPullActionName(); } - uint32 const avengersShieldSpellId = botAI->GetAiObjectContext()->GetValue("spell id", "avenger's shield")->Get(); - if (avengersShieldSpellId && bot->HasSpell(avengersShieldSpellId) && - botAI->CanCastSpell(avengersShieldSpellId, target)) - { + if (botAI->CanCastSpell("avenger's shield", target)) return "avenger's shield"; - } - uint32 const handOfReckoningSpellId = botAI->GetAiObjectContext()->GetValue("spell id", "hand of reckoning")->Get(); - if (handOfReckoningSpellId && bot->HasSpell(handOfReckoningSpellId) && - botAI->CanCastSpell(handOfReckoningSpellId, target)) - { + if (botAI->CanCastSpell("hand of reckoning", target)) return "hand of reckoning"; - } return PullStrategy::GetPullActionName(); } diff --git a/src/Ai/Class/Rogue/Action/RogueActions.cpp b/src/Ai/Class/Rogue/Action/RogueActions.cpp index 46beaf86c..aae0d3eb0 100644 --- a/src/Ai/Class/Rogue/Action/RogueActions.cpp +++ b/src/Ai/Class/Rogue/Action/RogueActions.cpp @@ -11,6 +11,14 @@ #include "PlayerbotAIConfig.h" #include "Playerbots.h" +namespace +{ +constexpr uint32 SPELL_WARSONG_FLAG = 23333; +constexpr uint32 SPELL_SILVERWING_FLAG = 23335; +constexpr uint32 SPELL_NETHERSTORM_FLAG = 34976; +constexpr uint32 SPELL_MASTER_POISONER_RANK_3 = 58410; +} + bool CastStealthAction::isUseful() { Unit* target = AI_VALUE(Unit*, "current target"); @@ -22,7 +30,8 @@ bool CastStealthAction::isUseful() bool CastStealthAction::isPossible() { // do not use with WSG flag or EYE flag - return !botAI->HasAura(23333, bot) && !botAI->HasAura(23335, bot) && !botAI->HasAura(34976, bot); + return !bot->HasAura(SPELL_WARSONG_FLAG) && !bot->HasAura(SPELL_SILVERWING_FLAG) && + !bot->HasAura(SPELL_NETHERSTORM_FLAG); } bool UnstealthAction::Execute(Event /*event*/) @@ -50,7 +59,8 @@ bool CheckStealthAction::Execute(Event /*event*/) bool CastVanishAction::isUseful() { // do not use with WSG flag or EYE flag - return !botAI->HasAura(23333, bot) && !botAI->HasAura(23335, bot) && !botAI->HasAura(34976, bot); + return !bot->HasAura(SPELL_WARSONG_FLAG) && !bot->HasAura(SPELL_SILVERWING_FLAG) && + !bot->HasAura(SPELL_NETHERSTORM_FLAG); } bool CastEnvenomAction::isUseful() @@ -61,7 +71,7 @@ bool CastEnvenomAction::isUseful() bool CastEnvenomAction::isPossible() { // alternate to eviscerate if talents unlearned - return botAI->HasAura(58410, bot) /* Master Poisoner Rank 3 */; + return bot->HasAura(SPELL_MASTER_POISONER_RANK_3); } bool CastTricksOfTheTradeOnMainTankAction::isUseful() diff --git a/src/Ai/Class/Rogue/RogueTriggers.cpp b/src/Ai/Class/Rogue/RogueTriggers.cpp index c33dbb7fe..d8fd166a1 100644 --- a/src/Ai/Class/Rogue/RogueTriggers.cpp +++ b/src/Ai/Class/Rogue/RogueTriggers.cpp @@ -9,6 +9,12 @@ #include "Playerbots.h" #include "ServerFacade.h" +namespace +{ +constexpr uint32 SPELL_STEALTH = 1784; +constexpr uint32 SPELL_SPRINT_RANK_1 = 2983; +} + // bool AdrenalineRushTrigger::isPossible() // { // return !botAI->HasAura("stealth", bot); @@ -29,7 +35,7 @@ bool UnstealthTrigger::IsActive() bool StealthTrigger::IsActive() { - if (botAI->HasAura("stealth", bot) || bot->IsInCombat() || bot->HasSpellCooldown(1784)) + if (bot->HasAura(SPELL_STEALTH) || bot->IsInCombat() || bot->HasSpellCooldown(SPELL_STEALTH)) return false; float distance = 30.f; @@ -63,13 +69,13 @@ bool StealthTrigger::IsActive() return target && ServerFacade::instance().GetDistance2d(bot, target) < distance; } -bool SapTrigger::IsPossible() { return bot->GetLevel() > 10 && bot->HasSpell(6770) && !bot->IsInCombat(); } +bool SapTrigger::IsPossible() { return bot->GetLevel() > 10 && botAI->HasSpell("sap") && !bot->IsInCombat(); } -bool SprintTrigger::IsPossible() { return bot->HasSpell(2983); } +bool SprintTrigger::IsPossible() { return bot->HasSpell(SPELL_SPRINT_RANK_1); } bool SprintTrigger::IsActive() { - if (bot->HasSpellCooldown(2983)) + if (bot->HasSpellCooldown(SPELL_SPRINT_RANK_1)) return false; float distance = botAI->GetMaster() ? 45.0f : 35.0f; @@ -105,7 +111,7 @@ bool SprintTrigger::IsActive() bool ExposeArmorTrigger::IsActive() { - Unit* target = AI_VALUE(Unit*, "current target"); // Get the bot's current target + Unit* target = AI_VALUE(Unit*, "current target"); return DebuffTrigger::IsActive() && !botAI->HasAura("sunder armor", target, false, false, -1, true) && AI_VALUE2(uint8, "combo", "current target") <= 3; } diff --git a/src/Ai/Class/Shaman/Strategy/TotemsShamanStrategy.cpp b/src/Ai/Class/Shaman/Strategy/TotemsShamanStrategy.cpp index c5ae222c1..083a67eef 100644 --- a/src/Ai/Class/Shaman/Strategy/TotemsShamanStrategy.cpp +++ b/src/Ai/Class/Shaman/Strategy/TotemsShamanStrategy.cpp @@ -6,6 +6,17 @@ #include "TotemsShamanStrategy.h" #include "Playerbots.h" +namespace +{ +constexpr uint32 SPELL_TOTEM_OF_WRATH = 30706; +constexpr uint32 SPELL_FLAMETONGUE_TOTEM = 8227; +constexpr uint32 SPELL_CLEANSING_TOTEM = 8170; +constexpr uint32 SPELL_MANA_SPRING_TOTEM = 5675; +constexpr uint32 SPELL_WRATH_OF_AIR_TOTEM = 3738; +constexpr uint32 SPELL_GROUNDING_TOTEM = 8177; +constexpr uint32 SPELL_WINDFURY_TOTEM = 8512; +} + // These combat strategies are used to set the corresponding totems on the bar, and cast the totem when it's missing. // There are special cases for Totem of Wrath, Windfury Totem, Wrath of Air totem, and Cleansing totem - these totems // aren't learned at level 30, and have fallbacks in order to prevent the trigger from continuously firing. @@ -74,9 +85,9 @@ void TotemOfWrathStrategy::InitTriggers(std::vector& triggers) GenericShamanStrategy::InitTriggers(triggers); // If the bot hasn't learned Totem of Wrath yet, set Flametongue Totem instead. Player* bot = botAI->GetBot(); - if (bot->HasSpell(30706)) + if (bot->HasSpell(SPELL_TOTEM_OF_WRATH)) triggers.push_back(new TriggerNode("set totem of wrath", { NextAction("set totem of wrath", 60.0f) })); - else if (bot->HasSpell(8227)) + else if (bot->HasSpell(SPELL_FLAMETONGUE_TOTEM)) triggers.push_back(new TriggerNode("set flametongue totem", { NextAction("set flametongue totem", 60.0f) })); triggers.push_back(new TriggerNode("no fire totem", { NextAction("totem of wrath", 55.0f) })); } @@ -112,9 +123,9 @@ void CleansingTotemStrategy::InitTriggers(std::vector& triggers) GenericShamanStrategy::InitTriggers(triggers); // If the bot hasn't learned Cleansing Totem yet, set Mana Spring Totem instead. Player* bot = botAI->GetBot(); - if (bot->HasSpell(8170)) + if (bot->HasSpell(SPELL_CLEANSING_TOTEM)) triggers.push_back(new TriggerNode("set cleansing totem", { NextAction("set cleansing totem", 60.0f) })); - else if (bot->HasSpell(5675)) + else if (bot->HasSpell(SPELL_MANA_SPRING_TOTEM)) triggers.push_back(new TriggerNode("set mana spring totem", { NextAction("set mana spring totem", 60.0f) })); triggers.push_back(new TriggerNode("no water totem", { NextAction("cleansing totem", 55.0f) })); } @@ -134,9 +145,9 @@ void WrathOfAirTotemStrategy::InitTriggers(std::vector& triggers) GenericShamanStrategy::InitTriggers(triggers); // If the bot hasn't learned Wrath of Air Totem yet, set Grounding Totem instead. Player* bot = botAI->GetBot(); - if (bot->HasSpell(3738)) + if (bot->HasSpell(SPELL_WRATH_OF_AIR_TOTEM)) triggers.push_back(new TriggerNode("set wrath of air totem", { NextAction("set wrath of air totem", 60.0f) })); - else if (bot->HasSpell(8177)) + else if (bot->HasSpell(SPELL_GROUNDING_TOTEM)) triggers.push_back(new TriggerNode("set grounding totem", { NextAction("set grounding totem", 60.0f) })); triggers.push_back( new TriggerNode("no air totem", { NextAction("wrath of air totem", 55.0f) })); } @@ -147,9 +158,9 @@ void WindfuryTotemStrategy::InitTriggers(std::vector& triggers) GenericShamanStrategy::InitTriggers(triggers); // If the bot hasn't learned Windfury Totem yet, set Grounding Totem instead. Player* bot = botAI->GetBot(); - if (bot->HasSpell(8512)) + if (bot->HasSpell(SPELL_WINDFURY_TOTEM)) triggers.push_back(new TriggerNode("set windfury totem", { NextAction("set windfury totem", 60.0f) })); - else if (bot->HasSpell(8177)) + else if (bot->HasSpell(SPELL_GROUNDING_TOTEM)) triggers.push_back(new TriggerNode("set grounding totem", { NextAction("set grounding totem", 60.0f) })); triggers.push_back(new TriggerNode("no air totem", { NextAction("windfury totem", 55.0f) })); } diff --git a/src/Ai/Class/Warlock/WarlockActions.cpp b/src/Ai/Class/Warlock/WarlockActions.cpp index 5e23f1af5..2deafd022 100644 --- a/src/Ai/Class/Warlock/WarlockActions.cpp +++ b/src/Ai/Class/Warlock/WarlockActions.cpp @@ -77,11 +77,7 @@ bool CastShadowflameAction::isUseful() bool CastRainOfFireAction::isUseful() { Unit* target = GetTarget(); - if (!target) - return false; - if (bot->HasSpell(27243) || bot->HasSpell(47835) || bot->HasSpell(47836)) // Seed of Corruption spell IDs - return false; - return true; + return target && !botAI->HasSpell("seed of corruption"); } // Checks if the enemies are close enough to use Hellfire diff --git a/src/Ai/Class/Warlock/WarlockTriggers.cpp b/src/Ai/Class/Warlock/WarlockTriggers.cpp index 14e7a52e5..52af8b038 100644 --- a/src/Ai/Class/Warlock/WarlockTriggers.cpp +++ b/src/Ai/Class/Warlock/WarlockTriggers.cpp @@ -104,10 +104,8 @@ bool LifeTapTrigger::IsActive() // Checks if the Life Tap Glyph buff is active bool LifeTapGlyphBuffTrigger::IsActive() { - if (!botAI->HasAura(63320, bot)) - return false; - - return BuffTrigger::IsActive(); + constexpr uint32 SPELL_LIFE_TAP_GLYPH = 63320; + return bot->HasAura(SPELL_LIFE_TAP_GLYPH) && BuffTrigger::IsActive(); } // Checks if the target has a conflicting debuff that is equal to Curse of the Elements diff --git a/src/Ai/Class/Warrior/Strategy/WarriorPullStrategy.cpp b/src/Ai/Class/Warrior/Strategy/WarriorPullStrategy.cpp index cdae7e29c..40609f5c8 100644 --- a/src/Ai/Class/Warrior/Strategy/WarriorPullStrategy.cpp +++ b/src/Ai/Class/Warrior/Strategy/WarriorPullStrategy.cpp @@ -5,23 +5,16 @@ #include "WarriorPullStrategy.h" -#include "AiObjectContext.h" -#include "Player.h" #include "PlayerbotAI.h" std::string WarriorPullStrategy::GetPullActionName() const { - Player* bot = botAI->GetBot(); Unit* target = GetTarget(); - if (!bot || !target) + if (!target) return PullStrategy::GetPullActionName(); - uint32 const heroicThrowSpellId = botAI->GetAiObjectContext()->GetValue("spell id", "heroic throw")->Get(); - if (heroicThrowSpellId && bot->HasSpell(heroicThrowSpellId) && - botAI->CanCastSpell(heroicThrowSpellId, target)) - { + if (botAI->CanCastSpell("heroic throw", target)) return "heroic throw"; - } return PullStrategy::GetPullActionName(); } diff --git a/src/Ai/Class/Warrior/WarriorActions.cpp b/src/Ai/Class/Warrior/WarriorActions.cpp index b30c1a38b..4636f7845 100644 --- a/src/Ai/Class/Warrior/WarriorActions.cpp +++ b/src/Ai/Class/Warrior/WarriorActions.cpp @@ -8,6 +8,15 @@ #include "AiFactory.h" #include "Playerbots.h" +namespace +{ +constexpr uint32 SPELL_RETALIATION = 20230; +constexpr uint32 SPELL_DIVINE_SHIELD = 642; +constexpr uint32 SPELL_ICE_BLOCK = 45438; +constexpr uint32 SPELL_BLESSING_OF_PROTECTION = 41450; +constexpr uint32 SPELL_SHATTERING_THROW = 64382; +} + bool CastBerserkerRageAction::isPossible() { if (botAI->IsInVehicle() && !botAI->IsInVehicle(false, false, true)) @@ -153,14 +162,8 @@ bool CastVigilanceAction::Execute(Event /*event*/) bool CastRetaliationAction::isUseful() { - // Spell cooldown check - if (!bot->HasSpell(20230)) - { - return false; - } - - // Spell cooldown check - if (bot->HasSpellCooldown(20230)) + if (!bot->HasSpell(SPELL_RETALIATION) || bot->HasSpellCooldown(SPELL_RETALIATION) || + bot->HasAura(SPELL_RETALIATION)) { return false; } @@ -199,8 +202,8 @@ bool CastRetaliationAction::isUseful() break; } - // Only cast Retaliation if there are at least 2 melee attackers and the buff is not active - return meleeAttackers >= 2 && !botAI->HasAura("retaliation", bot); + // Only cast Retaliation if there are at least 2 melee attackers + return meleeAttackers >= 2; } Unit* CastShatteringThrowAction::GetTarget() @@ -214,15 +217,15 @@ Unit* CastShatteringThrowAction::GetTarget() continue; if (bot->IsWithinDistInMap(enemy, 25.0f) && - (enemy->HasAura(642) || // Divine Shield - enemy->HasAura(45438) || // Ice Block - enemy->HasAura(41450))) // Blessing of Protection + (enemy->HasAura(SPELL_DIVINE_SHIELD) || + enemy->HasAura(SPELL_ICE_BLOCK) || + enemy->HasAura(SPELL_BLESSING_OF_PROTECTION))) { return enemy; } } - return nullptr; // No valid target + return nullptr; } bool CastShatteringThrowAction::Execute(Event /*event*/) @@ -236,7 +239,7 @@ bool CastShatteringThrowAction::Execute(Event /*event*/) bool CastShatteringThrowAction::isUseful() { - if (!bot->HasSpell(64382) || bot->HasSpellCooldown(64382)) + if (!bot->HasSpell(SPELL_SHATTERING_THROW) || bot->HasSpellCooldown(SPELL_SHATTERING_THROW)) return false; GuidVector enemies = AI_VALUE(GuidVector, "possible targets"); @@ -247,17 +250,16 @@ bool CastShatteringThrowAction::isUseful() if (!enemy || !enemy->IsAlive() || enemy->IsFriendlyTo(bot)) continue; - // Check if the enemy is within 25 yards and has the specific auras if (bot->IsWithinDistInMap(enemy, 25.0f) && - (enemy->HasAura(642) || // Divine Shield - enemy->HasAura(45438) || // Ice Block - enemy->HasAura(41450))) // Blessing of Protection + (enemy->HasAura(SPELL_DIVINE_SHIELD) || + enemy->HasAura(SPELL_ICE_BLOCK) || + enemy->HasAura(SPELL_BLESSING_OF_PROTECTION))) { return true; } } - return false; // No valid targets within range + return false; } bool CastShatteringThrowAction::isPossible() diff --git a/src/Ai/Class/Warrior/WarriorTriggers.cpp b/src/Ai/Class/Warrior/WarriorTriggers.cpp index f6e5bd234..e875c403c 100644 --- a/src/Ai/Class/Warrior/WarriorTriggers.cpp +++ b/src/Ai/Class/Warrior/WarriorTriggers.cpp @@ -6,6 +6,16 @@ #include "WarriorTriggers.h" #include "Playerbots.h" +namespace +{ +constexpr uint32 SPELL_VIGILANCE = 50720; +constexpr uint32 SPELL_SHATTERING_THROW = 64382; +constexpr uint32 SPELL_DIVINE_SHIELD = 642; +constexpr uint32 SPELL_ICE_BLOCK = 45438; +constexpr uint32 SPELL_BLESSING_OF_PROTECTION = 41450; +constexpr uint32 SPELL_COMMANDING_PRESENCE_RANKS[] = { 12318, 12857, 12858, 12860, 12861 }; +} + bool BloodrageBuffTrigger::IsActive() { return AI_VALUE2(uint8, "health", "self target") >= sPlayerbotAIConfig.mediumHealth && @@ -14,7 +24,7 @@ bool BloodrageBuffTrigger::IsActive() bool VigilanceTrigger::IsActive() { - if (!bot->HasSpell(50720)) + if (!bot->HasSpell(SPELL_VIGILANCE)) return false; Group* group = bot->GetGroup(); @@ -64,7 +74,7 @@ bool VigilanceTrigger::IsActive() bool ShatteringThrowTrigger::IsActive() { - if (!bot->HasSpell(64382) || bot->HasSpellCooldown(64382)) + if (!bot->HasSpell(SPELL_SHATTERING_THROW) || bot->HasSpellCooldown(SPELL_SHATTERING_THROW)) return false; GuidVector enemies = AI_VALUE(GuidVector, "possible targets"); @@ -76,9 +86,9 @@ bool ShatteringThrowTrigger::IsActive() continue; if (bot->IsWithinDistInMap(enemy, 25.0f) && - (enemy->HasAura(642) || // Divine Shield - enemy->HasAura(45438) || // Ice Block - enemy->HasAura(41450))) // Blessing of Protection + (enemy->HasAura(SPELL_DIVINE_SHIELD) || + enemy->HasAura(SPELL_ICE_BLOCK) || + enemy->HasAura(SPELL_BLESSING_OF_PROTECTION))) { return true; } @@ -112,15 +122,13 @@ bool BattleShoutTrigger::IsActive() if (!bsApValue) return false; - static const uint32 commandingPresenceSpells[] = { - 12318, 12857, 12858, 12860, 12861 }; static const float commandingPresenceBonus[] = { 0.05f, 0.10f, 0.15f, 0.20f, 0.25f }; float cpBonus = 0.0f; for (int rank = 4; rank >= 0; --rank) { - if (bot->HasAura(commandingPresenceSpells[rank])) + if (bot->HasAura(SPELL_COMMANDING_PRESENCE_RANKS[rank])) { cpBonus = commandingPresenceBonus[rank]; break; diff --git a/src/Ai/Raid/Naxx/Action/NaxxActions_Razuvious.cpp b/src/Ai/Raid/Naxx/Action/NaxxActions_Razuvious.cpp index 5d91db0c6..5d865b14f 100644 --- a/src/Ai/Raid/Naxx/Action/NaxxActions_Razuvious.cpp +++ b/src/Ai/Raid/Naxx/Action/NaxxActions_Razuvious.cpp @@ -5,6 +5,13 @@ #include "Playerbots.h" #include "SharedDefines.h" +namespace +{ +constexpr uint32 SPELL_UNDERSTUDY_TAUNT = 29060; +constexpr uint32 SPELL_BONE_BARRIER = 29061; +constexpr uint32 SPELL_BLOOD_STRIKE = 61696; +} + bool RazuviousUseObedienceCrystalAction::Execute(Event /*event*/) { if (!helper.UpdateBossAI()) @@ -42,7 +49,8 @@ bool RazuviousUseObedienceCrystalAction::Execute(Event /*event*/) bool tauntUseful = true; if (forceObedience->GetDuration() <= (duration_time - 5000)) { - if (target->GetVictim() && botAI->HasAura(29061, target->GetVictim())) + Unit* victim = target->GetVictim(); + if (victim && victim->HasAura(SPELL_BONE_BARRIER)) tauntUseful = false; if (forceObedience->GetDuration() <= 3000) @@ -55,19 +63,19 @@ bool RazuviousUseObedienceCrystalAction::Execute(Event /*event*/) if (tauntUseful && !charm->HasSpellCooldown(29060)) { // shield - if (!charm->HasSpellCooldown(29061)) + if (!charm->HasSpellCooldown(SPELL_BONE_BARRIER)) { - charm->CastSpell(charm, 29061, true); - charm->AddSpellCooldown(29061, 0, 30 * 1000); + charm->CastSpell(charm, SPELL_BONE_BARRIER, true); + charm->AddSpellCooldown(SPELL_BONE_BARRIER, 0, 30 * 1000); } - charm->CastSpell(target, 29060, true); - charm->AddSpellCooldown(29060, 0, 20 * 1000); + charm->CastSpell(target, SPELL_UNDERSTUDY_TAUNT, true); + charm->AddSpellCooldown(SPELL_UNDERSTUDY_TAUNT, 0, 20 * 1000); } // strike - if (!charm->HasSpellCooldown(61696)) + if (!charm->HasSpellCooldown(SPELL_BLOOD_STRIKE)) { - charm->CastSpell(target, 61696, true); - charm->AddSpellCooldown(61696, 0, 4 * 1000); + charm->CastSpell(target, SPELL_BLOOD_STRIKE, true); + charm->AddSpellCooldown(SPELL_BLOOD_STRIKE, 0, 4 * 1000); } } } diff --git a/src/Ai/Raid/Naxx/Action/NaxxActions_Sapphiron.cpp b/src/Ai/Raid/Naxx/Action/NaxxActions_Sapphiron.cpp index 55bcdabc4..a0861be39 100644 --- a/src/Ai/Raid/Naxx/Action/NaxxActions_Sapphiron.cpp +++ b/src/Ai/Raid/Naxx/Action/NaxxActions_Sapphiron.cpp @@ -75,7 +75,7 @@ bool SapphironFlightPositionAction::MoveToNearestIcebolt() for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) { Player* member = ref->GetSource(); - if (NaxxSpellIds::HasAnyAura(botAI, member, {NaxxSpellIds::Icebolt10, NaxxSpellIds::Icebolt25}) || + if (NaxxSpellIds::HasAnyAura(member, {NaxxSpellIds::Icebolt10, NaxxSpellIds::Icebolt25}) || botAI->HasAura("icebolt", member, false, false, -1, true)) { if (!playerWithIcebolt || minDistance > bot->GetDistance(member)) diff --git a/src/Ai/Raid/Naxx/Action/NaxxActions_Thaddius.cpp b/src/Ai/Raid/Naxx/Action/NaxxActions_Thaddius.cpp index 016a9faa5..106d43c36 100644 --- a/src/Ai/Raid/Naxx/Action/NaxxActions_Thaddius.cpp +++ b/src/Ai/Raid/Naxx/Action/NaxxActions_Thaddius.cpp @@ -111,15 +111,13 @@ bool ThaddiusMovePolarityAction::Execute(Event /*event*/) {3504.68f, -2936.68f}, }; uint32 idx; - if (NaxxSpellIds::HasAnyAura( - botAI, bot, + if (NaxxSpellIds::HasAnyAura(bot, {NaxxSpellIds::NegativeCharge10, NaxxSpellIds::NegativeCharge25, NaxxSpellIds::NegativeChargeStack}) || botAI->HasAura("negative charge", bot, false, false, -1, true)) { idx = 0; } - else if (NaxxSpellIds::HasAnyAura( - botAI, bot, + else if (NaxxSpellIds::HasAnyAura(bot, {NaxxSpellIds::PositiveCharge10, NaxxSpellIds::PositiveCharge25, NaxxSpellIds::PositiveChargeStack}) || botAI->HasAura("positive charge", bot, false, false, -1, true)) { diff --git a/src/Ai/Raid/Naxx/NaxxBossHelper.h b/src/Ai/Raid/Naxx/NaxxBossHelper.h index 7bec3a7df..2df40a701 100644 --- a/src/Ai/Raid/Naxx/NaxxBossHelper.h +++ b/src/Ai/Raid/Naxx/NaxxBossHelper.h @@ -192,7 +192,7 @@ public: { Player* member = ref->GetSource(); if (member && - (NaxxSpellIds::HasAnyAura(botAI, member, {NaxxSpellIds::Icebolt10, NaxxSpellIds::Icebolt25}) || + (NaxxSpellIds::HasAnyAura(member, {NaxxSpellIds::Icebolt10, NaxxSpellIds::Icebolt25}) || botAI->HasAura("icebolt", member, false, false, -1, true))) { return true; diff --git a/src/Ai/Raid/Naxx/NaxxMultipliers.cpp b/src/Ai/Raid/Naxx/NaxxMultipliers.cpp index b7700eb1b..1c2cf05bf 100644 --- a/src/Ai/Raid/Naxx/NaxxMultipliers.cpp +++ b/src/Ai/Raid/Naxx/NaxxMultipliers.cpp @@ -236,7 +236,7 @@ float AnubrekhanGenericMultiplier::GetValue(Action* action) return 1.0f; if (NaxxSpellIds::HasAnyAura( - botAI, boss, {NaxxSpellIds::LocustSwarm10, NaxxSpellIds::LocustSwarm10Alt, NaxxSpellIds::LocustSwarm25}) || + boss, {NaxxSpellIds::LocustSwarm10, NaxxSpellIds::LocustSwarm10Alt, NaxxSpellIds::LocustSwarm25}) || botAI->HasAura("locust swarm", boss)) { if (dynamic_cast(action)) diff --git a/src/Ai/Raid/Naxx/NaxxSpellIds.h b/src/Ai/Raid/Naxx/NaxxSpellIds.h index 94c013eb7..2a7308d81 100644 --- a/src/Ai/Raid/Naxx/NaxxSpellIds.h +++ b/src/Ai/Raid/Naxx/NaxxSpellIds.h @@ -123,14 +123,14 @@ namespace NaxxSpellIds SPELL_INEVITABLE_DOOM = 29204, SPELL_BERSERK = 26662 */ - inline bool HasAnyAura(PlayerbotAI* botAI, Unit* unit, std::initializer_list spellIds) + inline bool HasAnyAura(Unit* unit, std::initializer_list spellIds) { - if (!botAI || !unit) + if (!unit) return false; for (uint32 spellId : spellIds) { - if (botAI->HasAura(spellId, unit)) + if (unit->HasAura(spellId)) return true; } return false; diff --git a/src/Ai/Raid/Uld/UldTriggers.cpp b/src/Ai/Raid/Uld/UldTriggers.cpp index 7c84043ef..102bf0f59 100644 --- a/src/Ai/Raid/Uld/UldTriggers.cpp +++ b/src/Ai/Raid/Uld/UldTriggers.cpp @@ -1531,7 +1531,7 @@ bool VezaxShadowCrashTrigger::IsActive() if (!boss || !boss->IsAlive()) return false; - return botAI->HasAura(SPELL_VEZAX_SHADOW_CRASH, bot); + return bot->HasAura(SPELL_VEZAX_SHADOW_CRASH); } bool VezaxMarkOfTheFacelessTrigger::IsActive() @@ -1542,7 +1542,7 @@ bool VezaxMarkOfTheFacelessTrigger::IsActive() if (!boss || !boss->IsAlive()) return false; - if (!botAI->HasAura(SPELL_MARK_OF_THE_FACELESS, bot)) + if (!bot->HasAura(SPELL_MARK_OF_THE_FACELESS)) return false; float distance = bot->GetDistance2d(ULDUAR_VEZAX_MARK_OF_THE_FACELESS_SPOT.GetPositionX(), diff --git a/src/Bot/Factory/AiFactory.cpp b/src/Bot/Factory/AiFactory.cpp index b3abbd8ad..d54244994 100644 --- a/src/Bot/Factory/AiFactory.cpp +++ b/src/Bot/Factory/AiFactory.cpp @@ -26,6 +26,15 @@ #include "WarlockAiObjectContext.h" #include "WarriorAiObjectContext.h" +namespace +{ +constexpr uint32 SPELL_FROSTFIRE_BOLT = 44614; +constexpr uint32 SPELL_ICE_SHARDS = 15047; +constexpr uint32 SPELL_WHIRLWIND = 1680; +constexpr uint32 SPELL_CAT_FORM = 768; +constexpr uint32 SPELL_DRUID_THICK_HIDE = 16931; +} + AiObjectContext* AiFactory::createAiObjectContext(Player* player, PlayerbotAI* botAI) { switch (player->getClass()) @@ -300,7 +309,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa engine->addStrategiesNoInit("arcane", "bdps", nullptr); else if (tab == MAGE_TAB_FIRE) { - if (player->HasSpell(44614) /*Frostfire Bolt*/ && player->HasAura(15047) /*Ice Shards*/) + if (player->HasSpell(SPELL_FROSTFIRE_BOLT) && player->HasAura(SPELL_ICE_SHARDS)) engine->addStrategiesNoInit("frostfire", "bdps", nullptr); else engine->addStrategiesNoInit("fire", "bdps", nullptr); @@ -313,7 +322,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa case CLASS_WARRIOR: if (tab == WARRIOR_TAB_PROTECTION) engine->addStrategiesNoInit("tank", "tank assist", "pull", "pull back", "aoe", nullptr); - else if (tab == WARRIOR_TAB_ARMS || !player->HasSpell(1680)) // Whirlwind + else if (tab == WARRIOR_TAB_ARMS || !player->HasSpell(SPELL_WHIRLWIND)) engine->addStrategiesNoInit("arms", "aoe", "dps assist", nullptr); else // if (tab == WARRIOR_TAB_FURY) engine->addStrategiesNoInit("fury", "aoe", "dps assist", nullptr); @@ -345,7 +354,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa engine->addStrategiesNoInit("resto", "cure", "dps assist", "blanketing", "tranquility", nullptr); else { - if (player->HasSpell(768) /*cat form*/ && !player->HasAura(16931) /*thick hide*/) + if (player->HasSpell(SPELL_CAT_FORM) && !player->HasAura(SPELL_DRUID_THICK_HIDE)) engine->addStrategiesNoInit("cat", "aoe", "cc", "dps assist", "feral charge", nullptr); else engine->addStrategiesNoInit("bear", "tank assist", "pull", "pull back", "feral charge", nullptr); @@ -535,7 +544,7 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const case CLASS_DRUID: if (tab == DRUID_TAB_FERAL) { - if (player->GetLevel() >= 20 && !player->HasAura(16931) /*thick hide*/) + if (player->GetLevel() >= 20 && !player->HasAura(SPELL_DRUID_THICK_HIDE)) nonCombatEngine->addStrategy("dps assist", false); else nonCombatEngine->addStrategiesNoInit("tank assist", "pull", nullptr); diff --git a/src/Bot/Factory/PlayerbotFactory.cpp b/src/Bot/Factory/PlayerbotFactory.cpp index 06e3e8405..0abfecfa9 100644 --- a/src/Bot/Factory/PlayerbotFactory.cpp +++ b/src/Bot/Factory/PlayerbotFactory.cpp @@ -61,6 +61,56 @@ std::vector PlayerbotFactory::enchantGemIdCache; std::unordered_map> PlayerbotFactory::trainerIdCache; std::vector PlayerbotFactory::ccBreakTrinketCache; +namespace +{ +constexpr uint32 SPELL_DRUID_THICK_HIDE = 16931; +constexpr uint32 SPELL_OWLKIN_FRENZY = 48393; +constexpr uint32 SPELL_PRIMAL_TENACITY = 33957; +constexpr uint32 SPELL_IMPROVED_BARKSKIN = 63411; + +constexpr uint32 SPELL_SECOND_WIND = 29838; +constexpr uint32 SPELL_BLOOD_CRAZE = 16492; +constexpr uint32 SPELL_GAG_ORDER = 12958; + +constexpr uint32 SPELL_SACRED_CLEANSING = 53553; +constexpr uint32 SPELL_RECKONING = 20179; +constexpr uint32 SPELL_DIVINE_PURPOSE = 31872; + +constexpr uint32 SPELL_HUNTER_THICK_HIDE = 19612; +constexpr uint32 SPELL_CONCUSSIVE_BARRAGE = 35102; +constexpr uint32 SPELL_ENTRAPMENT = 19388; + +constexpr uint32 SPELL_DEADLY_BREW = 51626; +constexpr uint32 SPELL_THROWING_SPECIALIZATION = 51679; +constexpr uint32 SPELL_WAYLAY = 51696; + +constexpr uint32 SPELL_IMPROVED_MANA_BURN = 14772; +constexpr uint32 SPELL_BODY_AND_SOUL = 64129; +constexpr uint32 SPELL_IMPROVED_VAMPIRIC_EMBRACE = 27840; + +constexpr uint32 SPELL_ABOMINATIONS_MIGHT = 53138; +constexpr uint32 SPELL_IMPROVED_ICY_TALONS = 55610; +constexpr uint32 SPELL_SUDDEN_DOOM = 49529; +constexpr uint32 SPELL_ACCLIMATION = 50152; +constexpr uint32 SPELL_MAGIC_SUPPRESSION = 49611; + +constexpr uint32 SPELL_SHAMAN_DUAL_WIELD = 30798; +constexpr uint32 SPELL_ASTRAL_SHIFT = 51479; +constexpr uint32 SPELL_EARTHEN_POWER = 51524; +constexpr uint32 SPELL_FOCUSED_MIND = 30866; + +constexpr uint32 SPELL_BURNOUT = 44472; +constexpr uint32 SPELL_ICE_SHARDS = 15047; +constexpr uint32 SPELL_IMPROVED_BLINK = 31570; +constexpr uint32 SPELL_FIERY_PAYBACK = 64357; +constexpr uint32 SPELL_SHATTERED_BARRIER = 54787; + +constexpr uint32 SPELL_IMPROVED_HOWL_OF_TERROR = 30057; +constexpr uint32 SPELL_NEMESIS = 63123; +constexpr uint32 SPELL_INTENSITY = 18136; +constexpr uint32 SPELL_NETHER_PROTECTION = 30302; +} + bool PlayerbotFactory::IsPrimaryTradeSkill(uint16 skillId) { SkillLineEntry const* skillLine = sSkillLineStore.LookupEntry(skillId); @@ -1440,7 +1490,7 @@ uint32 PlayerbotFactory::InitTalentsTree(bool increment /*false*/, bool use_temp /// @todo: fix cat druid hardcode if (bot->getClass() == CLASS_DRUID && specTab == DRUID_TAB_FERAL && bot->GetLevel() >= 20) { - bool isCat = !bot->HasAura(16931); + bool isCat = !bot->HasAura(SPELL_DRUID_THICK_HIDE); if (!isCat && bot->GetLevel() == 20) { uint32 bearP = sPlayerbotAIConfig.randomClassSpecProb[cls][1]; @@ -1495,7 +1545,7 @@ uint32 PlayerbotFactory::InitTalentsTree(bool increment /*false*/, bool use_temp if (bot->GetFreeTalentPoints()) InitTalents((specTab + 2) % 3); - if (bot->getClass() == CLASS_SHAMAN && bot->HasSpell(30798)) + if (bot->getClass() == CLASS_SHAMAN && bot->HasSpell(SPELL_SHAMAN_DUAL_WIELD)) { bot->SetSkill(SKILL_DUAL_WIELD, 0, 1, 1); bot->SetCanDualWield(true); @@ -1581,7 +1631,7 @@ void PlayerbotFactory::InitTalentsBySpecNo(Player* bot, int specNo, bool reset) } } - if (bot->getClass() == CLASS_SHAMAN && bot->HasSpell(30798)) + if (bot->getClass() == CLASS_SHAMAN && bot->HasSpell(SPELL_SHAMAN_DUAL_WIELD)) { bot->SetSkill(SKILL_DUAL_WIELD, 0, 1, 1); bot->SetCanDualWield(true); @@ -4170,13 +4220,13 @@ void PlayerbotFactory::InitGlyphs(bool increment) if (bot->getClass() == CLASS_WARRIOR) { // Arms PvP (spec index 3): If the bot has the Second Wind talent - if (bot->HasAura(29838)) + if (bot->HasAura(SPELL_SECOND_WIND)) tab = 3; // Fury PvP (spec index 4): If the bot has the Blood Craze talent - else if (bot->HasAura(16492)) + else if (bot->HasAura(SPELL_BLOOD_CRAZE)) tab = 4; // Protection PvP (spec index 5): If the bot has the Gag Order talent - else if (bot->HasAura(12958)) + else if (bot->HasAura(SPELL_GAG_ORDER)) tab = 5; } @@ -4184,13 +4234,13 @@ void PlayerbotFactory::InitGlyphs(bool increment) if (bot->getClass() == CLASS_PALADIN) { // Holy PvP (spec index 3): If the bot has the Sacred Cleansing talent - if (bot->HasAura(53553)) + if (bot->HasAura(SPELL_SACRED_CLEANSING)) tab = 3; // Protection PvP (spec index 4): If the bot has the Reckoning talent - else if (bot->HasAura(20179)) + else if (bot->HasAura(SPELL_RECKONING)) tab = 4; // Retribution PvP (spec index 5): If the bot has the Divine Purpose talent - else if (bot->HasAura(31872)) + else if (bot->HasAura(SPELL_DIVINE_PURPOSE)) tab = 5; } @@ -4198,13 +4248,13 @@ void PlayerbotFactory::InitGlyphs(bool increment) if (bot->getClass() == CLASS_HUNTER) { // Beast Mastery PvP (spec index 3): If the bot has the Thick Hide talent - if (bot->HasAura(19612)) + if (bot->HasAura(SPELL_HUNTER_THICK_HIDE)) tab = 3; // Marksmanship PvP (spec index 4): If the bot has the Concussive Barrage talent - else if (bot->HasAura(35102)) + else if (bot->HasAura(SPELL_CONCUSSIVE_BARRAGE)) tab = 4; // Survival PvP (spec index 5): If the bot has the Entrapment talent and does NOT have the Concussive Barrage talent - else if (bot->HasAura(19388) && !bot->HasAura(35102)) + else if (bot->HasAura(SPELL_ENTRAPMENT) && !bot->HasAura(SPELL_CONCUSSIVE_BARRAGE)) tab = 5; } @@ -4212,13 +4262,13 @@ void PlayerbotFactory::InitGlyphs(bool increment) if (bot->getClass() == CLASS_ROGUE) { // Assassination PvP (spec index 3): If the bot has the Deadly Brew talent - if (bot->HasAura(51626)) + if (bot->HasAura(SPELL_DEADLY_BREW)) tab = 3; // Combat PvP (spec index 4): If the bot has the Throwing Specialization talent - else if (bot->HasAura(51679)) + else if (bot->HasAura(SPELL_THROWING_SPECIALIZATION)) tab = 4; // Subtlety PvP (spec index 5): If the bot has the Waylay talent - else if (bot->HasAura(51696)) + else if (bot->HasAura(SPELL_WAYLAY)) tab = 5; } @@ -4226,13 +4276,13 @@ void PlayerbotFactory::InitGlyphs(bool increment) if (bot->getClass() == CLASS_PRIEST) { // Discipline PvP (spec index 3): If the bot has the Improved Mana Burn talent - if (bot->HasAura(14772)) + if (bot->HasAura(SPELL_IMPROVED_MANA_BURN)) tab = 3; // Holy PvP (spec index 4): If the bot has the Body and Soul talent - else if (bot->HasAura(64129)) + else if (bot->HasAura(SPELL_BODY_AND_SOUL)) tab = 4; // Shadow PvP (spec index 5): If the bot has the Improved Vampiric Embrace talent - else if (bot->HasAura(27840)) + else if (bot->HasAura(SPELL_IMPROVED_VAMPIRIC_EMBRACE)) tab = 5; } @@ -4241,16 +4291,16 @@ void PlayerbotFactory::InitGlyphs(bool increment) { // Double Aura Blood PvE (spec index 3): If the bot has both the Abomination's Might and Improved Icy Talons // talents - if (bot->HasAura(53138) && bot->HasAura(55610)) + if (bot->HasAura(SPELL_ABOMINATIONS_MIGHT) && bot->HasAura(SPELL_IMPROVED_ICY_TALONS)) tab = 3; // Blood PvP (spec index 4): If the bot has the Sudden Doom talent - else if (bot->HasAura(49529)) + else if (bot->HasAura(SPELL_SUDDEN_DOOM)) tab = 4; // Frost PvP (spec index 5): If the bot has the Acclimation talent - else if (bot->HasAura(50152)) + else if (bot->HasAura(SPELL_ACCLIMATION)) tab = 5; // Unholy PvP (spec index 6): If the bot has the Magic Suppression talent - else if (bot->HasAura(49611)) + else if (bot->HasAura(SPELL_MAGIC_SUPPRESSION)) tab = 6; } @@ -4258,13 +4308,13 @@ void PlayerbotFactory::InitGlyphs(bool increment) if (bot->getClass() == CLASS_SHAMAN) { // Elemental PvP (spec index 3): If the bot has the Astral Shift talent - if (bot->HasAura(51479)) + if (bot->HasAura(SPELL_ASTRAL_SHIFT)) tab = 3; // Enhancement PvP (spec index 4): If the bot has the Earthen Power talent - else if (bot->HasAura(51524)) + else if (bot->HasAura(SPELL_EARTHEN_POWER)) tab = 4; // Restoration PvP (spec index 5): If the bot has the Focused Mind talent - else if (bot->HasAura(30866)) + else if (bot->HasAura(SPELL_FOCUSED_MIND)) tab = 5; } @@ -4272,16 +4322,16 @@ void PlayerbotFactory::InitGlyphs(bool increment) if (bot->getClass() == CLASS_MAGE) { // Frostfire PvE (spec index 3): If the bot has both the Burnout talent and the Ice Shards talent - if (bot->HasAura(44472) && bot->HasAura(15047)) + if (bot->HasAura(SPELL_BURNOUT) && bot->HasAura(SPELL_ICE_SHARDS)) tab = 3; // Arcane PvP (spec index 4): If the bot has the Improved Blink talent - else if (bot->HasAura(31570)) + else if (bot->HasAura(SPELL_IMPROVED_BLINK)) tab = 4; // Fire PvP (spec index 5): If the bot has the Fiery Payback talent - else if (bot->HasAura(64357)) + else if (bot->HasAura(SPELL_FIERY_PAYBACK)) tab = 5; // Frost PvP (spec index 6): If the bot has the Shattered Barrier talent - else if (bot->HasAura(54787)) + else if (bot->HasAura(SPELL_SHATTERED_BARRIER)) tab = 6; } @@ -4289,13 +4339,13 @@ void PlayerbotFactory::InitGlyphs(bool increment) if (bot->getClass() == CLASS_WARLOCK) { // Affliction PvP (spec index 3): If the bot has the Improved Howl of Terror talent - if (bot->HasAura(30057)) + if (bot->HasAura(SPELL_IMPROVED_HOWL_OF_TERROR)) tab = 3; // Demonology PvP (spec index 4): If the bot has both the Nemesis talent and the Intensity talent - else if (bot->HasAura(63123) && bot->HasAura(18136)) + else if (bot->HasAura(SPELL_NEMESIS) && bot->HasAura(SPELL_INTENSITY)) tab = 4; // Destruction PvP (spec index 5): If the bot has the Nether Protection talent - else if (bot->HasAura(30302)) + else if (bot->HasAura(SPELL_NETHER_PROTECTION)) tab = 5; } @@ -4303,16 +4353,16 @@ void PlayerbotFactory::InitGlyphs(bool increment) if (bot->getClass() == CLASS_DRUID) { // Cat PvE (spec index 3): If the bot is Feral spec, level 20 or higher, and does NOT have the Thick Hide talent - if (tab == DRUID_TAB_FERAL && bot->GetLevel() >= 20 && !bot->HasAura(16931)) + if (tab == DRUID_TAB_FERAL && bot->GetLevel() >= 20 && !bot->HasAura(SPELL_DRUID_THICK_HIDE)) tab = 3; // Balance PvP (spec index 4): If the bot has the Owlkin Frenzy talent - else if (bot->HasAura(48393)) + else if (bot->HasAura(SPELL_OWLKIN_FRENZY)) tab = 4; // Feral PvP (spec index 5): If the bot has the Primal Tenacity talent - else if (bot->HasAura(33957)) + else if (bot->HasAura(SPELL_PRIMAL_TENACITY)) tab = 5; // Resto PvP (spec index 6): If the bot has the Improved Barkskin talent - else if (bot->HasAura(63411)) + else if (bot->HasAura(SPELL_IMPROVED_BARKSKIN)) tab = 6; } diff --git a/src/Bot/PlayerbotAI.cpp b/src/Bot/PlayerbotAI.cpp index eeadbbc55..00f878686 100644 --- a/src/Bot/PlayerbotAI.cpp +++ b/src/Bot/PlayerbotAI.cpp @@ -3152,20 +3152,10 @@ bool PlayerbotAI::HasAura(std::string const name, Unit* unit, bool maxStack, boo return false; } -bool PlayerbotAI::HasAura(uint32 spellId, Unit const* unit) +bool PlayerbotAI::HasSpell(std::string const spellName) const { - if (!spellId || !unit) - return false; - - return unit->HasAura(spellId); - // for (uint8 effect = EFFECT_0; effect <= EFFECT_2; effect++) - // { - // AuraEffect const* aurEff = unit->GetAuraEffect(spellId, effect); - // if (IsRealAura(bot, aurEff, unit)) - // return true; - // } - - // return false; + uint32 const spellId = aiObjectContext->GetValue("spell id", spellName)->Get(); + return spellId && bot->HasSpell(spellId); } Aura* PlayerbotAI::GetAura(std::string const name, Unit* unit, bool checkIsOwner, bool checkDuration, int checkStack) @@ -4269,7 +4259,7 @@ void PlayerbotAI::InterruptSpell() void PlayerbotAI::RemoveAura(std::string const name) { uint32 spellid = aiObjectContext->GetValue("spell id", name)->Get(); - if (spellid && HasAura(spellid, bot)) + if (spellid && bot->HasAura(spellid)) bot->RemoveAurasDueToSpell(spellid); } diff --git a/src/Bot/PlayerbotAI.h b/src/Bot/PlayerbotAI.h index 2e31c9a9e..033b6aaeb 100644 --- a/src/Bot/PlayerbotAI.h +++ b/src/Bot/PlayerbotAI.h @@ -497,6 +497,7 @@ public: virtual bool CanCastSpell(std::string const name, Unit* target, Item* itemTarget = nullptr); virtual bool CastSpell(std::string const name, Unit* target, Item* itemTarget = nullptr); + virtual bool HasSpell(std::string const spellName) const; virtual bool HasAura(std::string const spellName, Unit* player, bool maxStack = false, bool checkIsOwner = false, int maxAmount = -1, bool checkDuration = false); virtual bool HasAnyAuraOf(Unit* player, ...); @@ -509,7 +510,6 @@ public: bool CanCastSpell(uint32 spellid, float x, float y, float z, bool checkHasSpell = true, Item* itemTarget = nullptr); - bool HasAura(uint32 spellId, Unit const* player); Aura* GetAura(std::string const spellName, Unit* unit, bool checkIsOwner = false, bool checkDuration = false, int checkStack = -1); bool CastSpell(uint32 spellId, Unit* target, Item* itemTarget = nullptr); diff --git a/src/Mgr/Item/StatsWeightCalculator.cpp b/src/Mgr/Item/StatsWeightCalculator.cpp index 2e11f0a38..4e61dd09e 100644 --- a/src/Mgr/Item/StatsWeightCalculator.cpp +++ b/src/Mgr/Item/StatsWeightCalculator.cpp @@ -23,13 +23,29 @@ namespace { -constexpr uint32 SPELL_MOLTEN_ARMOR_RANK_1 = 30482; -constexpr uint32 SPELL_MOLTEN_ARMOR_RANK_2 = 43045; -constexpr uint32 SPELL_MOLTEN_ARMOR_RANK_3 = 43046; -constexpr uint32 SPELL_FEL_ARMOR_RANK_1 = 28176; -constexpr uint32 SPELL_FEL_ARMOR_RANK_2 = 28189; -constexpr uint32 SPELL_FEL_ARMOR_RANK_3 = 47892; -constexpr uint32 SPELL_FEL_ARMOR_RANK_4 = 47893; +constexpr uint32 SPELL_MOLTEN_ARMOR_RANKS[] = { 30482, 43045, 43046 }; +constexpr uint32 SPELL_FEL_ARMOR_RANKS[] = { 28176, 28189, 47892, 47893 }; +constexpr uint32 SPELL_CAREFUL_AIM = 34484; +constexpr uint32 SPELL_HUNTER_VS_WILD = 56341; +constexpr uint32 SPELL_ARMORED_TO_THE_TEETH = 61222; +constexpr uint32 SPELL_MENTAL_DEXTERITY = 51885; +constexpr uint32 SPELL_ROGUE_SWORD_SPECIALIZATION = 13964; +constexpr uint32 SPELL_POLEAXE_SPECIALIZATION = 12785; +constexpr uint32 SPELL_NERVES_OF_COLD_STEEL = 50138; +constexpr uint32 SPELL_SHADOW_FOCUS = 15835; +constexpr uint32 SPELL_ARCANE_FOCUS = 12840; +} + +template +bool HasAnySpell(Player* player, uint32 const (&spellIds)[Size]) +{ + for (uint32 const spellId : spellIds) + { + if (player->HasSpell(spellId)) + return true; + } + + return false; } StatsWeightCalculator::StatsWeightCalculator(Player* player) : player_(player) @@ -512,26 +528,24 @@ void StatsWeightCalculator::GenerateAdditionalWeights(Player* player) // int tab = AiFactory::GetPlayerSpecTab(player); if (cls == CLASS_HUNTER) { - if (player->HasAura(34484)) + if (player->HasAura(SPELL_CAREFUL_AIM)) stats_weights_[STATS_TYPE_INTELLECT] += 1.1f; - if (player->HasAura(56341)) + if (player->HasAura(SPELL_HUNTER_VS_WILD)) stats_weights_[STATS_TYPE_STAMINA] += 0.3f; } else if (cls == CLASS_WARRIOR) { - if (player->HasAura(61222)) + if (player->HasAura(SPELL_ARMORED_TO_THE_TEETH)) stats_weights_[STATS_TYPE_ARMOR] += 0.03f; } else if (cls == CLASS_SHAMAN) { - if (player->HasAura(51885)) + if (player->HasAura(SPELL_MENTAL_DEXTERITY)) stats_weights_[STATS_TYPE_INTELLECT] += 1.1f; } else if (cls == CLASS_MAGE) { - if (!player->HasSpell(SPELL_MOLTEN_ARMOR_RANK_1) - && !player->HasSpell(SPELL_MOLTEN_ARMOR_RANK_2) - && !player->HasSpell(SPELL_MOLTEN_ARMOR_RANK_3)) + if (!HasAnySpell(player, SPELL_MOLTEN_ARMOR_RANKS)) { if (tab != MAGE_TAB_FIRE) stats_weights_[STATS_TYPE_SPIRIT] -= 0.6f; @@ -541,8 +555,7 @@ void StatsWeightCalculator::GenerateAdditionalWeights(Player* player) } else if (cls == CLASS_WARLOCK) { - if (!player->HasSpell(SPELL_FEL_ARMOR_RANK_1) && !player->HasSpell(SPELL_FEL_ARMOR_RANK_2) && - !player->HasSpell(SPELL_FEL_ARMOR_RANK_3) && !player->HasSpell(SPELL_FEL_ARMOR_RANK_4)) + if (!HasAnySpell(player, SPELL_FEL_ARMOR_RANKS)) stats_weights_[STATS_TYPE_SPIRIT] -= 0.4f; } @@ -690,17 +703,17 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto) weight_ *= 1.5; } - if (cls == CLASS_ROGUE && player_->HasAura(13964) && + if (cls == CLASS_ROGUE && player_->HasAura(SPELL_ROGUE_SWORD_SPECIALIZATION) && (proto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD || proto->SubClass == ITEM_SUBCLASS_WEAPON_AXE)) { weight_ *= 1.1; } - if (cls == CLASS_WARRIOR && player_->HasAura(12785) && + if (cls == CLASS_WARRIOR && player_->HasAura(SPELL_POLEAXE_SPECIALIZATION) && (proto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM || proto->SubClass == ITEM_SUBCLASS_WEAPON_AXE2)) { weight_ *= 1.1; } - if (cls == CLASS_DEATH_KNIGHT && player_->HasAura(50138) && !isDoubleHand) + if (cls == CLASS_DEATH_KNIGHT && player_->HasAura(SPELL_NERVES_OF_COLD_STEEL) && !isDoubleHand) { weight_ *= 1.3; } @@ -739,9 +752,9 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player) player->GetTotalAuraModifier(SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT); // suppression (18176) hit_current += player->GetRatingBonusValue(CR_HIT_SPELL); - if (cls == CLASS_PRIEST && tab == PRIEST_TAB_SHADOW && player->HasAura(15835)) // Shadow Focus + if (cls == CLASS_PRIEST && tab == PRIEST_TAB_SHADOW && player->HasAura(SPELL_SHADOW_FOCUS)) hit_current += 3; - if (cls == CLASS_MAGE && tab == MAGE_TAB_ARCANE && player->HasAura(12840)) // Arcane Focus + if (cls == CLASS_MAGE && tab == MAGE_TAB_ARCANE && player->HasAura(SPELL_ARCANE_FOCUS)) hit_current += 3; hit_overflow = SPELL_HIT_OVERFLOW;