Merge pull request #2467 from mod-playerbots/test-staging

Merge test-staging into master
This commit is contained in:
Keleborn 2026-06-12 07:24:30 -07:00 committed by GitHub
commit 085e127e38
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
36 changed files with 361 additions and 299 deletions

View File

@ -17,6 +17,7 @@
#include "SpellAuraEffects.h" #include "SpellAuraEffects.h"
static constexpr uint32 SPELL_COLD_WEATHER_FLYING = 54197; 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 // Define the static map / init bool for caching bot preferred mount data globally
std::unordered_map<uint32, PreferredMountCache> CheckMountStateAction::mountCache; std::unordered_map<uint32, PreferredMountCache> CheckMountStateAction::mountCache;
@ -61,6 +62,21 @@ MountData CollectMountData(const Player* bot)
bool CheckMountStateAction::Execute(Event /*event*/) 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 // Determine if there are no attackers
bool noAttackers = !AI_VALUE2(bool, "combat", "self target") || !AI_VALUE(uint8, "attacker count"); bool noAttackers = !AI_VALUE2(bool, "combat", "self target") || !AI_VALUE(uint8, "attacker count");
bool enemy = AI_VALUE(Unit*, "enemy player target"); bool enemy = AI_VALUE(Unit*, "enemy player target");
@ -204,7 +220,7 @@ bool CheckMountStateAction::Mount()
// Get bot mount data // Get bot mount data
MountData mountData = CollectMountData(bot); MountData mountData = CollectMountData(bot);
int32 masterMountType = GetMountType(master); int32 masterMountType = GetMountType(master);
int32 masterSpeed = CalculateMasterMountSpeed(master, mountData); int32 masterSpeed = CalculateMasterMountSpeed(master);
// Try shapeshift // Try shapeshift
if (TryForms(master, masterMountType, masterSpeed)) if (TryForms(master, masterMountType, masterSpeed))
@ -234,14 +250,17 @@ void CheckMountStateAction::Dismount()
WorldPacket emptyPacket; WorldPacket emptyPacket;
bot->GetSession()->HandleCancelMountAuraOpcode(emptyPacket); bot->GetSession()->HandleCancelMountAuraOpcode(emptyPacket);
bool const wantsFly = bot->HasIncreaseMountedFlightSpeedAura() || bot->HasFlyAura(); ClearStaleFlightFlags();
bool const isWaterWalking = bot->HasUnitMovementFlag(MOVEMENTFLAG_WATERWALKING); }
bool const isFlying = bot->HasUnitMovementFlag(MOVEMENTFLAG_FLYING);
bool const hasGravityDisabled = bot->HasUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY); void CheckMountStateAction::ClearStaleFlightFlags()
if (!wantsFly && !isWaterWalking && (isFlying || hasGravityDisabled)) {
if (bot->HasIncreaseMountedFlightSpeedAura() || bot->HasFlyAura())
return;
if (bot->HasUnitMovementFlag(MOVEMENTFLAG_FLYING | MOVEMENTFLAG_DISABLE_GRAVITY))
{ {
bot->RemoveUnitMovementFlag( bot->RemoveUnitMovementFlag(MOVEMENTFLAG_FLYING | MOVEMENTFLAG_DISABLE_GRAVITY | MOVEMENTFLAG_CAN_FLY);
MOVEMENTFLAG_FLYING | MOVEMENTFLAG_CAN_FLY | MOVEMENTFLAG_DISABLE_GRAVITY);
if (!bot->IsRooted()) if (!bot->IsRooted())
bot->SendMovementFlagUpdate(); bot->SendMovementFlagUpdate();
} }
@ -490,7 +509,7 @@ static bool BotCanUseFlyingMount(Player const* bot)
return true; return true;
} }
int32 CheckMountStateAction::CalculateMasterMountSpeed(Player* master, const MountData& mountData) const int32 CheckMountStateAction::CalculateMasterMountSpeed(Player* master) const
{ {
// Check riding skill and level requirements // Check riding skill and level requirements
int32 ridingSkill = bot->GetPureSkillValue(SKILL_RIDING); int32 ridingSkill = bot->GetPureSkillValue(SKILL_RIDING);

View File

@ -53,9 +53,10 @@ private:
float CalculateDismountDistance() const; float CalculateDismountDistance() const;
float CalculateMountDistance() const; float CalculateMountDistance() const;
void Dismount(); void Dismount();
void ClearStaleFlightFlags();
bool ShouldFollowMasterMountState(Player* master, bool noAttackers, bool shouldMount) const; bool ShouldFollowMasterMountState(Player* master, bool noAttackers, bool shouldMount) const;
bool ShouldDismountForMaster(Player* master) const; bool ShouldDismountForMaster(Player* master) const;
int32 CalculateMasterMountSpeed(Player* master, const MountData& mountData) const; int32 CalculateMasterMountSpeed(Player* master) const;
bool CheckForSwiftMount() const; bool CheckForSwiftMount() const;
std::map<uint32, std::map<int32, std::vector<uint32>>> GetAllMountSpells() const; std::map<uint32, std::map<int32, std::vector<uint32>>> GetAllMountSpells() const;
bool TryForms(Player* master, int32 masterMountType, int32 masterSpeed) const; bool TryForms(Player* master, int32 masterMountType, int32 masterSpeed) const;

View File

@ -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 // Remove the current pet and (re-)cast Call Pet spell if the bot is a hunter
bot->RemovePet(nullptr, PET_SAVE_AS_CURRENT, true); bot->RemovePet(nullptr, PET_SAVE_AS_CURRENT, true);
if (bot->getClass() == CLASS_HUNTER && bot->HasSpell(883)) constexpr uint32 SPELL_CALL_PET = 883;
{ if (bot->getClass() == CLASS_HUNTER && bot->HasSpell(SPELL_CALL_PET))
bot->CastSpell(bot, 883, true); bot->CastSpell(bot, SPELL_CALL_PET, true);
}
return true; return true;
} }

View File

@ -67,9 +67,10 @@ bool TradeStatusExtendedAction::Execute(Event event)
return false; 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->DoSpecificAction("unlock traded item");
botAI->SetNextCheckDelay(4000); // Delay before accepting trade botAI->SetNextCheckDelay(4000); // Delay before accepting trade
} }

View File

@ -23,9 +23,7 @@ bool BossFireResistanceTrigger::IsActive()
return false; return false;
// Check if bot have fire resistance aura // Check if bot have fire resistance aura
if (bot->HasAura(SPELL_FIRE_RESISTANCE_AURA_RANK_5) || bot->HasAura(SPELL_FIRE_RESISTANCE_AURA_RANK_4) || if (botAI->HasAura("fire resistance aura", bot))
bot->HasAura(SPELL_FIRE_RESISTANCE_AURA_RANK_3) || bot->HasAura(SPELL_FIRE_RESISTANCE_AURA_RANK_2) ||
bot->HasAura(SPELL_FIRE_RESISTANCE_AURA_RANK_1))
return false; return false;
// Check if bot dont have already have fire resistance strategy // Check if bot dont have already have fire resistance strategy
@ -76,9 +74,7 @@ bool BossFrostResistanceTrigger::IsActive()
return false; return false;
// Check if bot have frost resistance aura // Check if bot have frost resistance aura
if (bot->HasAura(SPELL_FROST_RESISTANCE_AURA_RANK_5) || bot->HasAura(SPELL_FROST_RESISTANCE_AURA_RANK_4) || if (botAI->HasAura("frost resistance aura", bot))
bot->HasAura(SPELL_FROST_RESISTANCE_AURA_RANK_3) || bot->HasAura(SPELL_FROST_RESISTANCE_AURA_RANK_2) ||
bot->HasAura(SPELL_FROST_RESISTANCE_AURA_RANK_1))
return false; return false;
// Check if bot dont have already have frost resistance strategy // Check if bot dont have already have frost resistance strategy
@ -133,8 +129,7 @@ bool BossNatureResistanceTrigger::IsActive()
return false; return false;
// Check if bot have nature resistance aura // 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) || if (botAI->HasAura("aspect of the wild", bot))
bot->HasAura(SPELL_ASPECT_OF_THE_WILD_RANK_2) || bot->HasAura(SPELL_ASPECT_OF_THE_WILD_RANK_1))
return false; return false;
// Check if bot dont have already setted nature resistance aura // Check if bot dont have already setted nature resistance aura
@ -184,11 +179,7 @@ bool BossShadowResistanceTrigger::IsActive()
return false; return false;
// Check if bot have shadow resistance aura // Check if bot have shadow resistance aura
if (bot->HasAura(SPELL_SHADOW_RESISTANCE_AURA_RANK_5) || if (botAI->HasAura("shadow resistance aura", bot))
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))
return false; return false;
// Check if bot dont have already have shadow resistance strategy // Check if bot dont have already have shadow resistance strategy

