diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 5dff66d0..3f61ef18 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -23,6 +23,7 @@ # LEVELS # GEAR # QUESTS +# ACTIVITIES # SPELLS # STRATEGIES # TELEPORTS @@ -612,6 +613,50 @@ AiPlayerbot.EquipmentPersistenceLevel = 80 # Default: 1 (enabled) AiPlayerbot.AutoUpgradeEquip = 1 +# Only set wolf pets for hunters to have higher damage (0 = disabled, 1 = enabled only for max-level, 2 = enabled) +# Default: 0 (disabled) +AiPlayerbot.HunterWolfPet = 0 + +# +# +# +#################################################################################################### + + +#################################################################################################### +# ACTIVITIES +# +# +# Specify percent of active bots +# The default is 10. With 10% of all bots going active or inactive each minute. Regardless +# This value is only applied to inactive areas where no real-players are detected, when +# real-players are nearby, friend, group, guild, bg, instances etc the value is always +# enforced to 100% +AiPlayerbot.BotActiveAlone = 100 + +# Force botActiveAlone when bot is ... of real player +AiPlayerbot.BotActiveAloneForceWhenInRadius = 150 +AiPlayerbot.BotActiveAloneForceWhenInZone = 1 +AiPlayerbot.BotActiveAloneForceWhenInMap = 0 +AiPlayerbot.BotActiveAloneForceWhenIsFriend = 1 +AiPlayerbot.BotActiveAloneForceWhenInGuild = 1 + +# SmartScale is enabled or not. +# The default is 1. When enabled (smart) scales the 'BotActiveAlone' value. +# (The scaling will be overruled by the BotActiveAloneForceWhen...rules) +# +# Limitfloor - when DIFF (latency) above floor, activity scaling is applied starting with 90% +# LimitCeiling - when DIFF (latency) above ceiling, activity is 0%; +# +# MinLevel - only apply scaling when level is above or equal to min(bot)Level +# MaxLevel - only apply scaling when level is lower or equal of max(bot)Level +# +AiPlayerbot.botActiveAloneSmartScale = 1 +AiPlayerbot.botActiveAloneSmartScaleDiffLimitfloor = 50 +AiPlayerbot.botActiveAloneSmartScaleDiffLimitCeiling = 200 +AiPlayerbot.botActiveAloneSmartScaleWhenMinLevel = 1 +AiPlayerbot.botActiveAloneSmartScaleWhenMaxLevel = 80 + # # # @@ -960,7 +1005,7 @@ AiPlayerbot.PremadeSpecGlyph.6.1 = 45805,43673,43547,43544,43672,43543 AiPlayerbot.PremadeSpecLink.6.1.60 = -32003350332203012300023101351 AiPlayerbot.PremadeSpecLink.6.1.80 = -32002350352203012300033101351-230200305003 AiPlayerbot.PremadeSpecName.6.2 = unholy pve -AiPlayerbot.PremadeSpecGlyph.6.2 = 43542,43673,45804,43544,43672,43549 +AiPlayerbot.PremadeSpecGlyph.6.2 = 43542,43673,43546,43544,43672,43549 AiPlayerbot.PremadeSpecLink.6.2.60 = --2301303050032151000150013131151 AiPlayerbot.PremadeSpecLink.6.2.80 = -320033500002-2301303050032151000150013133151 AiPlayerbot.PremadeSpecName.6.3 = double aura blood pve @@ -1551,36 +1596,6 @@ AiPlayerbot.RandombotsWalkingRPG = 0 # Set randombots movement speed to walking only inside buildings AiPlayerbot.RandombotsWalkingRPG.InDoors = 0 -# Specify percent of active bots -# The default is 10. With 10% of all bots going active or inactive each minute. Regardless -# This value is only applied to inactive areas where no real-players are detected, when -# real-players are nearby, friend, group, guild, bg, instances etc the value is always -# enforced to 100% -AiPlayerbot.BotActiveAlone = 100 - -# Force botActiveAlone when bot is ... of real player -AiPlayerbot.BotActiveAloneForceWhenInRadius = 150 -AiPlayerbot.BotActiveAloneForceWhenInZone = 1 -AiPlayerbot.BotActiveAloneForceWhenInMap = 0 -AiPlayerbot.BotActiveAloneForceWhenIsFriend = 1 -AiPlayerbot.BotActiveAloneForceWhenInGuild = 1 - -# SmartScale is enabled or not. -# The default is 1. When enabled (smart) scales the 'BotActiveAlone' value. -# (The scaling will be overruled by the BotActiveAloneForceWhen...rules) -# -# Limitfloor - when DIFF (latency) above floor, activity scaling is applied starting with 90% -# LimitCeiling - when DIFF (latency) above ceiling, activity is 0%; -# -# MinLevel - only apply scaling when level is above or equal to min(bot)Level -# MaxLevel - only apply scaling when level is lower or equal of max(bot)Level -# -AiPlayerbot.botActiveAloneSmartScale = 1 -AiPlayerbot.botActiveAloneSmartScaleDiffLimitfloor = 50 -AiPlayerbot.botActiveAloneSmartScaleDiffLimitCeiling = 200 -AiPlayerbot.botActiveAloneSmartScaleWhenMinLevel = 1 -AiPlayerbot.botActiveAloneSmartScaleWhenMaxLevel = 80 - # Premade spell to avoid (undetected spells) # spellid-radius, ... AiPlayerbot.PremadeAvoidAoe = 62234-4 diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 29c25ce1..25afccfb 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -2928,7 +2928,10 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, Unit* target, bool checkHasSpell, } Unit* oldSel = bot->GetSelectedUnit(); - Spell* spell = new Spell(bot, spellInfo, TRIGGERED_NONE); + // TRIGGERED_IGNORE_POWER_AND_REAGENT_COST flag for not calling CheckPower in check + // which avoids buff charge to be ineffectively reduced (e.g. dk freezing fog for howling blast) + /// @TODO: Fix all calls to ApplySpellMod + Spell* spell = new Spell(bot, spellInfo, TRIGGERED_IGNORE_POWER_AND_REAGENT_COST); spell->m_targets.SetUnitTarget(target); spell->m_CastItem = castItem; @@ -2938,7 +2941,6 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, Unit* target, bool checkHasSpell, ; } spell->m_targets.SetItemTarget(itemTarget); - SpellCastResult result = spell->CheckCast(true); delete spell; diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index ea223dda..739b400e 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -511,6 +511,7 @@ bool PlayerbotAIConfig::Initialize() autoTrainSpells = sConfigMgr->GetOption("AiPlayerbot.AutoTrainSpells", "yes"); autoPickTalents = sConfigMgr->GetOption("AiPlayerbot.AutoPickTalents", true); autoUpgradeEquip = sConfigMgr->GetOption("AiPlayerbot.AutoUpgradeEquip", false); + hunterWolfPet = sConfigMgr->GetOption("AiPlayerbot.HunterWolfPet", 0); autoLearnTrainerSpells = sConfigMgr->GetOption("AiPlayerbot.AutoLearnTrainerSpells", true); autoLearnQuestSpells = sConfigMgr->GetOption("AiPlayerbot.AutoLearnQuestSpells", false); autoTeleportForLevel = sConfigMgr->GetOption("AiPlayerbot.AutoTeleportForLevel", false); diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index 1c54a0cd..14ee21c8 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -289,6 +289,7 @@ public: std::string autoTrainSpells; bool autoPickTalents; bool autoUpgradeEquip; + int32 hunterWolfPet; bool autoLearnTrainerSpells; bool autoDoQuests; bool enableNewRpgStrategy; diff --git a/src/PlayerbotMgr.cpp b/src/PlayerbotMgr.cpp index fc7cead7..0af0e8d5 100644 --- a/src/PlayerbotMgr.cpp +++ b/src/PlayerbotMgr.cpp @@ -484,10 +484,11 @@ void PlayerbotHolder::OnBotLogin(Player* const bot) if (!groupValid) { - WorldPacket p; - std::string const member = bot->GetName(); - p << uint32(PARTY_OP_LEAVE) << member << uint32(0); - bot->GetSession()->HandleGroupDisbandOpcode(p); + bot->RemoveFromGroup(); + // WorldPacket p; + // std::string const member = bot->GetName(); + // p << uint32(PARTY_OP_LEAVE) << member << uint32(0); + // bot->GetSession()->HandleGroupDisbandOpcode(p); } } diff --git a/src/factory/PlayerbotFactory.cpp b/src/factory/PlayerbotFactory.cpp index 436a58b2..ff9930a7 100644 --- a/src/factory/PlayerbotFactory.cpp +++ b/src/factory/PlayerbotFactory.cpp @@ -36,6 +36,7 @@ #include "SharedDefines.h" #include "SpellAuraDefines.h" #include "StatsWeightCalculator.h" +#include "World.h" #define PLAYER_SKILL_INDEX(x) (PLAYER_SKILL_INFO_1_1 + ((x)*3)) @@ -786,6 +787,13 @@ void PlayerbotFactory::InitPet() if (itr->second.minlevel > bot->GetLevel()) continue; + bool onlyWolf = sPlayerbotAIConfig->hunterWolfPet == 2 || + (sPlayerbotAIConfig->hunterWolfPet == 1 && + bot->GetLevel() >= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)); + // Wolf only (for higher dps) + if (onlyWolf && itr->second.family != CREATURE_FAMILY_WOLF) + continue; + ids.push_back(itr->first); } @@ -2807,11 +2815,11 @@ void PlayerbotFactory::InitAmmo() uint32 entry = sRandomItemMgr->GetAmmo(level, subClass); uint32 count = bot->GetItemCount(entry); - uint32 maxCount = 6000; + uint32 maxCount = bot->getClass() == CLASS_HUNTER ? 6000 : 1000; - if (count < maxCount / 2) + if (count < maxCount) { - if (Item* newItem = StoreNewItemInInventorySlot(bot, entry, maxCount / 2)) + if (Item* newItem = StoreNewItemInInventorySlot(bot, entry, maxCount - count)) { newItem->AddToUpdateQueueOf(bot); } diff --git a/src/strategy/actions/LeaveGroupAction.cpp b/src/strategy/actions/LeaveGroupAction.cpp index b2594e0f..58602805 100644 --- a/src/strategy/actions/LeaveGroupAction.cpp +++ b/src/strategy/actions/LeaveGroupAction.cpp @@ -84,9 +84,7 @@ bool LeaveGroupAction::Leave(Player* player) bool shouldStay = randomBot && bot->GetGroup() && player == bot; if (!shouldStay) { - WorldPacket p; - p << uint32(PARTY_OP_LEAVE) << bot->GetName() << uint32(0); - bot->GetSession()->HandleGroupDisbandOpcode(p); + bot->RemoveFromGroup(); } if (randomBot) diff --git a/src/strategy/deathknight/DKAiObjectContext.cpp b/src/strategy/deathknight/DKAiObjectContext.cpp index 45d4b65f..c02ff82b 100644 --- a/src/strategy/deathknight/DKAiObjectContext.cpp +++ b/src/strategy/deathknight/DKAiObjectContext.cpp @@ -74,14 +74,14 @@ public: creators["plague strike"] = &DeathKnightTriggerFactoryInternal::plague_strike; creators["plague strike on attacker"] = &DeathKnightTriggerFactoryInternal::plague_strike_on_attacker; creators["icy touch"] = &DeathKnightTriggerFactoryInternal::icy_touch; - creators["icy touch 8s"] = &DeathKnightTriggerFactoryInternal::icy_touch_8s; - creators["dd cd and icy touch 8s"] = &DeathKnightTriggerFactoryInternal::dd_cd_and_icy_touch_8s; + creators["icy touch 3s"] = &DeathKnightTriggerFactoryInternal::icy_touch_3s; + creators["dd cd and icy touch 3s"] = &DeathKnightTriggerFactoryInternal::dd_cd_and_icy_touch_3s; creators["death coil"] = &DeathKnightTriggerFactoryInternal::death_coil; creators["icy touch on attacker"] = &DeathKnightTriggerFactoryInternal::icy_touch_on_attacker; creators["improved icy talons"] = &DeathKnightTriggerFactoryInternal::improved_icy_talons; creators["plague strike"] = &DeathKnightTriggerFactoryInternal::plague_strike; - creators["plague strike 8s"] = &DeathKnightTriggerFactoryInternal::plague_strike_8s; - creators["dd cd and plague strike 8s"] = &DeathKnightTriggerFactoryInternal::dd_cd_and_plague_strike_8s; + creators["plague strike 3s"] = &DeathKnightTriggerFactoryInternal::plague_strike_3s; + creators["dd cd and plague strike 3s"] = &DeathKnightTriggerFactoryInternal::dd_cd_and_plague_strike_3s; creators["horn of winter"] = &DeathKnightTriggerFactoryInternal::horn_of_winter; creators["mind freeze"] = &DeathKnightTriggerFactoryInternal::mind_freeze; creators["mind freeze on enemy healer"] = &DeathKnightTriggerFactoryInternal::mind_freeze_on_enemy_healer; @@ -94,6 +94,7 @@ public: creators["high blood rune"] = &DeathKnightTriggerFactoryInternal::high_blood_rune; creators["high frost rune"] = &DeathKnightTriggerFactoryInternal::high_frost_rune; creators["high unholy rune"] = &DeathKnightTriggerFactoryInternal::high_unholy_rune; + creators["no rune"] = &DeathKnightTriggerFactoryInternal::no_rune; creators["freezing fog"] = &DeathKnightTriggerFactoryInternal::freezing_fog; creators["no desolation"] = &DeathKnightTriggerFactoryInternal::no_desolation; creators["dd cd and no desolation"] = &DeathKnightTriggerFactoryInternal::dd_cd_and_no_desolation; @@ -106,15 +107,15 @@ private: static Trigger* pestilence_glyph(PlayerbotAI* botAI) { return new PestilenceGlyphTrigger(botAI); } static Trigger* blood_strike(PlayerbotAI* botAI) { return new BloodStrikeTrigger(botAI); } static Trigger* plague_strike(PlayerbotAI* botAI) { return new PlagueStrikeDebuffTrigger(botAI); } - static Trigger* plague_strike_8s(PlayerbotAI* botAI) { return new PlagueStrike8sDebuffTrigger(botAI); } - static Trigger* dd_cd_and_plague_strike_8s(PlayerbotAI* botAI) { return new TwoTriggers(botAI, "death and decay cooldown", "plague strike 8s"); } + static Trigger* plague_strike_3s(PlayerbotAI* botAI) { return new PlagueStrike3sDebuffTrigger(botAI); } + static Trigger* dd_cd_and_plague_strike_3s(PlayerbotAI* botAI) { return new TwoTriggers(botAI, "death and decay cooldown", "plague strike 3s"); } static Trigger* plague_strike_on_attacker(PlayerbotAI* botAI) { return new PlagueStrikeDebuffOnAttackerTrigger(botAI); } static Trigger* icy_touch(PlayerbotAI* botAI) { return new IcyTouchDebuffTrigger(botAI); } - static Trigger* icy_touch_8s(PlayerbotAI* botAI) { return new IcyTouch8sDebuffTrigger(botAI); } - static Trigger* dd_cd_and_icy_touch_8s(PlayerbotAI* botAI) { return new TwoTriggers(botAI, "death and decay cooldown", "icy touch 8s"); } + static Trigger* icy_touch_3s(PlayerbotAI* botAI) { return new IcyTouch3sDebuffTrigger(botAI); } + static Trigger* dd_cd_and_icy_touch_3s(PlayerbotAI* botAI) { return new TwoTriggers(botAI, "death and decay cooldown", "icy touch 3s"); } static Trigger* death_coil(PlayerbotAI* botAI) { return new DeathCoilTrigger(botAI); } static Trigger* icy_touch_on_attacker(PlayerbotAI* botAI) { return new IcyTouchDebuffOnAttackerTrigger(botAI); } static Trigger* improved_icy_talons(PlayerbotAI* botAI) { return new ImprovedIcyTalonsTrigger(botAI); } @@ -136,6 +137,7 @@ private: static Trigger* high_blood_rune(PlayerbotAI* botAI) { return new HighBloodRuneTrigger(botAI); } static Trigger* high_frost_rune(PlayerbotAI* botAI) { return new HighFrostRuneTrigger(botAI); } static Trigger* high_unholy_rune(PlayerbotAI* botAI) { return new HighUnholyRuneTrigger(botAI); } + static Trigger* no_rune(PlayerbotAI* botAI) { return new NoRuneTrigger(botAI); } static Trigger* freezing_fog(PlayerbotAI* botAI) { return new FreezingFogTrigger(botAI); } static Trigger* no_desolation(PlayerbotAI* botAI) { return new DesolationTrigger(botAI); } static Trigger* dd_cd_and_no_desolation(PlayerbotAI* botAI) { return new TwoTriggers(botAI, "death and decay cooldown", "no desolation"); } diff --git a/src/strategy/deathknight/DKTriggers.cpp b/src/strategy/deathknight/DKTriggers.cpp index 2d1cbb15..430ccdf9 100644 --- a/src/strategy/deathknight/DKTriggers.cpp +++ b/src/strategy/deathknight/DKTriggers.cpp @@ -37,20 +37,32 @@ bool PestilenceGlyphTrigger::IsActive() return false; } +// Based on runeSlotTypes bool HighBloodRuneTrigger::IsActive() { - return !bot->GetRuneCooldown(0) && !bot->GetRuneCooldown(1); + return bot->GetRuneCooldown(0) <= 2000 && bot->GetRuneCooldown(1) <= 2000; } bool HighFrostRuneTrigger::IsActive() { - return !bot->GetRuneCooldown(2) && !bot->GetRuneCooldown(3); + return bot->GetRuneCooldown(4) <= 2000 && bot->GetRuneCooldown(5) <= 2000; } bool HighUnholyRuneTrigger::IsActive() { - return !bot->GetRuneCooldown(4) && !bot->GetRuneCooldown(5); + return bot->GetRuneCooldown(2) <= 2000 && bot->GetRuneCooldown(3) <= 2000; } + +bool NoRuneTrigger::IsActive() +{ + for (uint32 i = 0; i < MAX_RUNES; ++i) + { + if (!bot->GetRuneCooldown(i)) + return false; + } + return true; +} + bool DesolationTrigger::IsActive() { return bot->HasAura(66817) && BuffTrigger::IsActive(); @@ -62,5 +74,5 @@ bool DeathAndDecayCooldownTrigger::IsActive() if (!spellId) return true; - return bot->GetSpellCooldownDelay(spellId) >= 3000; + return bot->GetSpellCooldownDelay(spellId) >= 2000; } \ No newline at end of file diff --git a/src/strategy/deathknight/DKTriggers.h b/src/strategy/deathknight/DKTriggers.h index ef82394b..721a1dbc 100644 --- a/src/strategy/deathknight/DKTriggers.h +++ b/src/strategy/deathknight/DKTriggers.h @@ -20,10 +20,10 @@ public: PlagueStrikeDebuffTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "blood plague", 1, true, .0f) {} }; -class PlagueStrike8sDebuffTrigger : public DebuffTrigger +class PlagueStrike3sDebuffTrigger : public DebuffTrigger { public: - PlagueStrike8sDebuffTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "blood plague", 1, true, .0f, 3000) {} + PlagueStrike3sDebuffTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "blood plague", 1, true, .0f, 3000) {} }; // DEBUFF_CHECKISOWNER_TRIGGER(IcyTouchDebuffTrigger, "frost fever"); @@ -33,10 +33,10 @@ public: IcyTouchDebuffTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "frost fever", 1, true, .0f) {} }; -class IcyTouch8sDebuffTrigger : public DebuffTrigger +class IcyTouch3sDebuffTrigger : public DebuffTrigger { public: - IcyTouch8sDebuffTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "frost fever", 1, true, .0f, 3000) {} + IcyTouch3sDebuffTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "frost fever", 1, true, .0f, 3000) {} }; BUFF_TRIGGER(UnbreakableArmorTrigger, "unbreakable armor"); @@ -165,6 +165,13 @@ public: bool IsActive() override; }; +class NoRuneTrigger : public Trigger +{ +public: + NoRuneTrigger(PlayerbotAI* botAI) : Trigger(botAI, "no rune") {} + bool IsActive() override; +}; + class FreezingFogTrigger : public HasAuraTrigger { public: diff --git a/src/strategy/deathknight/UnholyDKStrategy.cpp b/src/strategy/deathknight/UnholyDKStrategy.cpp index fb1baca4..cc60e4f8 100644 --- a/src/strategy/deathknight/UnholyDKStrategy.cpp +++ b/src/strategy/deathknight/UnholyDKStrategy.cpp @@ -80,7 +80,7 @@ NextAction** UnholyDKStrategy::getDefaultActions() return NextAction::array( 0, new NextAction("death and decay", ACTION_HIGH + 5), new NextAction("summon gargoyle", ACTION_DEFAULT + 0.4f), - new NextAction("empower rune weapon", ACTION_DEFAULT + 0.3f), + // new NextAction("empower rune weapon", ACTION_DEFAULT + 0.3f), new NextAction("horn of winter", ACTION_DEFAULT + 0.2f), new NextAction("death coil", ACTION_DEFAULT + 0.1f), new NextAction("melee", ACTION_DEFAULT), nullptr); @@ -93,8 +93,8 @@ void UnholyDKStrategy::InitTriggers(std::vector& triggers) "death and decay cooldown", NextAction::array(0, new NextAction("ghoul frenzy", ACTION_DEFAULT + 0.9f), new NextAction("scourge strike", ACTION_DEFAULT + 0.8f), - new NextAction("blood boil", ACTION_DEFAULT + 0.7f), - new NextAction("icy touch", ACTION_DEFAULT + 0.6f), + new NextAction("icy touch", ACTION_DEFAULT + 0.7f), + new NextAction("blood strike", ACTION_DEFAULT + 0.6f), new NextAction("plague strike", ACTION_DEFAULT + 0.5f), nullptr))); @@ -111,24 +111,26 @@ void UnholyDKStrategy::InitTriggers(std::vector& triggers) new NextAction("icy touch", ACTION_NORMAL + 3), nullptr))); triggers.push_back(new TriggerNode( - "high unholy rune", NextAction::array(0, - new NextAction("plague strike", ACTION_NORMAL + 2), nullptr))); + "high blood rune", NextAction::array(0, new NextAction("blood strike", ACTION_NORMAL + 2), nullptr))); triggers.push_back(new TriggerNode( - "high blood rune", NextAction::array(0, new NextAction("blood strike", ACTION_NORMAL + 1), nullptr))); + "high unholy rune", NextAction::array(0, + new NextAction("plague strike", ACTION_NORMAL + 1), nullptr))); triggers.push_back( - new TriggerNode("dd cd and plague strike 8s", NextAction::array(0, new NextAction("plague strike", ACTION_HIGH + 2), nullptr))); + new TriggerNode("dd cd and plague strike 3s", NextAction::array(0, new NextAction("plague strike", ACTION_HIGH + 1), nullptr))); triggers.push_back( - new TriggerNode("dd cd and icy touch 8s", NextAction::array(0, new NextAction("icy touch", ACTION_HIGH + 1), nullptr))); + new TriggerNode("dd cd and icy touch 3s", NextAction::array(0, new NextAction("icy touch", ACTION_HIGH + 2), nullptr))); + triggers.push_back( + new TriggerNode("no rune", NextAction::array(0, new NextAction("empower rune weapon", ACTION_HIGH + 1), nullptr))); // triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction(, ACTION_NORMAL + 2), nullptr))); triggers.push_back(new TriggerNode( "army of the dead", NextAction::array(0, new NextAction("army of the dead", ACTION_HIGH + 6), nullptr))); triggers.push_back( - new TriggerNode("bone shield", NextAction::array(0, new NextAction("bone shield", ACTION_HIGH + 1), nullptr))); + new TriggerNode("bone shield", NextAction::array(0, new NextAction("bone shield", ACTION_HIGH + 3), nullptr))); } void UnholyDKAoeStrategy::InitTriggers(std::vector& triggers) diff --git a/src/strategy/hunter/DpsHunterStrategy.cpp b/src/strategy/hunter/DpsHunterStrategy.cpp index 3ba171a2..b8709378 100644 --- a/src/strategy/hunter/DpsHunterStrategy.cpp +++ b/src/strategy/hunter/DpsHunterStrategy.cpp @@ -41,8 +41,11 @@ DpsHunterStrategy::DpsHunterStrategy(PlayerbotAI* botAI) : GenericHunterStrategy NextAction** DpsHunterStrategy::getDefaultActions() { return NextAction::array( - 0, new NextAction("kill shot", ACTION_DEFAULT + 0.8f), new NextAction("chimera shot", ACTION_DEFAULT + 0.7f), - new NextAction("explosive shot", ACTION_DEFAULT + 0.6f), new NextAction("aimed shot", ACTION_DEFAULT + 0.5f), + 0, + new NextAction("explosive shot", ACTION_HIGH + 1.0f), + new NextAction("kill shot", ACTION_DEFAULT + 0.8f), + new NextAction("chimera shot", ACTION_DEFAULT + 0.6f), + new NextAction("aimed shot", ACTION_DEFAULT + 0.5f), new NextAction("silencing shot", ACTION_DEFAULT + 0.4f), new NextAction("kill command", ACTION_DEFAULT + 0.3f), // new NextAction("arcane shot", ACTION_DEFAULT + 0.2f), @@ -55,7 +58,7 @@ void DpsHunterStrategy::InitTriggers(std::vector& triggers) GenericHunterStrategy::InitTriggers(triggers); triggers.push_back( - new TriggerNode("black arrow", NextAction::array(0, new NextAction("black arrow", 15.0f), nullptr))); + new TriggerNode("black arrow", NextAction::array(0, new NextAction("black arrow", 19.0f), nullptr))); triggers.push_back( new TriggerNode("low mana", NextAction::array(0, new NextAction("viper sting", 23.0f), nullptr))); triggers.push_back( diff --git a/src/strategy/hunter/GenericHunterNonCombatStrategy.cpp b/src/strategy/hunter/GenericHunterNonCombatStrategy.cpp index ca7debfa..e64a9d78 100644 --- a/src/strategy/hunter/GenericHunterNonCombatStrategy.cpp +++ b/src/strategy/hunter/GenericHunterNonCombatStrategy.cpp @@ -49,6 +49,8 @@ void GenericHunterNonCombatStrategy::InitTriggers(std::vector& tri triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("apply oil", 1.0f), nullptr))); triggers.push_back( new TriggerNode("low ammo", NextAction::array(0, new NextAction("say::low ammo", ACTION_NORMAL), nullptr))); + triggers.push_back( + new TriggerNode("no track", NextAction::array(0, new NextAction("track humanoids", ACTION_NORMAL), nullptr))); // triggers.push_back(new TriggerNode("no ammo", NextAction::array(0, new NextAction("switch to melee", // ACTION_NORMAL + 1), new NextAction("say::no ammo", ACTION_NORMAL), nullptr))); triggers.push_back(new // TriggerNode("has ammo", NextAction::array(0, new NextAction("switch to ranged", ACTION_NORMAL), nullptr))); diff --git a/src/strategy/hunter/HunterAiObjectContext.cpp b/src/strategy/hunter/HunterAiObjectContext.cpp index dbfb6a54..2dcd3550 100644 --- a/src/strategy/hunter/HunterAiObjectContext.cpp +++ b/src/strategy/hunter/HunterAiObjectContext.cpp @@ -75,6 +75,7 @@ public: creators["aspect of the wild"] = &HunterTriggerFactoryInternal::aspect_of_the_wild; creators["aspect of the viper"] = &HunterTriggerFactoryInternal::aspect_of_the_viper; creators["trueshot aura"] = &HunterTriggerFactoryInternal::trueshot_aura; + creators["no track"] = &HunterTriggerFactoryInternal::no_track; creators["serpent sting on attacker"] = &HunterTriggerFactoryInternal::serpent_sting_on_attacker; creators["pet not happy"] = &HunterTriggerFactoryInternal::pet_not_happy; creators["concussive shot on snare target"] = &HunterTriggerFactoryInternal::concussive_shot_on_snare_target; @@ -99,6 +100,7 @@ private: static Trigger* pet_not_happy(PlayerbotAI* botAI) { return new HunterPetNotHappy(botAI); } static Trigger* serpent_sting_on_attacker(PlayerbotAI* botAI) { return new SerpentStingOnAttackerTrigger(botAI); } static Trigger* trueshot_aura(PlayerbotAI* botAI) { return new TrueshotAuraTrigger(botAI); } + static Trigger* no_track(PlayerbotAI* botAI) { return new NoTrackTrigger(botAI); } static Trigger* aspect_of_the_viper(PlayerbotAI* botAI) { return new HunterAspectOfTheViperTrigger(botAI); } static Trigger* black_arrow(PlayerbotAI* botAI) { return new BlackArrowTrigger(botAI); } static Trigger* NoStings(PlayerbotAI* botAI) { return new HunterNoStingsActiveTrigger(botAI); } @@ -159,6 +161,7 @@ public: creators["aspect of the pack"] = &HunterAiObjectContextInternal::aspect_of_the_pack; creators["aspect of the cheetah"] = &HunterAiObjectContextInternal::aspect_of_the_cheetah; creators["trueshot aura"] = &HunterAiObjectContextInternal::trueshot_aura; + creators["track humanoids"] = &HunterAiObjectContextInternal::track_humanoids; creators["feign death"] = &HunterAiObjectContextInternal::feign_death; creators["wing clip"] = &HunterAiObjectContextInternal::wing_clip; creators["raptor strike"] = &HunterAiObjectContextInternal::raptor_strike; @@ -182,6 +185,7 @@ private: static Action* feed_pet(PlayerbotAI* botAI) { return new FeedPetAction(botAI); } static Action* feign_death(PlayerbotAI* botAI) { return new CastFeignDeathAction(botAI); } static Action* trueshot_aura(PlayerbotAI* botAI) { return new CastTrueshotAuraAction(botAI); } + static Action* track_humanoids(PlayerbotAI* botAI) { return new CastBuffSpellAction(botAI, "track humanoids"); } static Action* auto_shot(PlayerbotAI* botAI) { return new CastAutoShotAction(botAI); } static Action* aimed_shot(PlayerbotAI* botAI) { return new CastAimedShotAction(botAI); } static Action* chimera_shot(PlayerbotAI* botAI) { return new CastChimeraShotAction(botAI); } diff --git a/src/strategy/hunter/HunterTriggers.cpp b/src/strategy/hunter/HunterTriggers.cpp index 941ce755..6c9354fa 100644 --- a/src/strategy/hunter/HunterTriggers.cpp +++ b/src/strategy/hunter/HunterTriggers.cpp @@ -56,7 +56,7 @@ bool HunterPetNotHappy::IsActive() bool HunterAspectOfTheViperTrigger::IsActive() { return SpellTrigger::IsActive() && !botAI->HasAura(spell, GetTarget()) && - AI_VALUE2(uint8, "mana", "self target") < sPlayerbotAIConfig->lowMana; + AI_VALUE2(uint8, "mana", "self target") < (sPlayerbotAIConfig->lowMana / 2); ; } @@ -88,3 +88,36 @@ bool SwitchToMeleeTrigger::IsActive() (target->GetVictim() == bot && sServerFacade->IsDistanceLessOrEqualThan(AI_VALUE2(float, "distance", "current target"), 8.0f)); } + +bool NoTrackTrigger::IsActive() +{ + std::vector track_list = { + "track beasts", + "track demons", + "track dragonkin", + "track elementals", + "track giants", + "track hidden", + "track humanoids" + }; + + for (auto &track: track_list) + { + if (botAI->HasAura(track, bot)) + return false; + } + return true; +} + +bool SerpentStingOnAttackerTrigger::IsActive() +{ + if (!DebuffOnAttackerTrigger::IsActive()) + return false; + Unit* target = GetTarget(); + if (!target) + { + return false; + } + return !botAI->HasAura("scorpid sting", target, false, true) && + !botAI->HasAura("viper sting", target, false, true); +} \ No newline at end of file diff --git a/src/strategy/hunter/HunterTriggers.h b/src/strategy/hunter/HunterTriggers.h index 2ff44fe3..e9d09c91 100644 --- a/src/strategy/hunter/HunterTriggers.h +++ b/src/strategy/hunter/HunterTriggers.h @@ -101,10 +101,18 @@ public: TrueshotAuraTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "trueshot aura") {} }; +class NoTrackTrigger : public BuffTrigger +{ +public: + NoTrackTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "no track") {} + bool IsActive() override; +}; + class SerpentStingOnAttackerTrigger : public DebuffOnAttackerTrigger { public: SerpentStingOnAttackerTrigger(PlayerbotAI* botAI) : DebuffOnAttackerTrigger(botAI, "serpent sting", true) {} + bool IsActive() override; }; BEGIN_TRIGGER(HunterPetNotHappy, Trigger) diff --git a/src/strategy/mage/ArcaneMageStrategy.cpp b/src/strategy/mage/ArcaneMageStrategy.cpp index 3546072b..a350fc74 100644 --- a/src/strategy/mage/ArcaneMageStrategy.cpp +++ b/src/strategy/mage/ArcaneMageStrategy.cpp @@ -31,7 +31,7 @@ private: { return new ActionNode("arcane barrage", /*P*/ nullptr, - /*A*/ NextAction::array(0, new NextAction("arcane missiles"), nullptr), + /*A*/ nullptr, /*C*/ nullptr); } @@ -59,8 +59,10 @@ ArcaneMageStrategy::ArcaneMageStrategy(PlayerbotAI* botAI) : GenericMageStrategy NextAction** ArcaneMageStrategy::getDefaultActions() { - return NextAction::array(0, new NextAction("arcane blast", ACTION_DEFAULT + 0.1f), - new NextAction("shoot", ACTION_DEFAULT), NULL); + return NextAction::array(0, new NextAction("arcane blast", ACTION_DEFAULT + 0.3f), + // new NextAction("arcane barrage", ACTION_DEFAULT + 0.2f), // cast during movement + new NextAction("fire blast", ACTION_DEFAULT + 0.1f), // cast during movement + new NextAction("shoot", ACTION_DEFAULT), nullptr); } void ArcaneMageStrategy::InitTriggers(std::vector& triggers) diff --git a/src/strategy/mage/FireMageStrategy.cpp b/src/strategy/mage/FireMageStrategy.cpp index 1d9512b3..5e7df0ae 100644 --- a/src/strategy/mage/FireMageStrategy.cpp +++ b/src/strategy/mage/FireMageStrategy.cpp @@ -9,7 +9,8 @@ NextAction** FireMageStrategy::getDefaultActions() { - return NextAction::array(0, new NextAction("fireball", ACTION_DEFAULT + 0.1f), + return NextAction::array(0, new NextAction("fireball", ACTION_DEFAULT + 0.2f), + new NextAction("fire blast", ACTION_DEFAULT + 0.1f), // cast during movement new NextAction("shoot", ACTION_DEFAULT), NULL); } @@ -23,6 +24,8 @@ void FireMageStrategy::InitTriggers(std::vector& triggers) new TriggerNode("hot streak", NextAction::array(0, new NextAction("pyroblast", 25.0f), nullptr))); triggers.push_back( new TriggerNode("combustion", NextAction::array(0, new NextAction("combustion", 50.0f), nullptr))); + triggers.push_back( + new TriggerNode("living bomb", NextAction::array(0, new NextAction("living bomb", 19.0f), nullptr))); // triggers.push_back(new TriggerNode("enemy too close for spell", NextAction::array(0, new NextAction("dragon's // breath", 70.0f), nullptr))); } @@ -30,7 +33,10 @@ void FireMageStrategy::InitTriggers(std::vector& triggers) void FireMageAoeStrategy::InitTriggers(std::vector& triggers) { triggers.push_back( - new TriggerNode("medium aoe", NextAction::array(0, new NextAction("flamestrike", 20.0f), nullptr))); - triggers.push_back( - new TriggerNode("living bomb", NextAction::array(0, new NextAction("living bomb", 25.0f), nullptr))); + new TriggerNode("medium aoe", NextAction::array(0, + new NextAction("dragon's breath", 24.0f), + new NextAction("flamestrike", 23.0f), + new NextAction("blast wave", 22.0f), + new NextAction("living bomb on attackers", 21.0f), + new NextAction("blizzard", 20.0f), nullptr))); } diff --git a/src/strategy/mage/FrostMageStrategy.cpp b/src/strategy/mage/FrostMageStrategy.cpp index 6dd46152..6dfaaf36 100644 --- a/src/strategy/mage/FrostMageStrategy.cpp +++ b/src/strategy/mage/FrostMageStrategy.cpp @@ -77,7 +77,7 @@ void FrostMageStrategy::InitTriggers(std::vector& triggers) triggers.push_back( new TriggerNode("has pet", NextAction::array(0, new NextAction("toggle pet spell", ACTION_HIGH + 1), nullptr))); triggers.push_back( - new TriggerNode("ice barrier", NextAction::array(0, new NextAction("ice barrier", ACTION_NORMAL), nullptr))); + new TriggerNode("medium health", NextAction::array(0, new NextAction("ice barrier", ACTION_NORMAL), nullptr))); triggers.push_back(new TriggerNode( "brain freeze", NextAction::array(0, new NextAction("frostfire bolt", ACTION_NORMAL + 3), nullptr))); diff --git a/src/strategy/mage/GenericMageNonCombatStrategy.cpp b/src/strategy/mage/GenericMageNonCombatStrategy.cpp index 963d9edb..8e7a475e 100644 --- a/src/strategy/mage/GenericMageNonCombatStrategy.cpp +++ b/src/strategy/mage/GenericMageNonCombatStrategy.cpp @@ -54,6 +54,9 @@ void GenericMageNonCombatStrategy::InitTriggers(std::vector& trigg triggers.push_back( new TriggerNode("arcane intellect", NextAction::array(0, new NextAction("arcane intellect", 21.0f), nullptr))); + + triggers.push_back( + new TriggerNode("no focus magic", NextAction::array(0, new NextAction("focus magic on party", 19.0f), nullptr))); // triggers.push_back(new TriggerNode("no drink", NextAction::array(0, new NextAction("conjure water", 16.0f), // nullptr))); triggers.push_back(new TriggerNode("no food", NextAction::array(0, new NextAction("conjure // food", 15.0f), nullptr))); diff --git a/src/strategy/mage/GenericMageStrategy.cpp b/src/strategy/mage/GenericMageStrategy.cpp index ad5862cf..453dbf7a 100644 --- a/src/strategy/mage/GenericMageStrategy.cpp +++ b/src/strategy/mage/GenericMageStrategy.cpp @@ -59,7 +59,7 @@ private: { return new ActionNode("fire blast", /*P*/ nullptr, - /*A*/ NextAction::array(0, new NextAction("scorch"), nullptr), + /*A*/ nullptr, /*C*/ nullptr); } @@ -115,16 +115,16 @@ private: { return new ActionNode("dragon's breath", /*P*/ nullptr, - /*A*/ NextAction::array(0, new NextAction("blast wave"), nullptr), - /*C*/ NextAction::array(0, new NextAction("flamestrike", 71.0f), nullptr)); + /*A*/ nullptr, + /*C*/ nullptr); } static ActionNode* blast_wave([[maybe_unused]] PlayerbotAI* botAI) { return new ActionNode("blast wave", /*P*/ nullptr, - /*A*/ NextAction::array(0, new NextAction("frost nova"), nullptr), - /*C*/ NextAction::array(0, new NextAction("flamestrike", 71.0f), nullptr)); + /*A*/ nullptr, + /*C*/ nullptr); } static ActionNode* remove_curse([[maybe_unused]] PlayerbotAI* botAI) @@ -194,8 +194,7 @@ void MageBoostStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode("icy veins", NextAction::array(0, new NextAction("icy veins", 50.0f), nullptr))); triggers.push_back( new TriggerNode("presence of mind", NextAction::array(0, new NextAction("presence of mind", 42.0f), nullptr))); - // triggers.push_back(new TriggerNode("arcane power", NextAction::array(0, new NextAction("arcane power", 41.0f), - // nullptr))); + // triggers.push_back(new TriggerNode("arcane power", NextAction::array(0, new NextAction("arcane power", 41.0f), nullptr))); triggers.push_back( new TriggerNode("mirror image", NextAction::array(0, new NextAction("mirror image", 41.0f), nullptr))); } diff --git a/src/strategy/mage/MageActions.cpp b/src/strategy/mage/MageActions.cpp index beb82636..786b1717 100644 --- a/src/strategy/mage/MageActions.cpp +++ b/src/strategy/mage/MageActions.cpp @@ -5,13 +5,18 @@ #include "MageActions.h" +#include "PlayerbotAIConfig.h" #include "Playerbots.h" #include "ServerFacade.h" +#include "SharedDefines.h" Value* CastPolymorphAction::GetTargetValue() { return context->GetValue("cc target", getName()); } bool CastFrostNovaAction::isUseful() { + Unit* target = AI_VALUE(Unit*, "current target"); + if (target && target->ToCreature() && target->ToCreature()->HasMechanicTemplateImmunity(1 << (MECHANIC_FREEZE - 1))) + return false; return sServerFacade->IsDistanceLessOrEqualThan(AI_VALUE2(float, "distance", GetTargetName()), 10.f); } @@ -21,3 +26,65 @@ bool CastConeOfColdAction::isUseful() bool targetClose = sServerFacade->IsDistanceLessOrEqualThan(AI_VALUE2(float, "distance", GetTargetName()), 10.f); return facingTarget && targetClose; } + +bool CastDragonsBreathAction::isUseful() +{ + Unit* target = AI_VALUE(Unit*, "current target"); + if (!target) + return false; + bool facingTarget = AI_VALUE2(bool, "facing", "current target"); + bool targetClose = bot->IsWithinCombatRange(target, 10.0f); + return facingTarget && targetClose; +} + +bool CastBlastWaveAction::isUseful() +{ + Unit* target = AI_VALUE(Unit*, "current target"); + if (!target) + return false; + bool targetClose = bot->IsWithinCombatRange(target, 10.0f); + return targetClose; +} + +Unit* CastFocusMagicOnPartyAction::GetTarget() +{ + Group* group = bot->GetGroup(); + if (!group) + return nullptr; + + Unit* casterDps = nullptr; + Unit* healer = nullptr; + Unit* target = nullptr; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || member == bot || !member->IsAlive()) + continue; + + if (member->GetMap() != bot->GetMap() || bot->GetDistance(member) > sPlayerbotAIConfig->spellDistance) + continue; + + if (member->HasAura(54646)) + continue; + + if (member->getClass() == CLASS_MAGE) + return member; + + if (!casterDps && botAI->IsCaster(member) && botAI->IsDps(member)) + casterDps = member; + + if (!healer && botAI->IsHeal(member)) + healer = member; + + if (!target) + target = member; + } + + if (casterDps) + return casterDps; + + if (healer) + return healer; + + return target; +} \ No newline at end of file diff --git a/src/strategy/mage/MageActions.h b/src/strategy/mage/MageActions.h index 844bea67..66f6157e 100644 --- a/src/strategy/mage/MageActions.h +++ b/src/strategy/mage/MageActions.h @@ -57,10 +57,10 @@ public: CastPyroblastAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "pyroblast") {} }; -class CastFlamestrikeAction : public CastSpellAction +class CastFlamestrikeAction : public CastDebuffSpellAction { public: - CastFlamestrikeAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "flamestrike") {} + CastFlamestrikeAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "flamestrike", true, 0.0f) {} ActionThreatType getThreatType() override { return ActionThreatType::Aoe; } }; @@ -243,11 +243,18 @@ public: CastLivingBombAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "living bomb", true) {} }; +class CastLivingBombOnAttackersAction : public CastDebuffSpellOnAttackerAction +{ +public: + CastLivingBombOnAttackersAction(PlayerbotAI* botAI) : CastDebuffSpellOnAttackerAction(botAI, "living bomb", true) {} +}; + class CastDragonsBreathAction : public CastSpellAction { public: CastDragonsBreathAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "dragon's breath") {} ActionThreatType getThreatType() override { return ActionThreatType::Aoe; } + bool isUseful() override; }; class CastBlastWaveAction : public CastSpellAction @@ -255,6 +262,7 @@ class CastBlastWaveAction : public CastSpellAction public: CastBlastWaveAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "blast wave") {} ActionThreatType getThreatType() override { return ActionThreatType::Aoe; } + bool isUseful() override; }; class CastInvisibilityAction : public CastBuffSpellAction @@ -294,4 +302,11 @@ public: CastMirrorImageAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "mirror image") {} }; +class CastFocusMagicOnPartyAction : public CastSpellAction +{ +public: + CastFocusMagicOnPartyAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "focus magic") {} + Unit* GetTarget() override; +}; + #endif diff --git a/src/strategy/mage/MageAiObjectContext.cpp b/src/strategy/mage/MageAiObjectContext.cpp index 23db7aa6..4439194f 100644 --- a/src/strategy/mage/MageAiObjectContext.cpp +++ b/src/strategy/mage/MageAiObjectContext.cpp @@ -108,6 +108,7 @@ public: creators["mirror image"] = &MageTriggerFactoryInternal::mirror_image; creators["frost nova on target"] = &MageTriggerFactoryInternal::frost_nova_on_target; creators["frostbite on target"] = &MageTriggerFactoryInternal::frostbite_on_target; + creators["no focus magic"] = &MageTriggerFactoryInternal::no_focus_magic; } private: @@ -141,6 +142,7 @@ private: static Trigger* mirror_image(PlayerbotAI* botAI) { return new MirrorImageTrigger(botAI); } static Trigger* frost_nova_on_target(PlayerbotAI* botAI) { return new FrostNovaOnTargetTrigger(botAI); } static Trigger* frostbite_on_target(PlayerbotAI* botAI) { return new FrostbiteOnTargetTrigger(botAI); } + static Trigger* no_focus_magic(PlayerbotAI* botAI) { return new NoFocusMagicTrigger(botAI); } }; class MageAiObjectContextInternal : public NamedObjectContext @@ -184,6 +186,7 @@ public: creators["polymorph"] = &MageAiObjectContextInternal::polymorph; creators["spellsteal"] = &MageAiObjectContextInternal::spellsteal; creators["living bomb"] = &MageAiObjectContextInternal::living_bomb; + creators["living bomb on attackers"] = &MageAiObjectContextInternal::living_bomb_on_attackers; creators["dragon's breath"] = &MageAiObjectContextInternal::dragons_breath; creators["blast wave"] = &MageAiObjectContextInternal::blast_wave; creators["invisibility"] = &MageAiObjectContextInternal::invisibility; @@ -195,6 +198,7 @@ public: creators["fire ward"] = &MageAiObjectContextInternal::fire_ward; creators["frost ward"] = &MageAiObjectContextInternal::frost_ward; creators["mirror image"] = &MageAiObjectContextInternal::mirror_image; + creators["focus magic on party"] = &MageAiObjectContextInternal::focus_magic_on_party; } private: @@ -242,6 +246,7 @@ private: static Action* polymorph(PlayerbotAI* botAI) { return new CastPolymorphAction(botAI); } static Action* spellsteal(PlayerbotAI* botAI) { return new CastSpellstealAction(botAI); } static Action* living_bomb(PlayerbotAI* botAI) { return new CastLivingBombAction(botAI); } + static Action* living_bomb_on_attackers(PlayerbotAI* botAI) { return new CastLivingBombOnAttackersAction(botAI); } static Action* dragons_breath(PlayerbotAI* botAI) { return new CastDragonsBreathAction(botAI); } static Action* blast_wave(PlayerbotAI* botAI) { return new CastBlastWaveAction(botAI); } static Action* invisibility(PlayerbotAI* botAI) { return new CastInvisibilityAction(botAI); } @@ -251,6 +256,7 @@ private: return new CastCounterspellOnEnemyHealerAction(botAI); } static Action* mirror_image(PlayerbotAI* botAI) { return new CastMirrorImageAction(botAI); } + static Action* focus_magic_on_party(PlayerbotAI* botAI) { return new CastFocusMagicOnPartyAction(botAI); } }; MageAiObjectContext::MageAiObjectContext(PlayerbotAI* botAI) : AiObjectContext(botAI) diff --git a/src/strategy/mage/MageTriggers.cpp b/src/strategy/mage/MageTriggers.cpp index 95c22e4d..956fa752 100644 --- a/src/strategy/mage/MageTriggers.cpp +++ b/src/strategy/mage/MageTriggers.cpp @@ -33,6 +33,17 @@ bool FingersOfFrostSingleTrigger::IsActive() return (aura && aura->GetCharges() == 1); } +bool ArcaneBlastStackTrigger::IsActive() +{ + Aura* aura = botAI->GetAura(getName(), GetTarget(), false, true, 3); + if (!aura) + return false; + if (aura->GetStackAmount() >= 4) + return true; + bool hasMissileBarrage = botAI->HasAura(44401, bot); + return hasMissileBarrage; +} + bool FrostNovaOnTargetTrigger::IsActive() { Unit* target = GetTarget(); @@ -52,3 +63,24 @@ bool FrostbiteOnTargetTrigger::IsActive() } return botAI->HasAura(spell, target); } + +bool NoFocusMagicTrigger::IsActive() +{ + if (!bot->HasSpell(54646)) + return false; + + Group* group = bot->GetGroup(); + if (!group) + return false; + + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || member == bot || !member->IsAlive()) + continue; + + if (member->HasAura(54646, bot->GetGUID())) + return false; + } + return true; +} diff --git a/src/strategy/mage/MageTriggers.h b/src/strategy/mage/MageTriggers.h index dd531872..59cb0fee 100644 --- a/src/strategy/mage/MageTriggers.h +++ b/src/strategy/mage/MageTriggers.h @@ -171,6 +171,7 @@ class ArcaneBlastStackTrigger : public HasAuraStackTrigger { public: ArcaneBlastStackTrigger(PlayerbotAI* botAI) : HasAuraStackTrigger(botAI, "arcane blast", 3, 1) {} + bool IsActive() override; }; class MirrorImageTrigger : public BoostTrigger @@ -193,4 +194,11 @@ public: bool IsActive() override; }; +class NoFocusMagicTrigger : public Trigger +{ +public: + NoFocusMagicTrigger(PlayerbotAI* botAI) : Trigger(botAI, "no focus magic") {} + bool IsActive() override; +}; + #endif diff --git a/src/strategy/priest/ShadowPriestStrategy.cpp b/src/strategy/priest/ShadowPriestStrategy.cpp index e2433c7d..b84c639a 100644 --- a/src/strategy/priest/ShadowPriestStrategy.cpp +++ b/src/strategy/priest/ShadowPriestStrategy.cpp @@ -15,10 +15,10 @@ ShadowPriestStrategy::ShadowPriestStrategy(PlayerbotAI* botAI) : GenericPriestSt NextAction** ShadowPriestStrategy::getDefaultActions() { - return NextAction::array(0, new NextAction("mind blast", ACTION_DEFAULT + 0.2f), - // new NextAction("shadow word: death", 12.0f), - new NextAction("mind flay", ACTION_DEFAULT + 0.1f), - new NextAction("shoot", ACTION_DEFAULT), NULL); + return NextAction::array(0, new NextAction("mind blast", ACTION_DEFAULT + 0.3f), + new NextAction("mind flay", ACTION_DEFAULT + 0.2f), + new NextAction("shadow word: death", ACTION_DEFAULT + 0.1f), // cast during movement + new NextAction("shoot", ACTION_DEFAULT), nullptr); } void ShadowPriestStrategy::InitTriggers(std::vector& triggers) diff --git a/src/strategy/shaman/CasterShamanStrategy.cpp b/src/strategy/shaman/CasterShamanStrategy.cpp index c11c523d..50babc94 100644 --- a/src/strategy/shaman/CasterShamanStrategy.cpp +++ b/src/strategy/shaman/CasterShamanStrategy.cpp @@ -41,7 +41,9 @@ CasterShamanStrategy::CasterShamanStrategy(PlayerbotAI* botAI) : GenericShamanSt NextAction** CasterShamanStrategy::getDefaultActions() { return NextAction::array(0, new NextAction("lava burst", ACTION_DEFAULT + 0.2f), - new NextAction("lightning bolt", ACTION_DEFAULT), NULL); + new NextAction("lightning bolt", ACTION_DEFAULT + 0.1f), + // new NextAction("earth shock", ACTION_DEFAULT), // cast during movement + nullptr); } void CasterShamanStrategy::InitTriggers(std::vector& triggers) diff --git a/src/strategy/shaman/ShamanTriggers.cpp b/src/strategy/shaman/ShamanTriggers.cpp index 5f74e901..e8c569f3 100644 --- a/src/strategy/shaman/ShamanTriggers.cpp +++ b/src/strategy/shaman/ShamanTriggers.cpp @@ -5,6 +5,7 @@ #include "ShamanTriggers.h" +#include "ItemTemplate.h" #include "Playerbots.h" /* @@ -46,8 +47,12 @@ bool MainHandWeaponNoImbueTrigger::IsActive() bool OffHandWeaponNoImbueTrigger::IsActive() { Item* const itemForSpell = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); - if (!itemForSpell || itemForSpell->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT) || - itemForSpell->GetTemplate()->InventoryType != INVTYPE_WEAPON) + if (!itemForSpell) + return false; + uint32 invType = itemForSpell->GetTemplate()->InventoryType; + bool allowedType = (invType == INVTYPE_WEAPON) || (invType == INVTYPE_WEAPONOFFHAND); + if (itemForSpell->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT) || + !allowedType) return false; return true; } diff --git a/src/strategy/shaman/ShamanTriggers.h b/src/strategy/shaman/ShamanTriggers.h index 2ab651a1..94102e7b 100644 --- a/src/strategy/shaman/ShamanTriggers.h +++ b/src/strategy/shaman/ShamanTriggers.h @@ -302,7 +302,7 @@ public: class FlameShockTrigger : public DebuffTrigger { public: - FlameShockTrigger(PlayerbotAI* ai) : DebuffTrigger(ai, "flame shock", 1, true) {} + FlameShockTrigger(PlayerbotAI* ai) : DebuffTrigger(ai, "flame shock", 1, true, 6.0f) {} }; class WrathOfAirTotemTrigger : public TotemTrigger diff --git a/src/strategy/triggers/GenericTriggers.h b/src/strategy/triggers/GenericTriggers.h index f93cb156..ac25dfc4 100644 --- a/src/strategy/triggers/GenericTriggers.h +++ b/src/strategy/triggers/GenericTriggers.h @@ -618,8 +618,8 @@ public: { } - std::string const GetTargetName() { return "self target"; } - virtual bool IsActive(); + std::string const GetTargetName() override { return "self target"; } + bool IsActive() override; private: int stack; diff --git a/src/strategy/values/EstimatedLifetimeValue.cpp b/src/strategy/values/EstimatedLifetimeValue.cpp index 6ab58f83..1c19e310 100644 --- a/src/strategy/values/EstimatedLifetimeValue.cpp +++ b/src/strategy/values/EstimatedLifetimeValue.cpp @@ -36,7 +36,7 @@ float EstimatedGroupDpsValue::Calculate() if (member == bot) // calculated continue; - if (!member || !member->IsInWorld()) + if (!member || !member->IsInWorld() || !member->IsAlive()) continue; if (member->GetMapId() != bot->GetMapId()) diff --git a/src/strategy/warlock/DpsWarlockStrategy.cpp b/src/strategy/warlock/DpsWarlockStrategy.cpp index e9b7fea5..18133846 100644 --- a/src/strategy/warlock/DpsWarlockStrategy.cpp +++ b/src/strategy/warlock/DpsWarlockStrategy.cpp @@ -49,8 +49,8 @@ DpsWarlockStrategy::DpsWarlockStrategy(PlayerbotAI* botAI) : GenericWarlockStrat NextAction** DpsWarlockStrategy::getDefaultActions() { return NextAction::array( - 0, new NextAction("haunt", ACTION_DEFAULT + 0.3f), new NextAction("demonic empowerment", ACTION_DEFAULT + 0.2f), - new NextAction("shadow bolt", ACTION_DEFAULT + 0.1f), new NextAction("shoot", ACTION_DEFAULT), nullptr); + 0, new NextAction("haunt", ACTION_DEFAULT + 0.4f), new NextAction("demonic empowerment", ACTION_DEFAULT + 0.3f), + new NextAction("shadow bolt", ACTION_DEFAULT + 0.2f), new NextAction("shoot", ACTION_DEFAULT), nullptr); } void DpsWarlockStrategy::InitTriggers(std::vector& triggers) @@ -71,6 +71,12 @@ void DpsWarlockStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode("decimation", NextAction::array(0, new NextAction("soul fire", 16.0f), NULL))); + // cast during movement + triggers.push_back( + new TriggerNode("high mana", NextAction::array(0, new NextAction("life tap", ACTION_DEFAULT + 0.1f), nullptr))); + + triggers.push_back(new TriggerNode("life tap glyph buff", NextAction::array(0, new NextAction("life tap", 28.0f), NULL))); + triggers.push_back( new TriggerNode("metamorphosis", NextAction::array(0, new NextAction("metamorphosis", 20.0f), NULL))); } @@ -78,9 +84,9 @@ void DpsWarlockStrategy::InitTriggers(std::vector& triggers) void DpsAoeWarlockStrategy::InitTriggers(std::vector& triggers) { triggers.push_back( - new TriggerNode("medium aoe", NextAction::array(0, new NextAction("seed of corruption", 39.0f), - new NextAction("seed of corruption on attacker", 38.0f), - new NextAction("rain of fire", 37.0f), nullptr))); + new TriggerNode("medium aoe", NextAction::array(0, new NextAction("seed of corruption", 33.0f), + new NextAction("seed of corruption on attacker", 32.0f), + new NextAction("rain of fire", 31.0f), nullptr))); triggers.push_back(new TriggerNode("corruption on attacker", NextAction::array(0, new NextAction("corruption on attacker", 27.0f), nullptr))); triggers.push_back( diff --git a/src/strategy/warlock/WarlockActions.cpp b/src/strategy/warlock/WarlockActions.cpp index 627b042a..6be8826b 100644 --- a/src/strategy/warlock/WarlockActions.cpp +++ b/src/strategy/warlock/WarlockActions.cpp @@ -18,7 +18,7 @@ Value* CastFearOnCcAction::GetTargetValue() { return context->GetValueCastSpell("fear", GetTarget()); } -bool CastFearOnCcAction::isPossible() { return botAI->CanCastSpell("fear", GetTarget()); } +bool CastFearOnCcAction::isPossible() { return true; } bool CastFearOnCcAction::isUseful() { return true; } diff --git a/src/strategy/warlock/WarlockAiObjectContext.cpp b/src/strategy/warlock/WarlockAiObjectContext.cpp index 909e874d..74dde632 100644 --- a/src/strategy/warlock/WarlockAiObjectContext.cpp +++ b/src/strategy/warlock/WarlockAiObjectContext.cpp @@ -6,6 +6,7 @@ #include "WarlockAiObjectContext.h" #include "DpsWarlockStrategy.h" +#include "GenericTriggers.h" #include "GenericWarlockNonCombatStrategy.h" #include "NamedObjectContext.h" #include "Playerbots.h" @@ -98,6 +99,7 @@ public: creators["unstable affliction on attacker"] = &WarlockTriggerFactoryInternal::unstable_affliction_on_attacker; creators["haunt"] = &WarlockTriggerFactoryInternal::haunt; creators["decimation"] = &WarlockTriggerFactoryInternal::decimation; + creators["life tap glyph buff"] = &WarlockTriggerFactoryInternal::life_tap_glyph_buff; creators["molten core"] = &WarlockTriggerFactoryInternal::molten_core; creators["metamorphosis"] = &WarlockTriggerFactoryInternal::metamorphosis; } @@ -131,6 +133,7 @@ private: } static Trigger* haunt(PlayerbotAI* ai) { return new HauntTrigger(ai); } static Trigger* decimation(PlayerbotAI* ai) { return new DecimationTrigger(ai); } + static Trigger* life_tap_glyph_buff(PlayerbotAI* ai) { return new LifeTapGlyphBuffTrigger(ai); } static Trigger* molten_core(PlayerbotAI* ai) { return new MoltenCoreTrigger(ai); } static Trigger* metamorphosis(PlayerbotAI* ai) { return new MetamorphosisTrigger(ai); } }; diff --git a/src/strategy/warlock/WarlockTriggers.cpp b/src/strategy/warlock/WarlockTriggers.cpp index 6192dfc7..1ba37ca2 100644 --- a/src/strategy/warlock/WarlockTriggers.cpp +++ b/src/strategy/warlock/WarlockTriggers.cpp @@ -5,6 +5,7 @@ #include "WarlockTriggers.h" +#include "GenericTriggers.h" #include "Playerbots.h" bool DemonArmorTrigger::IsActive() @@ -44,4 +45,13 @@ bool DecimationTrigger::IsActive() { Aura* aura = botAI->GetAura(getName(), GetTarget(), false, true); return aura && aura->GetDuration() > 3000; +} + +bool LifeTapGlyphBuffTrigger::IsActive() +{ + // Check life tap glyph first + if (!botAI->HasAura(63320, bot)) + return false; + + return BuffTrigger::IsActive(); } \ No newline at end of file diff --git a/src/strategy/warlock/WarlockTriggers.h b/src/strategy/warlock/WarlockTriggers.h index 38514d04..6c908ed7 100644 --- a/src/strategy/warlock/WarlockTriggers.h +++ b/src/strategy/warlock/WarlockTriggers.h @@ -163,6 +163,13 @@ public: bool IsActive() override; }; +class LifeTapGlyphBuffTrigger : public BuffTrigger +{ +public: + LifeTapGlyphBuffTrigger(PlayerbotAI* ai) : BuffTrigger(ai, "life tap") {} + bool IsActive() override; +}; + class MoltenCoreTrigger : public HasAuraTrigger { public: diff --git a/src/strategy/warrior/WarriorActions.cpp b/src/strategy/warrior/WarriorActions.cpp index a917afd2..a6eee7dc 100644 --- a/src/strategy/warrior/WarriorActions.cpp +++ b/src/strategy/warrior/WarriorActions.cpp @@ -13,12 +13,12 @@ bool CastSunderArmorAction::isUseful() return !aura || aura->GetStackAmount() < 5 || aura->GetDuration() <= 6000; } -Value* CastVigilanceAction::GetTargetValue() +Unit* CastVigilanceAction::GetTarget() { Group* group = bot->GetGroup(); if (!group) { - return new ManualSetValue(botAI, nullptr); + return nullptr; } Player* currentVigilanceTarget = nullptr; @@ -74,23 +74,23 @@ Value* CastVigilanceAction::GetTargetValue() // If no valid target, return nullptr if (!highestPriorityTarget) { - return new ManualSetValue(botAI, nullptr); + return nullptr; } // If the current target is already the highest-priority target, do nothing if (currentVigilanceTarget == highestPriorityTarget) { - return new ManualSetValue(botAI, nullptr); + return nullptr; } // Assign the new target Unit* targetUnit = highestPriorityTarget->ToUnit(); if (targetUnit) { - return new ManualSetValue(botAI, targetUnit); + return targetUnit; } - return new ManualSetValue(botAI, nullptr); + return nullptr; } bool CastVigilanceAction::Execute(Event event) diff --git a/src/strategy/warrior/WarriorActions.h b/src/strategy/warrior/WarriorActions.h index 5683589c..79929b45 100644 --- a/src/strategy/warrior/WarriorActions.h +++ b/src/strategy/warrior/WarriorActions.h @@ -140,7 +140,7 @@ class CastVigilanceAction : public BuffOnPartyAction public: CastVigilanceAction(PlayerbotAI* botAI) : BuffOnPartyAction(botAI, "vigilance") {} - Value* GetTargetValue() override; + Unit* GetTarget() override; bool Execute(Event event) override; };