View File

@ -5,39 +5,27 @@
#include "DeathKnightPullStrategy.h" #include "DeathKnightPullStrategy.h"
#include "AiObjectContext.h"
#include "Player.h" #include "Player.h"
#include "PlayerbotAI.h" #include "PlayerbotAI.h"
#include "Playerbots.h" #include "Playerbots.h"
std::string DeathKnightPullStrategy::GetPullActionName() const std::string DeathKnightPullStrategy::GetPullActionName() const
{ {
Player* bot = botAI->GetBot();
Unit* target = GetTarget(); Unit* target = GetTarget();
if (!bot || !target || if (!target ||
(!botAI->HasStrategy("blood", BOT_STATE_COMBAT) && !botAI->HasStrategy("blood", BOT_STATE_NON_COMBAT))) (!botAI->HasStrategy("blood", BOT_STATE_COMBAT) && !botAI->HasStrategy("blood", BOT_STATE_NON_COMBAT)))
{ {
return PullStrategy::GetPullActionName(); return PullStrategy::GetPullActionName();
} }
uint32 const deathGripSpellId = botAI->GetAiObjectContext()->GetValue<uint32>("spell id", "death grip")->Get(); if (botAI->CanCastSpell("death grip", target))
if (deathGripSpellId && bot->HasSpell(deathGripSpellId) &&
botAI->CanCastSpell(deathGripSpellId, target))
{
return "death grip"; return "death grip";
}
uint32 const icyTouchSpellId = botAI->GetAiObjectContext()->GetValue<uint32>("spell id", "icy touch")->Get(); if (!botAI->CanCastSpell("icy touch", target) &&
if (!icyTouchSpellId || !bot->HasSpell(icyTouchSpellId) || botAI->CanCastSpell("dark command", target))
!botAI->CanCastSpell(icyTouchSpellId, target))
{
uint32 const darkCommandSpellId = botAI->GetAiObjectContext()->GetValue<uint32>("spell id", "dark command")->Get();
if (darkCommandSpellId && bot->HasSpell(darkCommandSpellId) &&
botAI->CanCastSpell(darkCommandSpellId, target))
{ {
return "dark command"; return "dark command";
} }
}
return PullStrategy::GetPullActionName(); return PullStrategy::GetPullActionName();
} }

View File

@ -50,9 +50,10 @@ bool CastCancelDruidAction::Execute(Event /*event*/)
return true; return true;
} }
bool CastCancelDruidAction::isUseful() { return botAI->HasAura(auraId, bot); } bool CastCancelDruidAction::isUseful() { return bot->HasAura(auraId); }
bool CastTreeFormAction::isUseful() 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);
} }

View File

@ -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 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); } bool CatFormTrigger::IsActive() { return !botAI->HasAura("cat form", bot); }
@ -43,8 +47,11 @@ bool ProwlTrigger::IsActive()
if (botAI->HasAura("prowl", bot) || bot->IsInCombat()) if (botAI->HasAura("prowl", bot) || bot->IsInCombat())
return false; return false;
uint32 prowlId = botAI->GetAiObjectContext()->GetValue<uint32>("spell id", "prowl")->Get(); if (!botAI->HasSpell("prowl"))
if (!prowlId || !bot->HasSpell(prowlId) || bot->HasSpellCooldown(prowlId)) return false;
uint32 const prowlId = AI_VALUE2(uint32, "spell id", "prowl");
if (bot->HasSpellCooldown(prowlId))
return false; return false;
float distance = 30.f; float distance = 30.f;

View File

@ -393,14 +393,14 @@ public:
class FerociousBiteExecuteTrigger : public Trigger class FerociousBiteExecuteTrigger : public Trigger
{ {
public: public:
FerociousBiteExecuteTrigger(PlayerbotAI* ai) : Trigger(ai, "ferocious bite execute") {} FerociousBiteExecuteTrigger(PlayerbotAI* botAI) : Trigger(botAI, "ferocious bite execute") {}
bool IsActive() override bool IsActive() override
{ {
Unit* target = AI_VALUE(Unit*, "current target"); Unit* target = AI_VALUE(Unit*, "current target");
if (!target || !target->IsAlive()) if (!target || !target->IsAlive())
return false; return false;
if (!AI_VALUE2(uint32, "spell id", "ferocious bite")) if (!botAI->HasSpell("ferocious bite"))
return false; return false;
if (AI_VALUE2(uint8, "combo", "current target") < 1) if (AI_VALUE2(uint8, "combo", "current target") < 1)

View File

@ -5,34 +5,23 @@
#include "DruidPullStrategy.h" #include "DruidPullStrategy.h"
#include "AiObjectContext.h"
#include "Player.h"
#include "PlayerbotAI.h" #include "PlayerbotAI.h"
#include "Playerbots.h" #include "Playerbots.h"
std::string DruidPullStrategy::GetPullActionName() const std::string DruidPullStrategy::GetPullActionName() const
{ {
Player* bot = botAI->GetBot(); std::string const pullActionName = PullStrategy::GetPullActionName();
std::string actionName = PullStrategy::GetPullActionName(); std::string const actionName =
if (!bot) botAI->HasSpell("faerie fire (feral)") &&
return actionName; (botAI->HasStrategy("bear", BOT_STATE_COMBAT) || botAI->HasStrategy("cat", BOT_STATE_COMBAT))
? "faerie fire (feral)" : pullActionName;
uint32 const faerieFireFeralId = botAI->GetAiObjectContext()->GetValue<uint32>("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)";
}
Unit* target = GetTarget(); Unit* target = GetTarget();
uint32 const faerieFireSpellId = botAI->GetAiObjectContext()->GetValue<uint32>("spell id", actionName)->Get(); if (!target)
if (target && (!faerieFireSpellId || !bot->HasSpell(faerieFireSpellId) || return actionName;
!botAI->CanCastSpell(faerieFireSpellId, target)))
{ if (!botAI->CanCastSpell(actionName, target) && botAI->CanCastSpell("growl", target))
uint32 const growlSpellId = botAI->GetAiObjectContext()->GetValue<uint32>("spell id", "growl")->Get();
if (growlSpellId && bot->HasSpell(growlSpellId) && botAI->CanCastSpell(growlSpellId, target))
return "growl"; return "growl";
}
return actionName; return actionName;
} }

View File

@ -19,23 +19,13 @@ bool CastViperStingAction::isUseful()
bool CastAspectOfTheHawkAction::isUseful() bool CastAspectOfTheHawkAction::isUseful()
{ {
Unit* target = GetTarget(); Unit* target = GetTarget();
if (!target) return target && !botAI->HasSpell("aspect of the dragonhawk");
return false;
if (bot->HasSpell(61846) || bot->HasSpell(61847)) // Aspect of the Dragonhawk spell IDs
return false;
return true;
} }
bool CastArcaneShotAction::isUseful() bool CastArcaneShotAction::isUseful()
{ {
Unit* target = GetTarget(); Unit* target = GetTarget();
if (!target) if (!target || !botAI->HasSpell("explosive shot"))
return false;
if (bot->HasSpell(53301) || bot->HasSpell(60051) ||
bot->HasSpell(60052) || bot->HasSpell(60053)) // Explosive Shot spell IDs
return false; return false;
// Armor Penetration rating check - will not cast Arcane Shot above 435 ArP // Armor Penetration rating check - will not cast Arcane Shot above 435 ArP
@ -50,14 +40,7 @@ bool CastArcaneShotAction::isUseful()
bool CastImmolationTrapAction::isUseful() bool CastImmolationTrapAction::isUseful()
{ {
Unit* target = GetTarget(); Unit* target = GetTarget();
if (!target) return target && !botAI->HasSpell("explosive trap");
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;
} }
Value<Unit*>* CastFreezingTrap::GetTargetValue() Value<Unit*>* CastFreezingTrap::GetTargetValue()

View File

@ -13,7 +13,7 @@
std::vector<NextAction> CastMoltenArmorAction::getAlternatives() std::vector<NextAction> CastMoltenArmorAction::getAlternatives()
{ {
if (!AI_VALUE2(uint32, "spell id", "molten armor")) if (!botAI->HasSpell("molten armor"))
return NextAction::merge({ NextAction("mage armor") }, CastBuffSpellAction::getAlternatives()); return NextAction::merge({ NextAction("mage armor") }, CastBuffSpellAction::getAlternatives());
return CastBuffSpellAction::getAlternatives(); return CastBuffSpellAction::getAlternatives();
@ -21,7 +21,7 @@ std::vector<NextAction> CastMoltenArmorAction::getAlternatives()
std::vector<NextAction> CastMageArmorAction::getAlternatives() std::vector<NextAction> CastMageArmorAction::getAlternatives()
{ {
if (!AI_VALUE2(uint32, "spell id", "mage armor")) if (!botAI->HasSpell("mage armor"))
return NextAction::merge({ NextAction("ice armor") }, CastBuffSpellAction::getAlternatives()); return NextAction::merge({ NextAction("ice armor") }, CastBuffSpellAction::getAlternatives());
return CastBuffSpellAction::getAlternatives(); return CastBuffSpellAction::getAlternatives();

View File

@ -39,26 +39,16 @@ bool ArcaneIntellectTrigger::IsActive()
bool MageArmorTrigger::IsActive() bool MageArmorTrigger::IsActive()
{ {
Unit* target = GetTarget(); Unit* target = GetTarget();
if (botAI->HasAura("mage armor", target)) return botAI->HasSpell("mage armor") && !botAI->HasAura("mage armor", target) &&
return false; !botAI->HasAura("ice armor", target) && !botAI->HasAura("frost armor", target) &&
if (AI_VALUE2(uint32, "spell id", "mage armor"))
return true;
return !botAI->HasAura("ice armor", target) && !botAI->HasAura("frost armor", target) &&
!botAI->HasAura("molten armor", target); !botAI->HasAura("molten armor", target);
} }
bool MoltenArmorTrigger::IsActive() bool MoltenArmorTrigger::IsActive()
{ {
Unit* target = GetTarget(); Unit* target = GetTarget();
if (botAI->HasAura("molten armor", target)) return botAI->HasSpell("molten armor") && !botAI->HasAura("molten armor", target) &&
return false; !botAI->HasAura("ice armor", target) && !botAI->HasAura("frost armor", target) &&
if (AI_VALUE2(uint32, "spell id", "molten armor"))
return true;
return !botAI->HasAura("ice armor", target) && !botAI->HasAura("frost armor", target) &&
!botAI->HasAura("mage armor", target); !botAI->HasAura("mage armor", target);
} }
@ -82,7 +72,8 @@ bool FrostbiteOnTargetTrigger::IsActive()
bool NoFocusMagicTrigger::IsActive() bool NoFocusMagicTrigger::IsActive()
{ {
if (!bot->HasSpell(54646)) // Focus Magic constexpr uint32 SPELL_FOCUS_MAGIC = 54646;
if (!bot->HasSpell(SPELL_FOCUS_MAGIC))
return false; return false;
Group* group = bot->GetGroup(); Group* group = bot->GetGroup();
@ -95,7 +86,7 @@ bool NoFocusMagicTrigger::IsActive()
if (!member || member == bot || !member->IsAlive()) if (!member || member == bot || !member->IsAlive())
continue; continue;
if (member->HasAura(54646, bot->GetGUID())) if (member->HasAura(SPELL_FOCUS_MAGIC, bot->GetGUID()))
return false; return false;
} }
return true; return true;
@ -103,11 +94,9 @@ bool NoFocusMagicTrigger::IsActive()
bool DeepFreezeCooldownTrigger::IsActive() bool DeepFreezeCooldownTrigger::IsActive()
{ {
// If the bot does NOT have Deep Freeze, treat as "on cooldown" constexpr uint32 SPELL_DEEP_FREEZE = 44572;
if (!bot->HasSpell(44572)) // Deep Freeze return !bot->HasSpell(SPELL_DEEP_FREEZE) ||
return true; SpellCooldownTrigger::IsActive();
return SpellCooldownTrigger::IsActive();
} }
const std::unordered_set<uint32> FlamestrikeNearbyTrigger::FLAMESTRIKE_SPELL_IDS = { const std::unordered_set<uint32> FlamestrikeNearbyTrigger::FLAMESTRIKE_SPELL_IDS = {

View File

@ -8,6 +8,18 @@
#include "Playerbots.h" #include "Playerbots.h"
#include "RangedCombatStrategy.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<ActionNode> class GenericMageStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{ {
public: public:
@ -102,17 +114,17 @@ void GenericMageStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
// Mana Threshold Triggers // Mana Threshold Triggers
Player* bot = botAI->GetBot(); 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) })); 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) })); 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) })); 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) })); 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) })); 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("high mana", { NextAction("use mana agate", 90.0f) }));
triggers.push_back(new TriggerNode("medium mana", { NextAction("mana potion", 90.0f) })); triggers.push_back(new TriggerNode("medium mana", { NextAction("mana potion", 90.0f) }));
@ -142,7 +154,7 @@ void MageBoostStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
} }
else if (tab == MAGE_TAB_FIRE) 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 { // Frostfire
triggers.push_back(new TriggerNode("combustion", { NextAction("combustion", 18.0f) })); triggers.push_back(new TriggerNode("combustion", { NextAction("combustion", 18.0f) }));
triggers.push_back(new TriggerNode("icy veins", { NextAction("icy veins", 17.5f) })); triggers.push_back(new TriggerNode("icy veins", { NextAction("icy veins", 17.5f) }));

View File

@ -1145,8 +1145,7 @@ bool CastGreaterBlessingAssignmentAction::Execute(Event /*event*/)
if (!FindPendingAssignment(assignment, spellName)) if (!FindPendingAssignment(assignment, spellName))
return false; return false;
uint32 finalId = AI_VALUE2(uint32, "spell id", spellName); if (!botAI->HasSpell(spellName))
if (!finalId)
return false; return false;
return botAI->CastSpell(spellName, assignment.player); return botAI->CastSpell(spellName, assignment.player);

View File

@ -5,34 +5,23 @@
#include "PaladinPullStrategy.h" #include "PaladinPullStrategy.h"
#include "AiObjectContext.h"
#include "Player.h"
#include "PlayerbotAI.h" #include "PlayerbotAI.h"
#include "Playerbots.h" #include "Playerbots.h"
std::string PaladinPullStrategy::GetPullActionName() const std::string PaladinPullStrategy::GetPullActionName() const
{ {
Player* bot = botAI->GetBot();
Unit* target = GetTarget(); Unit* target = GetTarget();
if (!bot || !target || if (!target ||
(!botAI->HasStrategy("tank", BOT_STATE_COMBAT) && !botAI->HasStrategy("tank", BOT_STATE_NON_COMBAT))) (!botAI->HasStrategy("tank", BOT_STATE_COMBAT) && !botAI->HasStrategy("tank", BOT_STATE_NON_COMBAT)))
{ {
return PullStrategy::GetPullActionName(); return PullStrategy::GetPullActionName();
} }
uint32 const avengersShieldSpellId = botAI->GetAiObjectContext()->GetValue<uint32>("spell id", "avenger's shield")->Get(); if (botAI->CanCastSpell("avenger's shield", target))
if (avengersShieldSpellId && bot->HasSpell(avengersShieldSpellId) &&
botAI->CanCastSpell(avengersShieldSpellId, target))
{
return "avenger's shield"; return "avenger's shield";
}
uint32 const handOfReckoningSpellId = botAI->GetAiObjectContext()->GetValue<uint32>("spell id", "hand of reckoning")->Get(); if (botAI->CanCastSpell("hand of reckoning", target))
if (handOfReckoningSpellId && bot->HasSpell(handOfReckoningSpellId) &&
botAI->CanCastSpell(handOfReckoningSpellId, target))
{
return "hand of reckoning"; return "hand of reckoning";
}
return PullStrategy::GetPullActionName(); return PullStrategy::GetPullActionName();
} }

View File

@ -11,6 +11,14 @@
#include "PlayerbotAIConfig.h" #include "PlayerbotAIConfig.h"
#include "Playerbots.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() bool CastStealthAction::isUseful()
{ {
Unit* target = AI_VALUE(Unit*, "current target"); Unit* target = AI_VALUE(Unit*, "current target");
@ -22,7 +30,8 @@ bool CastStealthAction::isUseful()
bool CastStealthAction::isPossible() bool CastStealthAction::isPossible()
{ {
// do not use with WSG flag or EYE flag // 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*/) bool UnstealthAction::Execute(Event /*event*/)
@ -50,7 +59,8 @@ bool CheckStealthAction::Execute(Event /*event*/)
bool CastVanishAction::isUseful() bool CastVanishAction::isUseful()
{ {
// do not use with WSG flag or EYE flag // 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() bool CastEnvenomAction::isUseful()
@ -61,7 +71,7 @@ bool CastEnvenomAction::isUseful()
bool CastEnvenomAction::isPossible() bool CastEnvenomAction::isPossible()
{ {
// alternate to eviscerate if talents unlearned // 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() bool CastTricksOfTheTradeOnMainTankAction::isUseful()

View File

@ -9,6 +9,12 @@
#include "Playerbots.h" #include "Playerbots.h"
#include "ServerFacade.h" #include "ServerFacade.h"
namespace
{
constexpr uint32 SPELL_STEALTH = 1784;
constexpr uint32 SPELL_SPRINT_RANK_1 = 2983;
}
// bool AdrenalineRushTrigger::isPossible() // bool AdrenalineRushTrigger::isPossible()
// { // {
// return !botAI->HasAura("stealth", bot); // return !botAI->HasAura("stealth", bot);
@ -29,7 +35,7 @@ bool UnstealthTrigger::IsActive()
bool StealthTrigger::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; return false;
float distance = 30.f; float distance = 30.f;
@ -63,13 +69,13 @@ bool StealthTrigger::IsActive()
return target && ServerFacade::instance().GetDistance2d(bot, target) < distance; 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() bool SprintTrigger::IsActive()
{ {
if (bot->HasSpellCooldown(2983)) if (bot->HasSpellCooldown(SPELL_SPRINT_RANK_1))
return false; return false;
float distance = botAI->GetMaster() ? 45.0f : 35.0f; float distance = botAI->GetMaster() ? 45.0f : 35.0f;
@ -105,7 +111,7 @@ bool SprintTrigger::IsActive()
bool ExposeArmorTrigger::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) && return DebuffTrigger::IsActive() && !botAI->HasAura("sunder armor", target, false, false, -1, true) &&
AI_VALUE2(uint8, "combo", "current target") <= 3; AI_VALUE2(uint8, "combo", "current target") <= 3;
} }

View File

@ -6,6 +6,17 @@
#include "TotemsShamanStrategy.h" #include "TotemsShamanStrategy.h"
#include "Playerbots.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. // 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 // 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. // 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<TriggerNode*>& triggers)
GenericShamanStrategy::InitTriggers(triggers); GenericShamanStrategy::InitTriggers(triggers);
// If the bot hasn't learned Totem of Wrath yet, set Flametongue Totem instead. // If the bot hasn't learned Totem of Wrath yet, set Flametongue Totem instead.
Player* bot = botAI->GetBot(); 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) })); 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("set flametongue totem", { NextAction("set flametongue totem", 60.0f) }));
triggers.push_back(new TriggerNode("no fire totem", { NextAction("totem of wrath", 55.0f) })); triggers.push_back(new TriggerNode("no fire totem", { NextAction("totem of wrath", 55.0f) }));
} }
@ -112,9 +123,9 @@ void CleansingTotemStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
GenericShamanStrategy::InitTriggers(triggers); GenericShamanStrategy::InitTriggers(triggers);
// If the bot hasn't learned Cleansing Totem yet, set Mana Spring Totem instead. // If the bot hasn't learned Cleansing Totem yet, set Mana Spring Totem instead.
Player* bot = botAI->GetBot(); 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) })); 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("set mana spring totem", { NextAction("set mana spring totem", 60.0f) }));
triggers.push_back(new TriggerNode("no water totem", { NextAction("cleansing totem", 55.0f) })); triggers.push_back(new TriggerNode("no water totem", { NextAction("cleansing totem", 55.0f) }));
} }
@ -134,9 +145,9 @@ void WrathOfAirTotemStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
GenericShamanStrategy::InitTriggers(triggers); GenericShamanStrategy::InitTriggers(triggers);
// If the bot hasn't learned Wrath of Air Totem yet, set Grounding Totem instead. // If the bot hasn't learned Wrath of Air Totem yet, set Grounding Totem instead.
Player* bot = botAI->GetBot(); 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) })); 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("set grounding totem", { NextAction("set grounding totem", 60.0f) }));
triggers.push_back( new TriggerNode("no air totem", { NextAction("wrath of air totem", 55.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<TriggerNode*>& triggers)
GenericShamanStrategy::InitTriggers(triggers); GenericShamanStrategy::InitTriggers(triggers);
// If the bot hasn't learned Windfury Totem yet, set Grounding Totem instead. // If the bot hasn't learned Windfury Totem yet, set Grounding Totem instead.
Player* bot = botAI->GetBot(); 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) })); 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("set grounding totem", { NextAction("set grounding totem", 60.0f) }));
triggers.push_back(new TriggerNode("no air totem", { NextAction("windfury totem", 55.0f) })); triggers.push_back(new TriggerNode("no air totem", { NextAction("windfury totem", 55.0f) }));
} }

View File

@ -77,11 +77,7 @@ bool CastShadowflameAction::isUseful()
bool CastRainOfFireAction::isUseful() bool CastRainOfFireAction::isUseful()
{ {
Unit* target = GetTarget(); Unit* target = GetTarget();
if (!target) return target && !botAI->HasSpell("seed of corruption");
return false;
if (bot->HasSpell(27243) || bot->HasSpell(47835) || bot->HasSpell(47836)) // Seed of Corruption spell IDs
return false;
return true;
} }
// Checks if the enemies are close enough to use Hellfire // Checks if the enemies are close enough to use Hellfire

View File

@ -104,10 +104,8 @@ bool LifeTapTrigger::IsActive()
// Checks if the Life Tap Glyph buff is active // Checks if the Life Tap Glyph buff is active
bool LifeTapGlyphBuffTrigger::IsActive() bool LifeTapGlyphBuffTrigger::IsActive()
{ {
if (!botAI->HasAura(63320, bot)) constexpr uint32 SPELL_LIFE_TAP_GLYPH = 63320;
return false; return bot->HasAura(SPELL_LIFE_TAP_GLYPH) && BuffTrigger::IsActive();
return BuffTrigger::IsActive();
} }
// Checks if the target has a conflicting debuff that is equal to Curse of the Elements // Checks if the target has a conflicting debuff that is equal to Curse of the Elements

View File

@ -5,23 +5,16 @@
#include "WarriorPullStrategy.h" #include "WarriorPullStrategy.h"
#include "AiObjectContext.h"
#include "Player.h"
#include "PlayerbotAI.h" #include "PlayerbotAI.h"
std::string WarriorPullStrategy::GetPullActionName() const std::string WarriorPullStrategy::GetPullActionName() const
{ {
Player* bot = botAI->GetBot();
Unit* target = GetTarget(); Unit* target = GetTarget();
if (!bot || !target) if (!target)
return PullStrategy::GetPullActionName(); return PullStrategy::GetPullActionName();
uint32 const heroicThrowSpellId = botAI->GetAiObjectContext()->GetValue<uint32>("spell id", "heroic throw")->Get(); if (botAI->CanCastSpell("heroic throw", target))
if (heroicThrowSpellId && bot->HasSpell(heroicThrowSpellId) &&
botAI->CanCastSpell(heroicThrowSpellId, target))
{
return "heroic throw"; return "heroic throw";
}
return PullStrategy::GetPullActionName(); return PullStrategy::GetPullActionName();
} }

View File

@ -8,6 +8,15 @@
#include "AiFactory.h" #include "AiFactory.h"
#include "Playerbots.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() bool CastBerserkerRageAction::isPossible()
{ {
if (botAI->IsInVehicle() && !botAI->IsInVehicle(false, false, true)) if (botAI->IsInVehicle() && !botAI->IsInVehicle(false, false, true))
@ -153,14 +162,8 @@ bool CastVigilanceAction::Execute(Event /*event*/)
bool CastRetaliationAction::isUseful() bool CastRetaliationAction::isUseful()
{ {
// Spell cooldown check if (!bot->HasSpell(SPELL_RETALIATION) || bot->HasSpellCooldown(SPELL_RETALIATION) ||
if (!bot->HasSpell(20230)) bot->HasAura(SPELL_RETALIATION))
{
return false;
}
// Spell cooldown check
if (bot->HasSpellCooldown(20230))
{ {
return false; return false;
} }
@ -199,8 +202,8 @@ bool CastRetaliationAction::isUseful()
break; break;
} }
// Only cast Retaliation if there are at least 2 melee attackers and the buff is not active // Only cast Retaliation if there are at least 2 melee attackers
return meleeAttackers >= 2 && !botAI->HasAura("retaliation", bot); return meleeAttackers >= 2;
} }
Unit* CastShatteringThrowAction::GetTarget() Unit* CastShatteringThrowAction::GetTarget()
@ -214,15 +217,15 @@ Unit* CastShatteringThrowAction::GetTarget()
continue; continue;
if (bot->IsWithinDistInMap(enemy, 25.0f) && if (bot->IsWithinDistInMap(enemy, 25.0f) &&
(enemy->HasAura(642) || // Divine Shield (enemy->HasAura(SPELL_DIVINE_SHIELD) ||
enemy->HasAura(45438) || // Ice Block enemy->HasAura(SPELL_ICE_BLOCK) ||
enemy->HasAura(41450))) // Blessing of Protection enemy->HasAura(SPELL_BLESSING_OF_PROTECTION)))
{ {
return enemy; return enemy;
} }
} }
return nullptr; // No valid target return nullptr;
} }
bool CastShatteringThrowAction::Execute(Event /*event*/) bool CastShatteringThrowAction::Execute(Event /*event*/)
@ -236,7 +239,7 @@ bool CastShatteringThrowAction::Execute(Event /*event*/)
bool CastShatteringThrowAction::isUseful() bool CastShatteringThrowAction::isUseful()
{ {
if (!bot->HasSpell(64382) || bot->HasSpellCooldown(64382)) if (!bot->HasSpell(SPELL_SHATTERING_THROW) || bot->HasSpellCooldown(SPELL_SHATTERING_THROW))
return false; return false;
GuidVector enemies = AI_VALUE(GuidVector, "possible targets"); GuidVector enemies = AI_VALUE(GuidVector, "possible targets");
@ -247,17 +250,16 @@ bool CastShatteringThrowAction::isUseful()
if (!enemy || !enemy->IsAlive() || enemy->IsFriendlyTo(bot)) if (!enemy || !enemy->IsAlive() || enemy->IsFriendlyTo(bot))
continue; continue;
// Check if the enemy is within 25 yards and has the specific auras
if (bot->IsWithinDistInMap(enemy, 25.0f) && if (bot->IsWithinDistInMap(enemy, 25.0f) &&
(enemy->HasAura(642) || // Divine Shield (enemy->HasAura(SPELL_DIVINE_SHIELD) ||
enemy->HasAura(45438) || // Ice Block enemy->HasAura(SPELL_ICE_BLOCK) ||
enemy->HasAura(41450))) // Blessing of Protection enemy->HasAura(SPELL_BLESSING_OF_PROTECTION)))
{ {
return true; return true;
} }
} }
return false; // No valid targets within range return false;
} }
bool CastShatteringThrowAction::isPossible() bool CastShatteringThrowAction::isPossible()

View File

@ -6,6 +6,16 @@
#include "WarriorTriggers.h" #include "WarriorTriggers.h"
#include "Playerbots.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() bool BloodrageBuffTrigger::IsActive()
{ {
return AI_VALUE2(uint8, "health", "self target") >= sPlayerbotAIConfig.mediumHealth && return AI_VALUE2(uint8, "health", "self target") >= sPlayerbotAIConfig.mediumHealth &&
@ -14,7 +24,7 @@ bool BloodrageBuffTrigger::IsActive()
bool VigilanceTrigger::IsActive() bool VigilanceTrigger::IsActive()
{ {
if (!bot->HasSpell(50720)) if (!bot->HasSpell(SPELL_VIGILANCE))
return false; return false;
Group* group = bot->GetGroup(); Group* group = bot->GetGroup();
@ -64,7 +74,7 @@ bool VigilanceTrigger::IsActive()
bool ShatteringThrowTrigger::IsActive() bool ShatteringThrowTrigger::IsActive()
{ {
if (!bot->HasSpell(64382) || bot->HasSpellCooldown(64382)) if (!bot->HasSpell(SPELL_SHATTERING_THROW) || bot->HasSpellCooldown(SPELL_SHATTERING_THROW))
return false; return false;
GuidVector enemies = AI_VALUE(GuidVector, "possible targets"); GuidVector enemies = AI_VALUE(GuidVector, "possible targets");
@ -76,9 +86,9 @@ bool ShatteringThrowTrigger::IsActive()
continue; continue;
if (bot->IsWithinDistInMap(enemy, 25.0f) && if (bot->IsWithinDistInMap(enemy, 25.0f) &&
(enemy->HasAura(642) || // Divine Shield (enemy->HasAura(SPELL_DIVINE_SHIELD) ||
enemy->HasAura(45438) || // Ice Block enemy->HasAura(SPELL_ICE_BLOCK) ||
enemy->HasAura(41450))) // Blessing of Protection enemy->HasAura(SPELL_BLESSING_OF_PROTECTION)))
{ {
return true; return true;
} }
@ -112,15 +122,13 @@ bool BattleShoutTrigger::IsActive()
if (!bsApValue) if (!bsApValue)
return false; return false;
static const uint32 commandingPresenceSpells[] = {
12318, 12857, 12858, 12860, 12861 };
static const float commandingPresenceBonus[] = { static const float commandingPresenceBonus[] = {
0.05f, 0.10f, 0.15f, 0.20f, 0.25f }; 0.05f, 0.10f, 0.15f, 0.20f, 0.25f };
float cpBonus = 0.0f; float cpBonus = 0.0f;
for (int rank = 4; rank >= 0; --rank) for (int rank = 4; rank >= 0; --rank)
{ {
if (bot->HasAura(commandingPresenceSpells[rank])) if (bot->HasAura(SPELL_COMMANDING_PRESENCE_RANKS[rank]))
{ {
cpBonus = commandingPresenceBonus[rank]; cpBonus = commandingPresenceBonus[rank];
break; break;

View File

@ -5,6 +5,13 @@
#include "Playerbots.h" #include "Playerbots.h"
#include "SharedDefines.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*/) bool RazuviousUseObedienceCrystalAction::Execute(Event /*event*/)
{ {
if (!helper.UpdateBossAI()) if (!helper.UpdateBossAI())
@ -42,7 +49,8 @@ bool RazuviousUseObedienceCrystalAction::Execute(Event /*event*/)
bool tauntUseful = true; bool tauntUseful = true;
if (forceObedience->GetDuration() <= (duration_time - 5000)) 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; tauntUseful = false;
if (forceObedience->GetDuration() <= 3000) if (forceObedience->GetDuration() <= 3000)
@ -55,19 +63,19 @@ bool RazuviousUseObedienceCrystalAction::Execute(Event /*event*/)
if (tauntUseful && !charm->HasSpellCooldown(29060)) if (tauntUseful && !charm->HasSpellCooldown(29060))
{ {
// shield // shield
if (!charm->HasSpellCooldown(29061)) if (!charm->HasSpellCooldown(SPELL_BONE_BARRIER))
{ {
charm->CastSpell(charm, 29061, true); charm->CastSpell(charm, SPELL_BONE_BARRIER, true);
charm->AddSpellCooldown(29061, 0, 30 * 1000); charm->AddSpellCooldown(SPELL_BONE_BARRIER, 0, 30 * 1000);
} }
charm->CastSpell(target, 29060, true); charm->CastSpell(target, SPELL_UNDERSTUDY_TAUNT, true);
charm->AddSpellCooldown(29060, 0, 20 * 1000); charm->AddSpellCooldown(SPELL_UNDERSTUDY_TAUNT, 0, 20 * 1000);
} }
// strike // strike
if (!charm->HasSpellCooldown(61696)) if (!charm->HasSpellCooldown(SPELL_BLOOD_STRIKE))
{ {
charm->CastSpell(target, 61696, true); charm->CastSpell(target, SPELL_BLOOD_STRIKE, true);
charm->AddSpellCooldown(61696, 0, 4 * 1000); charm->AddSpellCooldown(SPELL_BLOOD_STRIKE, 0, 4 * 1000);
} }
} }
} }

View File

@ -75,7 +75,7 @@ bool SapphironFlightPositionAction::MoveToNearestIcebolt()
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{ {
Player* member = ref->GetSource(); 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)) botAI->HasAura("icebolt", member, false, false, -1, true))
{ {
if (!playerWithIcebolt || minDistance > bot->GetDistance(member)) if (!playerWithIcebolt || minDistance > bot->GetDistance(member))

View File

@ -111,15 +111,13 @@ bool ThaddiusMovePolarityAction::Execute(Event /*event*/)
{3504.68f, -2936.68f}, {3504.68f, -2936.68f},
}; };
uint32 idx; uint32 idx;
if (NaxxSpellIds::HasAnyAura( if (NaxxSpellIds::HasAnyAura(bot,
botAI, bot,
{NaxxSpellIds::NegativeCharge10, NaxxSpellIds::NegativeCharge25, NaxxSpellIds::NegativeChargeStack}) || {NaxxSpellIds::NegativeCharge10, NaxxSpellIds::NegativeCharge25, NaxxSpellIds::NegativeChargeStack}) ||
botAI->HasAura("negative charge", bot, false, false, -1, true)) botAI->HasAura("negative charge", bot, false, false, -1, true))
{ {
idx = 0; idx = 0;
} }
else if (NaxxSpellIds::HasAnyAura( else if (NaxxSpellIds::HasAnyAura(bot,
botAI, bot,
{NaxxSpellIds::PositiveCharge10, NaxxSpellIds::PositiveCharge25, NaxxSpellIds::PositiveChargeStack}) || {NaxxSpellIds::PositiveCharge10, NaxxSpellIds::PositiveCharge25, NaxxSpellIds::PositiveChargeStack}) ||
botAI->HasAura("positive charge", bot, false, false, -1, true)) botAI->HasAura("positive charge", bot, false, false, -1, true))
{ {

View File

@ -192,7 +192,7 @@ public:
{ {
Player* member = ref->GetSource(); Player* member = ref->GetSource();
if (member && 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))) botAI->HasAura("icebolt", member, false, false, -1, true)))
{ {
return true; return true;

View File

@ -236,7 +236,7 @@ float AnubrekhanGenericMultiplier::GetValue(Action* action)
return 1.0f; return 1.0f;
if (NaxxSpellIds::HasAnyAura( if (NaxxSpellIds::HasAnyAura(
botAI, boss, {NaxxSpellIds::LocustSwarm10, NaxxSpellIds::LocustSwarm10Alt, NaxxSpellIds::LocustSwarm25}) || boss, {NaxxSpellIds::LocustSwarm10, NaxxSpellIds::LocustSwarm10Alt, NaxxSpellIds::LocustSwarm25}) ||
botAI->HasAura("locust swarm", boss)) botAI->HasAura("locust swarm", boss))
{ {
if (dynamic_cast<FleeAction*>(action)) if (dynamic_cast<FleeAction*>(action))

View File

@ -123,14 +123,14 @@ namespace NaxxSpellIds
SPELL_INEVITABLE_DOOM = 29204, SPELL_INEVITABLE_DOOM = 29204,
SPELL_BERSERK = 26662 SPELL_BERSERK = 26662
*/ */
inline bool HasAnyAura(PlayerbotAI* botAI, Unit* unit, std::initializer_list<uint32> spellIds) inline bool HasAnyAura(Unit* unit, std::initializer_list<uint32> spellIds)
{ {
if (!botAI || !unit) if (!unit)
return false; return false;
for (uint32 spellId : spellIds) for (uint32 spellId : spellIds)
{ {
if (botAI->HasAura(spellId, unit)) if (unit->HasAura(spellId))
return true; return true;
} }
return false; return false;

View File

@ -1531,7 +1531,7 @@ bool VezaxShadowCrashTrigger::IsActive()
if (!boss || !boss->IsAlive()) if (!boss || !boss->IsAlive())
return false; return false;
return botAI->HasAura(SPELL_VEZAX_SHADOW_CRASH, bot); return bot->HasAura(SPELL_VEZAX_SHADOW_CRASH);
} }
bool VezaxMarkOfTheFacelessTrigger::IsActive() bool VezaxMarkOfTheFacelessTrigger::IsActive()
@ -1542,7 +1542,7 @@ bool VezaxMarkOfTheFacelessTrigger::IsActive()
if (!boss || !boss->IsAlive()) if (!boss || !boss->IsAlive())
return false; return false;
if (!botAI->HasAura(SPELL_MARK_OF_THE_FACELESS, bot)) if (!bot->HasAura(SPELL_MARK_OF_THE_FACELESS))
return false; return false;
float distance = bot->GetDistance2d(ULDUAR_VEZAX_MARK_OF_THE_FACELESS_SPOT.GetPositionX(), float distance = bot->GetDistance2d(ULDUAR_VEZAX_MARK_OF_THE_FACELESS_SPOT.GetPositionX(),

View File

@ -26,6 +26,15 @@
#include "WarlockAiObjectContext.h" #include "WarlockAiObjectContext.h"
#include "WarriorAiObjectContext.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) AiObjectContext* AiFactory::createAiObjectContext(Player* player, PlayerbotAI* botAI)
{ {
switch (player->getClass()) switch (player->getClass())
@ -300,7 +309,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
engine->addStrategiesNoInit("arcane", "bdps", nullptr); engine->addStrategiesNoInit("arcane", "bdps", nullptr);
else if (tab == MAGE_TAB_FIRE) 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); engine->addStrategiesNoInit("frostfire", "bdps", nullptr);
else else
engine->addStrategiesNoInit("fire", "bdps", nullptr); engine->addStrategiesNoInit("fire", "bdps", nullptr);
@ -313,7 +322,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
case CLASS_WARRIOR: case CLASS_WARRIOR:
if (tab == WARRIOR_TAB_PROTECTION) if (tab == WARRIOR_TAB_PROTECTION)
engine->addStrategiesNoInit("tank", "tank assist", "pull", "pull back", "aoe", nullptr); 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); engine->addStrategiesNoInit("arms", "aoe", "dps assist", nullptr);
else // if (tab == WARRIOR_TAB_FURY) else // if (tab == WARRIOR_TAB_FURY)
engine->addStrategiesNoInit("fury", "aoe", "dps assist", nullptr); 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); engine->addStrategiesNoInit("resto", "cure", "dps assist", "blanketing", "tranquility", nullptr);
else 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); engine->addStrategiesNoInit("cat", "aoe", "cc", "dps assist", "feral charge", nullptr);
else else
engine->addStrategiesNoInit("bear", "tank assist", "pull", "pull back", "feral charge", nullptr); 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: case CLASS_DRUID:
if (tab == DRUID_TAB_FERAL) 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); nonCombatEngine->addStrategy("dps assist", false);
else else
nonCombatEngine->addStrategiesNoInit("tank assist", "pull", nullptr); nonCombatEngine->addStrategiesNoInit("tank assist", "pull", nullptr);

View File

@ -61,6 +61,56 @@ std::vector<uint32> PlayerbotFactory::enchantGemIdCache;
std::unordered_map<uint32, std::vector<uint32>> PlayerbotFactory::trainerIdCache; std::unordered_map<uint32, std::vector<uint32>> PlayerbotFactory::trainerIdCache;
std::vector<uint32> PlayerbotFactory::ccBreakTrinketCache; std::vector<uint32> 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) bool PlayerbotFactory::IsPrimaryTradeSkill(uint16 skillId)
{ {
SkillLineEntry const* skillLine = sSkillLineStore.LookupEntry(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 /// @todo: fix cat druid hardcode
if (bot->getClass() == CLASS_DRUID && specTab == DRUID_TAB_FERAL && bot->GetLevel() >= 20) 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) if (!isCat && bot->GetLevel() == 20)
{ {
uint32 bearP = sPlayerbotAIConfig.randomClassSpecProb[cls][1]; uint32 bearP = sPlayerbotAIConfig.randomClassSpecProb[cls][1];
@ -1495,7 +1545,7 @@ uint32 PlayerbotFactory::InitTalentsTree(bool increment /*false*/, bool use_temp
if (bot->GetFreeTalentPoints()) if (bot->GetFreeTalentPoints())
InitTalents((specTab + 2) % 3); 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->SetSkill(SKILL_DUAL_WIELD, 0, 1, 1);
bot->SetCanDualWield(true); 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->SetSkill(SKILL_DUAL_WIELD, 0, 1, 1);
bot->SetCanDualWield(true); bot->SetCanDualWield(true);
@ -4170,13 +4220,13 @@ void PlayerbotFactory::InitGlyphs(bool increment)
if (bot->getClass() == CLASS_WARRIOR) if (bot->getClass() == CLASS_WARRIOR)
{ {
// Arms PvP (spec index 3): If the bot has the Second Wind talent // 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; tab = 3;
// Fury PvP (spec index 4): If the bot has the Blood Craze talent // 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; tab = 4;
// Protection PvP (spec index 5): If the bot has the Gag Order talent // 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; tab = 5;
} }
@ -4184,13 +4234,13 @@ void PlayerbotFactory::InitGlyphs(bool increment)
if (bot->getClass() == CLASS_PALADIN) if (bot->getClass() == CLASS_PALADIN)
{ {
// Holy PvP (spec index 3): If the bot has the Sacred Cleansing talent // 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; tab = 3;
// Protection PvP (spec index 4): If the bot has the Reckoning talent // 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; tab = 4;
// Retribution PvP (spec index 5): If the bot has the Divine Purpose talent // 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; tab = 5;
} }
@ -4198,13 +4248,13 @@ void PlayerbotFactory::InitGlyphs(bool increment)
if (bot->getClass() == CLASS_HUNTER) if (bot->getClass() == CLASS_HUNTER)
{ {
// Beast Mastery PvP (spec index 3): If the bot has the Thick Hide talent // 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; tab = 3;
// Marksmanship PvP (spec index 4): If the bot has the Concussive Barrage talent // 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; tab = 4;
// Survival PvP (spec index 5): If the bot has the Entrapment talent and does NOT have the Concussive Barrage talent // 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; tab = 5;
} }
@ -4212,13 +4262,13 @@ void PlayerbotFactory::InitGlyphs(bool increment)
if (bot->getClass() == CLASS_ROGUE) if (bot->getClass() == CLASS_ROGUE)
{ {
// Assassination PvP (spec index 3): If the bot has the Deadly Brew talent // 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; tab = 3;
// Combat PvP (spec index 4): If the bot has the Throwing Specialization talent // 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; tab = 4;
// Subtlety PvP (spec index 5): If the bot has the Waylay talent // 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; tab = 5;
} }
@ -4226,13 +4276,13 @@ void PlayerbotFactory::InitGlyphs(bool increment)
if (bot->getClass() == CLASS_PRIEST) if (bot->getClass() == CLASS_PRIEST)
{ {
// Discipline PvP (spec index 3): If the bot has the Improved Mana Burn talent // 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; tab = 3;
// Holy PvP (spec index 4): If the bot has the Body and Soul talent // 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; tab = 4;
// Shadow PvP (spec index 5): If the bot has the Improved Vampiric Embrace talent // 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; 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 // Double Aura Blood PvE (spec index 3): If the bot has both the Abomination's Might and Improved Icy Talons
// talents // talents
if (bot->HasAura(53138) && bot->HasAura(55610)) if (bot->HasAura(SPELL_ABOMINATIONS_MIGHT) && bot->HasAura(SPELL_IMPROVED_ICY_TALONS))
tab = 3; tab = 3;
// Blood PvP (spec index 4): If the bot has the Sudden Doom talent // 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; tab = 4;
// Frost PvP (spec index 5): If the bot has the Acclimation talent // 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; tab = 5;
// Unholy PvP (spec index 6): If the bot has the Magic Suppression talent // 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; tab = 6;
} }
@ -4258,13 +4308,13 @@ void PlayerbotFactory::InitGlyphs(bool increment)
if (bot->getClass() == CLASS_SHAMAN) if (bot->getClass() == CLASS_SHAMAN)
{ {
// Elemental PvP (spec index 3): If the bot has the Astral Shift talent // 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; tab = 3;
// Enhancement PvP (spec index 4): If the bot has the Earthen Power talent // 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; tab = 4;
// Restoration PvP (spec index 5): If the bot has the Focused Mind talent // 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; tab = 5;
} }
@ -4272,16 +4322,16 @@ void PlayerbotFactory::InitGlyphs(bool increment)
if (bot->getClass() == CLASS_MAGE) if (bot->getClass() == CLASS_MAGE)
{ {
// Frostfire PvE (spec index 3): If the bot has both the Burnout talent and the Ice Shards talent // 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; tab = 3;
// Arcane PvP (spec index 4): If the bot has the Improved Blink talent // 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; tab = 4;
// Fire PvP (spec index 5): If the bot has the Fiery Payback talent // 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; tab = 5;
// Frost PvP (spec index 6): If the bot has the Shattered Barrier talent // 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; tab = 6;
} }
@ -4289,13 +4339,13 @@ void PlayerbotFactory::InitGlyphs(bool increment)
if (bot->getClass() == CLASS_WARLOCK) if (bot->getClass() == CLASS_WARLOCK)
{ {
// Affliction PvP (spec index 3): If the bot has the Improved Howl of Terror talent // 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; tab = 3;
// Demonology PvP (spec index 4): If the bot has both the Nemesis talent and the Intensity talent // 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; tab = 4;
// Destruction PvP (spec index 5): If the bot has the Nether Protection talent // 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; tab = 5;
} }
@ -4303,16 +4353,16 @@ void PlayerbotFactory::InitGlyphs(bool increment)
if (bot->getClass() == CLASS_DRUID) 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 // 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; tab = 3;
// Balance PvP (spec index 4): If the bot has the Owlkin Frenzy talent // 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; tab = 4;
// Feral PvP (spec index 5): If the bot has the Primal Tenacity talent // 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; tab = 5;
// Resto PvP (spec index 6): If the bot has the Improved Barkskin talent // 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; tab = 6;
} }

View File

@ -3027,6 +3027,8 @@ bool PlayerbotAI::TellMaster(std::string const text, PlayerbotSecurityLevel secu
{ {
if (sPlayerbotAIConfig.randomBotSayWithoutMaster) if (sPlayerbotAIConfig.randomBotSayWithoutMaster)
return TellMasterNoFacing(text, securityLevel); return TellMasterNoFacing(text, securityLevel);
return false;
} }
if (!TellMasterNoFacing(text, securityLevel)) if (!TellMasterNoFacing(text, securityLevel))
return false; return false;
@ -3150,20 +3152,10 @@ bool PlayerbotAI::HasAura(std::string const name, Unit* unit, bool maxStack, boo
return false; return false;
} }
bool PlayerbotAI::HasAura(uint32 spellId, Unit const* unit) bool PlayerbotAI::HasSpell(std::string const spellName) const
{ {
if (!spellId || !unit) uint32 const spellId = aiObjectContext->GetValue<uint32>("spell id", spellName)->Get();
return false; return spellId && bot->HasSpell(spellId);
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;
} }
Aura* PlayerbotAI::GetAura(std::string const name, Unit* unit, bool checkIsOwner, bool checkDuration, int checkStack) Aura* PlayerbotAI::GetAura(std::string const name, Unit* unit, bool checkIsOwner, bool checkDuration, int checkStack)
@ -4267,7 +4259,7 @@ void PlayerbotAI::InterruptSpell()
void PlayerbotAI::RemoveAura(std::string const name) void PlayerbotAI::RemoveAura(std::string const name)
{ {
uint32 spellid = aiObjectContext->GetValue<uint32>("spell id", name)->Get(); uint32 spellid = aiObjectContext->GetValue<uint32>("spell id", name)->Get();
if (spellid && HasAura(spellid, bot)) if (spellid && bot->HasAura(spellid))
bot->RemoveAurasDueToSpell(spellid); bot->RemoveAurasDueToSpell(spellid);
} }

View File

@ -497,6 +497,7 @@ public:
virtual bool CanCastSpell(std::string const name, Unit* target, Item* itemTarget = nullptr); 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 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, virtual bool HasAura(std::string const spellName, Unit* player, bool maxStack = false, bool checkIsOwner = false,
int maxAmount = -1, bool checkDuration = false); int maxAmount = -1, bool checkDuration = false);
virtual bool HasAnyAuraOf(Unit* player, ...); virtual bool HasAnyAuraOf(Unit* player, ...);
@ -509,7 +510,6 @@ public:
bool CanCastSpell(uint32 spellid, float x, float y, float z, bool checkHasSpell = true, bool CanCastSpell(uint32 spellid, float x, float y, float z, bool checkHasSpell = true,
Item* itemTarget = nullptr); Item* itemTarget = nullptr);
bool HasAura(uint32 spellId, Unit const* player);
Aura* GetAura(std::string const spellName, Unit* unit, bool checkIsOwner = false, bool checkDuration = false, Aura* GetAura(std::string const spellName, Unit* unit, bool checkIsOwner = false, bool checkDuration = false,
int checkStack = -1); int checkStack = -1);
bool CastSpell(uint32 spellId, Unit* target, Item* itemTarget = nullptr); bool CastSpell(uint32 spellId, Unit* target, Item* itemTarget = nullptr);

View File

@ -23,13 +23,29 @@
namespace namespace
{ {
constexpr uint32 SPELL_MOLTEN_ARMOR_RANK_1 = 30482; constexpr uint32 SPELL_MOLTEN_ARMOR_RANKS[] = { 30482, 43045, 43046 };
constexpr uint32 SPELL_MOLTEN_ARMOR_RANK_2 = 43045; constexpr uint32 SPELL_FEL_ARMOR_RANKS[] = { 28176, 28189, 47892, 47893 };
constexpr uint32 SPELL_MOLTEN_ARMOR_RANK_3 = 43046; constexpr uint32 SPELL_CAREFUL_AIM = 34484;
constexpr uint32 SPELL_FEL_ARMOR_RANK_1 = 28176; constexpr uint32 SPELL_HUNTER_VS_WILD = 56341;
constexpr uint32 SPELL_FEL_ARMOR_RANK_2 = 28189; constexpr uint32 SPELL_ARMORED_TO_THE_TEETH = 61222;
constexpr uint32 SPELL_FEL_ARMOR_RANK_3 = 47892; constexpr uint32 SPELL_MENTAL_DEXTERITY = 51885;
constexpr uint32 SPELL_FEL_ARMOR_RANK_4 = 47893; 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 <size_t Size>
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) StatsWeightCalculator::StatsWeightCalculator(Player* player) : player_(player)
@ -512,26 +528,24 @@ void StatsWeightCalculator::GenerateAdditionalWeights(Player* player)
// int tab = AiFactory::GetPlayerSpecTab(player); // int tab = AiFactory::GetPlayerSpecTab(player);
if (cls == CLASS_HUNTER) if (cls == CLASS_HUNTER)
{ {
if (player->HasAura(34484)) if (player->HasAura(SPELL_CAREFUL_AIM))
stats_weights_[STATS_TYPE_INTELLECT] += 1.1f; 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; stats_weights_[STATS_TYPE_STAMINA] += 0.3f;
} }
else if (cls == CLASS_WARRIOR) else if (cls == CLASS_WARRIOR)
{ {
if (player->HasAura(61222)) if (player->HasAura(SPELL_ARMORED_TO_THE_TEETH))
stats_weights_[STATS_TYPE_ARMOR] += 0.03f; stats_weights_[STATS_TYPE_ARMOR] += 0.03f;
} }
else if (cls == CLASS_SHAMAN) else if (cls == CLASS_SHAMAN)
{ {
if (player->HasAura(51885)) if (player->HasAura(SPELL_MENTAL_DEXTERITY))
stats_weights_[STATS_TYPE_INTELLECT] += 1.1f; stats_weights_[STATS_TYPE_INTELLECT] += 1.1f;
} }
else if (cls == CLASS_MAGE) else if (cls == CLASS_MAGE)
{ {
if (!player->HasSpell(SPELL_MOLTEN_ARMOR_RANK_1) if (!HasAnySpell(player, SPELL_MOLTEN_ARMOR_RANKS))
&& !player->HasSpell(SPELL_MOLTEN_ARMOR_RANK_2)
&& !player->HasSpell(SPELL_MOLTEN_ARMOR_RANK_3))
{ {
if (tab != MAGE_TAB_FIRE) if (tab != MAGE_TAB_FIRE)
stats_weights_[STATS_TYPE_SPIRIT] -= 0.6f; stats_weights_[STATS_TYPE_SPIRIT] -= 0.6f;
@ -541,8 +555,7 @@ void StatsWeightCalculator::GenerateAdditionalWeights(Player* player)
} }
else if (cls == CLASS_WARLOCK) else if (cls == CLASS_WARLOCK)
{ {
if (!player->HasSpell(SPELL_FEL_ARMOR_RANK_1) && !player->HasSpell(SPELL_FEL_ARMOR_RANK_2) && if (!HasAnySpell(player, SPELL_FEL_ARMOR_RANKS))
!player->HasSpell(SPELL_FEL_ARMOR_RANK_3) && !player->HasSpell(SPELL_FEL_ARMOR_RANK_4))
stats_weights_[STATS_TYPE_SPIRIT] -= 0.4f; stats_weights_[STATS_TYPE_SPIRIT] -= 0.4f;
} }
@ -690,17 +703,17 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto)
weight_ *= 1.5; 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)) (proto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD || proto->SubClass == ITEM_SUBCLASS_WEAPON_AXE))
{ {
weight_ *= 1.1; 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)) (proto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM || proto->SubClass == ITEM_SUBCLASS_WEAPON_AXE2))
{ {
weight_ *= 1.1; 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; weight_ *= 1.3;
} }
@ -739,9 +752,9 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player)
player->GetTotalAuraModifier(SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT); // suppression (18176) player->GetTotalAuraModifier(SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT); // suppression (18176)
hit_current += player->GetRatingBonusValue(CR_HIT_SPELL); 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; 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_current += 3;
hit_overflow = SPELL_HIT_OVERFLOW; hit_overflow = SPELL_HIT_OVERFLOW;