From b59c87871f204e206cd9748495a672f5c8aee915 Mon Sep 17 00:00:00 2001 From: Bobblybook Date: Tue, 1 Oct 2024 18:27:06 +1000 Subject: [PATCH 01/40] Indentation and misc cleanup --- .../utgardekeep/UtgardeKeepMultipliers.cpp | 4 +- .../utgardekeep/UtgardeKeepMultipliers.h | 4 +- .../wotlk/utgardekeep/UtgardeKeepStrategy.cpp | 44 +++++++++---------- .../wotlk/utgardekeep/UtgardeKeepStrategy.h | 4 +- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepMultipliers.cpp b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepMultipliers.cpp index 7d785e4c..d46afd9b 100644 --- a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepMultipliers.cpp +++ b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepMultipliers.cpp @@ -6,7 +6,7 @@ float PrinceKelesethMultiplier::GetValue(Action* action) { - Unit* boss = AI_VALUE2(Unit*, "find target", "prince keleseth"); + Unit* boss = AI_VALUE2(Unit*, "find target", "prince keleseth"); if (!boss) { return 1.0f; @@ -15,7 +15,7 @@ float PrinceKelesethMultiplier::GetValue(Action* action) { return 0.0f; } - return 1.0f; + return 1.0f; } float SkarvaldAndDalronnMultiplier::GetValue(Action* action) { diff --git a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepMultipliers.h b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepMultipliers.h index e9e265be..0b42300e 100644 --- a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepMultipliers.h +++ b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepMultipliers.h @@ -1,5 +1,5 @@ -#ifndef _PLAYERRBOT_WOTLKDUNGEONUKMULTIPLIERS_H_ -#define _PLAYERRBOT_WOTLKDUNGEONUKMULTIPLIERS_H_ +#ifndef _PLAYERRBOT_WOTLKDUNGEONUKMULTIPLIERS_H +#define _PLAYERRBOT_WOTLKDUNGEONUKMULTIPLIERS_H #include "Multiplier.h" diff --git a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepStrategy.cpp b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepStrategy.cpp index c79b3dd3..cb3178ed 100644 --- a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepStrategy.cpp +++ b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepStrategy.cpp @@ -4,40 +4,40 @@ void WotlkDungeonUKStrategy::InitTriggers(std::vector &triggers) { - // Prince Keleseth - triggers.push_back(new TriggerNode("keleseth frost tomb", + // Prince Keleseth + triggers.push_back(new TriggerNode("keleseth frost tomb", NextAction::array(0, new NextAction("attack frost tomb", ACTION_RAID + 1), nullptr))); - - // Skarvald the Constructor & Dalronn the Controller - triggers.push_back(new TriggerNode("dalronn priority", + + // Skarvald the Constructor & Dalronn the Controller + triggers.push_back(new TriggerNode("dalronn priority", NextAction::array(0, new NextAction("attack dalronn", ACTION_RAID + 1), nullptr))); - - // Ingvar the Plunderer + + // Ingvar the Plunderer - // Doesn't work yet, this action doesn't get processed until the existing cast finishes - // triggers.push_back(new TriggerNode("ingvar staggering roar", + // Doesn't work yet, this action doesn't get processed until the existing cast finishes + // triggers.push_back(new TriggerNode("ingvar staggering roar", // NextAction::array(0, new NextAction("ingvar stop casting", ACTION_RAID + 1), nullptr))); - // No easy way to check LoS here, the pillars do not seem to count as gameobjects. - // Not implemented for now, unsure if this is needed as a good group can probably burst through the boss - // and just eat the debuff. - // triggers.push_back(new TriggerNode("ingvar dreadful roar", + // No easy way to check LoS here, the pillars do not seem to count as gameobjects. + // Not implemented for now, unsure if this is needed as a good group can probably burst through the boss + // and just eat the debuff. + // triggers.push_back(new TriggerNode("ingvar dreadful roar", // NextAction::array(0, new NextAction("ingvar hide los", ACTION_RAID + 1), nullptr))); - triggers.push_back(new TriggerNode("ingvar smash tank", + triggers.push_back(new TriggerNode("ingvar smash tank", NextAction::array(0, new NextAction("ingvar dodge smash", ACTION_MOVE + 5), nullptr))); - triggers.push_back(new TriggerNode("ingvar smash tank return", + triggers.push_back(new TriggerNode("ingvar smash tank return", NextAction::array(0, new NextAction("ingvar smash return", ACTION_MOVE + 5), nullptr))); - // Buggy... if not behind target, ai can get stuck running towards and away from target. - // I think for ranged chars, a custom action should be added that doesn't attempt to run into melee. - // This is a bandaid for now, needs to be improved. - triggers.push_back(new TriggerNode("not behind ingvar", + // Buggy... if not behind target, ai can get stuck running towards and away from target. + // I think for ranged chars, a custom action should be added that doesn't attempt to run into melee. + // This is a bandaid for now, needs to be improved. + triggers.push_back(new TriggerNode("not behind ingvar", NextAction::array(0, new NextAction("set behind", ACTION_MOVE + 1), nullptr))); } void WotlkDungeonUKStrategy::InitMultipliers(std::vector &multipliers) { - multipliers.push_back(new PrinceKelesethMultiplier(botAI)); - multipliers.push_back(new SkarvaldAndDalronnMultiplier(botAI)); - multipliers.push_back(new IngvarThePlundererMultiplier(botAI)); + multipliers.push_back(new PrinceKelesethMultiplier(botAI)); + multipliers.push_back(new SkarvaldAndDalronnMultiplier(botAI)); + multipliers.push_back(new IngvarThePlundererMultiplier(botAI)); } diff --git a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepStrategy.h b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepStrategy.h index 90873feb..f4c21aa5 100644 --- a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepStrategy.h +++ b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepStrategy.h @@ -1,5 +1,5 @@ -#ifndef _PLAYERBOT_WOTLKDUNGEONUKSTRATEGY_H -#define _PLAYERBOT_WOTLKDUNGEONUKSTRATEGY_H +#ifndef _PLAYERBOT_WOTLKDUNGEONUKSTRATEGY_H_ +#define _PLAYERBOT_WOTLKDUNGEONUKSTRATEGY_H_ #include "Multiplier.h" #include "AiObjectContext.h" From 32c0f31031993e41d85eeae1af34d09baa87cc46 Mon Sep 17 00:00:00 2001 From: Bobblybook Date: Tue, 1 Oct 2024 18:28:06 +1000 Subject: [PATCH 02/40] Update UtgardeKeepTriggers.h --- src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepTriggers.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepTriggers.h b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepTriggers.h index e59ad8ca..0f0903be 100644 --- a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepTriggers.h +++ b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepTriggers.h @@ -9,7 +9,7 @@ // Taken from: // src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_ingvar_the_plunderer.cpp -enum eSpells +enum Spells { SPELL_SUMMON_VALKYR = 42912, SPELL_RESURRECTION_BEAM = 42857, From 8ec71d2b179f4f62fd50230e7e3e9598861b8661 Mon Sep 17 00:00:00 2001 From: Bobblybook Date: Tue, 1 Oct 2024 18:36:11 +1000 Subject: [PATCH 03/40] header defs --- .../dungeons/DungeonStrategyContext.h | 4 +- src/strategy/dungeons/DungeonStrategyUtils.h | 4 +- .../wotlk/WotlkDungeonActionContext.h | 4 +- .../wotlk/WotlkDungeonTriggerContext.h | 4 +- .../dungeons/wotlk/nexus/NexusTriggers.h | 94 +++++++++++++++++++ src/strategy/dungeons/wotlk/nexus/TODO | 0 6 files changed, 102 insertions(+), 8 deletions(-) create mode 100644 src/strategy/dungeons/wotlk/nexus/NexusTriggers.h delete mode 100644 src/strategy/dungeons/wotlk/nexus/TODO diff --git a/src/strategy/dungeons/DungeonStrategyContext.h b/src/strategy/dungeons/DungeonStrategyContext.h index f1d8e18f..4715de4e 100644 --- a/src/strategy/dungeons/DungeonStrategyContext.h +++ b/src/strategy/dungeons/DungeonStrategyContext.h @@ -1,5 +1,5 @@ -#ifndef _PLAYERBOT_DUNGEONSTRATEGYCONTEXT_H_ -#define _PLAYERBOT_DUNGEONSTRATEGYCONTEXT_H_ +#ifndef _PLAYERBOT_DUNGEONSTRATEGYCONTEXT_H +#define _PLAYERBOT_DUNGEONSTRATEGYCONTEXT_H #include "Strategy.h" #include "wotlk/utgardekeep/UtgardeKeepStrategy.h" diff --git a/src/strategy/dungeons/DungeonStrategyUtils.h b/src/strategy/dungeons/DungeonStrategyUtils.h index 6c014aba..2b738360 100644 --- a/src/strategy/dungeons/DungeonStrategyUtils.h +++ b/src/strategy/dungeons/DungeonStrategyUtils.h @@ -1,5 +1,5 @@ -#ifndef _PLAYERBOT_DUNGEONUTILS_H_ -#define _PLAYERBOT_DUNGEONUTILS_H_ +#ifndef _PLAYERBOT_DUNGEONUTILS_H +#define _PLAYERBOT_DUNGEONUTILS_H template inline diff --git a/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h b/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h index ea13c9b0..419229dd 100644 --- a/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h +++ b/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h @@ -1,5 +1,5 @@ -#ifndef _PLAYERBOT_WOTLKDUNGEONACTIONCONTEXT_H_ -#define _PLAYERBOT_WOTLKDUNGEONACTIONCONTEXT_H_ +#ifndef _PLAYERBOT_WOTLKDUNGEONACTIONCONTEXT_H +#define _PLAYERBOT_WOTLKDUNGEONACTIONCONTEXT_H #include "utgardekeep/UtgardeKeepActionContext.h" // #include "nexus/NexusActionContext.h" diff --git a/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h b/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h index 12654700..28a42d87 100644 --- a/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h +++ b/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h @@ -1,5 +1,5 @@ -#ifndef _PLAYERBOT_WOTLKDUNGEONTRIGGERCONTEXT_H_ -#define _PLAYERBOT_WOTLKDUNGEONTRIGGERCONTEXT_H_ +#ifndef _PLAYERBOT_WOTLKDUNGEONTRIGGERCONTEXT_H +#define _PLAYERBOT_WOTLKDUNGEONTRIGGERCONTEXT_H #include "utgardekeep/UtgardeKeepTriggerContext.h" // #include "nexus/NexusTriggerContext.h" diff --git a/src/strategy/dungeons/wotlk/nexus/NexusTriggers.h b/src/strategy/dungeons/wotlk/nexus/NexusTriggers.h new file mode 100644 index 00000000..894b86f7 --- /dev/null +++ b/src/strategy/dungeons/wotlk/nexus/NexusTriggers.h @@ -0,0 +1,94 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONNEXTRIGGERS_H +#define _PLAYERBOT_WOTLKDUNGEONNEXTRIGGERS_H + +#include "EventMap.h" +#include "Trigger.h" +#include "PlayerbotAIConfig.h" +#include "GenericTriggers.h" +#include "DungeonStrategyUtils.h" + +// Taken from: +// src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_ingvar_the_plunderer.cpp +enum Spells +{ + SPELL_SUMMON_VALKYR = 42912, + SPELL_RESURRECTION_BEAM = 42857, + SPELL_RESURRECTION_BALL = 42862, + SPELL_RESURRECTION_HEAL = 42704, + SPELL_INGVAR_TRANSFORM = 42796, + + SPELL_STAGGERING_ROAR_N = 42708, + SPELL_STAGGERING_ROAR_H = 59708, + SPELL_CLEAVE = 42724, + SPELL_SMASH_N = 42669, + SPELL_SMASH_H = 59706, + SPELL_ENRAGE_N = 42705, + SPELL_ENRAGE_H = 59707, + + SPELL_DREADFUL_ROAR_N = 42729, + SPELL_DREADFUL_ROAR_H = 59734, + SPELL_WOE_STRIKE_N = 42730, + SPELL_WOE_STRIKE_H = 59735, + SPELL_DARK_SMASH = 42723, + SPELL_SHADOW_AXE = 42749, + + // Added + DEBUFF_FROST_TOMB = 48400, +}; + +#define SPELL_STAGGERING_ROAR DUNGEON_MODE(bot, SPELL_STAGGERING_ROAR_N, SPELL_STAGGERING_ROAR_H) +#define SPELL_DREADFUL_ROAR DUNGEON_MODE(bot, SPELL_DREADFUL_ROAR_N, SPELL_DREADFUL_ROAR_H) +#define SPELL_WOE_STRIKE DUNGEON_MODE(bot, SPELL_WOE_STRIKE_N, SPELL_WOE_STRIKE_H) +#define SPELL_SMASH DUNGEON_MODE(bot, SPELL_SMASH_N, SPELL_SMASH_H) +#define SPELL_ENRAGE DUNGEON_MODE(bot, SPELL_ENRAGE_N, SPELL_ENRAGE_H) + +class KelesethFrostTombTrigger : public Trigger +{ +public: + KelesethFrostTombTrigger(PlayerbotAI* ai) : Trigger(ai, "keleseth frost tomb") {} + bool IsActive() override; +}; + +class DalronnNontankTrigger : public Trigger +{ +public: + DalronnNontankTrigger(PlayerbotAI* ai) : Trigger(ai, "dalronn non-tank") {} + bool IsActive() override; +}; + +class IngvarStaggeringRoarTrigger : public Trigger +{ +public: + IngvarStaggeringRoarTrigger(PlayerbotAI* ai) : Trigger(ai, "ingvar staggering roar") {} + bool IsActive() override; +}; + +class IngvarDreadfulRoarTrigger : public Trigger +{ +public: + IngvarDreadfulRoarTrigger(PlayerbotAI* ai) : Trigger(ai, "ingvar dreadful roar") {} + bool IsActive() override; +}; + +class IngvarSmashTankTrigger : public Trigger +{ +public: + IngvarSmashTankTrigger(PlayerbotAI* ai) : Trigger(ai, "ingvar smash tank") {} + bool IsActive() override; +}; + +class IngvarSmashTankReturnTrigger : public Trigger +{ +public: + IngvarSmashTankReturnTrigger(PlayerbotAI* ai) : Trigger(ai, "ingvar smash tank return") {} + bool IsActive() override; +}; + +class NotBehindIngvarTrigger : public Trigger +{ +public: + NotBehindIngvarTrigger(PlayerbotAI* ai) : Trigger(ai, "not behind ingvar") {} + bool IsActive() override; +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/nexus/TODO b/src/strategy/dungeons/wotlk/nexus/TODO deleted file mode 100644 index e69de29b..00000000 From 008d098eda4d475ca75c2b042ec32a7fae72ab76 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Thu, 3 Oct 2024 22:35:26 +0800 Subject: [PATCH 04/40] [Assist Dps] Healer assist dps strats --- conf/playerbots.conf.dist | 8 ++-- src/AiFactory.cpp | 11 ++--- src/PlayerbotAI.cpp | 36 +++++++---------- src/strategy/druid/CasterDruidStrategy.cpp | 3 -- src/strategy/druid/DruidAiObjectContext.cpp | 3 ++ src/strategy/druid/GenericDruidStrategy.cpp | 15 +++++++ src/strategy/druid/GenericDruidStrategy.h | 9 +++++ src/strategy/druid/HealDruidStrategy.cpp | 40 +++++++++++++++---- .../paladin/GenericPaladinStrategy.cpp | 13 ++++++ src/strategy/paladin/GenericPaladinStrategy.h | 9 +++++ .../GenericPaladinStrategyActionNodeFactory.h | 16 ++++---- src/strategy/paladin/HealPaladinStrategy.cpp | 2 +- .../paladin/PaladinAiObjectContext.cpp | 2 + src/strategy/paladin/TankPaladinStrategy.cpp | 2 +- src/strategy/priest/GenericPriestStrategy.cpp | 20 ++++++++++ src/strategy/priest/GenericPriestStrategy.h | 9 +++++ src/strategy/priest/PriestActions.h | 8 +++- src/strategy/priest/PriestAiObjectContext.cpp | 3 ++ .../priest/PriestNonCombatStrategy.cpp | 2 +- src/strategy/shaman/CasterShamanStrategy.cpp | 4 +- src/strategy/shaman/GenericShamanStrategy.cpp | 16 ++++++++ src/strategy/shaman/GenericShamanStrategy.h | 9 +++++ src/strategy/shaman/ShamanActions.h | 2 +- src/strategy/shaman/ShamanAiObjectContext.cpp | 3 ++ .../shaman/ShamanNonCombatStrategy.cpp | 2 +- src/strategy/triggers/GenericTriggers.cpp | 30 +++++++++++++- src/strategy/triggers/GenericTriggers.h | 10 +++++ src/strategy/triggers/HealthTriggers.cpp | 17 ++++++-- src/strategy/triggers/HealthTriggers.h | 5 +-- src/strategy/triggers/TriggerContext.h | 10 ++++- src/strategy/values/AttackerCountValues.cpp | 2 +- 31 files changed, 253 insertions(+), 68 deletions(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index d7d02059..f158327a 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -1002,16 +1002,16 @@ AiPlayerbot.PremadeSpecLink.9.2.80 = -03310030003-05203205210331051335230351 AiPlayerbot.PremadeSpecName.11.0 = balance pve AiPlayerbot.PremadeSpecGlyph.11.0 = 40916,43331,40921,43335,44922,40919 -AiPlayerbot.PremadeSpecLink.11.0.60 = 5012203115331003213302301231 -AiPlayerbot.PremadeSpecLink.11.0.80 = 5012203125331103213305301231--205003212 +AiPlayerbot.PremadeSpecLink.11.0.60 = 5022203105331003213005301231 +AiPlayerbot.PremadeSpecLink.11.0.80 = 5032203105331303213305301231--205003012 AiPlayerbot.PremadeSpecName.11.1 = bear pve AiPlayerbot.PremadeSpecGlyph.11.1 = 40897,43331,46372,43335,43332,40899 AiPlayerbot.PremadeSpecLink.11.1.60 = -500232130322110353100301310501 AiPlayerbot.PremadeSpecLink.11.1.80 = -501232130322110353120303313511-20350001 AiPlayerbot.PremadeSpecName.11.2 = resto pve AiPlayerbot.PremadeSpecGlyph.11.2 = 40913,43331,40906,43335,44922,45602 -AiPlayerbot.PremadeSpecLink.11.2.60 = --230033312031501531050013051 -AiPlayerbot.PremadeSpecLink.11.2.80 = 05320001--230033312031512531153313051 +AiPlayerbot.PremadeSpecLink.11.2.60 = --230033312031500531050113051 +AiPlayerbot.PremadeSpecLink.11.2.80 = 05320031--230033312031501531053313051 AiPlayerbot.PremadeSpecName.11.3 = cat pve AiPlayerbot.PremadeSpecGlyph.11.3 = 40902,43331,40901,43335,44922,45604 AiPlayerbot.PremadeSpecLink.11.3.60 = -552202032322010053100030310501 diff --git a/src/AiFactory.cpp b/src/AiFactory.cpp index dadf327f..ce3fc272 100644 --- a/src/AiFactory.cpp +++ b/src/AiFactory.cpp @@ -277,10 +277,6 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa { engine->addStrategiesNoInit("racials", "chat", "default", "cast time", "duel", "boost", nullptr); } - if (sPlayerbotAIConfig->autoSaveMana) - { - engine->addStrategy("save mana", false); - } if (sPlayerbotAIConfig->autoAvoidAoe && facade->HasRealPlayerMaster()) { engine->addStrategy("avoid aoe", false); @@ -394,7 +390,12 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa if (PlayerbotAI::IsMelee(player, true) && PlayerbotAI::IsDps(player, true)) { engine->addStrategy("behind", false); } - + if (PlayerbotAI::IsHeal(player, true)) + { + if (sPlayerbotAIConfig->autoSaveMana) + engine->addStrategy("save mana", false); + engine->addStrategy("assist dps", false); + } if (facade->IsRealPlayer() || sRandomPlayerbotMgr->IsRandomBot(player)) { if (!player->GetGroup()) diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 4f79196f..7b6b9887 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -3339,11 +3339,11 @@ bool PlayerbotAI::CastSpell(uint32 spellId, Unit* target, Item* itemTarget) // } // } - WaitForSpellCast(spell); - if (spell->GetCastTime()) - aiObjectContext->GetValue("last spell cast") - ->Get() - .Set(spellId, target->GetGUID(), time(nullptr)); + // WaitForSpellCast(spell); + + aiObjectContext->GetValue("last spell cast") + ->Get() + .Set(spellId, target->GetGUID(), time(nullptr)); aiObjectContext->GetValue("position")->Get()["random"].Reset(); @@ -3473,7 +3473,7 @@ bool PlayerbotAI::CastSpell(uint32 spellId, float x, float y, float z, Item* ite } } - WaitForSpellCast(spell); + // WaitForSpellCast(spell); aiObjectContext->GetValue("last spell cast")->Get().Set(spellId, bot->GetGUID(), time(nullptr)); aiObjectContext->GetValue("position")->Get()["random"].Reset(); @@ -3688,7 +3688,7 @@ bool PlayerbotAI::CastVehicleSpell(uint32 spellId, Unit* target) return false; } - WaitForSpellCast(spell); + // WaitForSpellCast(spell); // aiObjectContext->GetValue("last spell cast")->Get().Set(spellId, target->GetGUID(), time(0)); // aiObjectContext->GetValue("position")->Get()["random"].Reset(); @@ -3745,23 +3745,15 @@ bool PlayerbotAI::IsInVehicle(bool canControl, bool canCast, bool canAttack, boo void PlayerbotAI::WaitForSpellCast(Spell* spell) { - return; SpellInfo const* spellInfo = spell->GetSpellInfo(); uint32 castTime = spell->GetCastTime(); - // float castTime = spell->GetCastTime(); - // if (spellInfo->IsChanneled()) - // { - // int32 duration = spellInfo->GetDuration(); - // bot->ApplySpellMod(spellInfo->Id, SPELLMOD_DURATION, duration); - // if (duration > 0) - // castTime += duration; - // } - - // castTime = ceil(castTime); - - // uint32 globalCooldown = CalculateGlobalCooldown(spellInfo->Id); - // if (castTime < globalCooldown) - // castTime = globalCooldown; + if (spellInfo->IsChanneled()) + { + int32 duration = spellInfo->GetDuration(); + bot->ApplySpellMod(spellInfo->Id, SPELLMOD_DURATION, duration); + if (duration > 0) + castTime += duration; + } SetNextCheckDelay(castTime + sPlayerbotAIConfig->reactDelay); } diff --git a/src/strategy/druid/CasterDruidStrategy.cpp b/src/strategy/druid/CasterDruidStrategy.cpp index 1dfeadfa..61e7ab3d 100644 --- a/src/strategy/druid/CasterDruidStrategy.cpp +++ b/src/strategy/druid/CasterDruidStrategy.cpp @@ -145,9 +145,6 @@ void CasterDruidStrategy::InitTriggers(std::vector& triggers) new TriggerNode("medium mana", NextAction::array(0, new NextAction("innervate", ACTION_HIGH + 9), NULL))); triggers.push_back(new TriggerNode("enemy too close for spell", NextAction::array(0, new NextAction("flee", ACTION_MOVE + 9), nullptr))); - triggers.push_back( - new TriggerNode("party member remove curse", - NextAction::array(0, new NextAction("remove curse on party", ACTION_DISPEL + 7), NULL))); } void CasterDruidAoeStrategy::InitTriggers(std::vector& triggers) diff --git a/src/strategy/druid/DruidAiObjectContext.cpp b/src/strategy/druid/DruidAiObjectContext.cpp index 04652487..fabd491b 100644 --- a/src/strategy/druid/DruidAiObjectContext.cpp +++ b/src/strategy/druid/DruidAiObjectContext.cpp @@ -14,6 +14,7 @@ #include "DruidShapeshiftActions.h" #include "DruidTriggers.h" #include "GenericDruidNonCombatStrategy.h" +#include "GenericDruidStrategy.h" #include "HealDruidStrategy.h" #include "MeleeDruidStrategy.h" #include "Playerbots.h" @@ -33,6 +34,7 @@ public: creators["buff"] = &DruidStrategyFactoryInternal::buff; creators["boost"] = &DruidStrategyFactoryInternal::boost; creators["cc"] = &DruidStrategyFactoryInternal::cc; + creators["assist dps"] = &DruidStrategyFactoryInternal::assist_dps; } private: @@ -45,6 +47,7 @@ private: static Strategy* buff(PlayerbotAI* botAI) { return new GenericDruidBuffStrategy(botAI); } static Strategy* boost(PlayerbotAI* botAI) { return new DruidBoostStrategy(botAI); } static Strategy* cc(PlayerbotAI* botAI) { return new DruidCcStrategy(botAI); } + static Strategy* assist_dps(PlayerbotAI* botAI) { return new DruidAssistDpsStrategy(botAI); } }; class DruidDruidStrategyFactoryInternal : public NamedObjectContext diff --git a/src/strategy/druid/GenericDruidStrategy.cpp b/src/strategy/druid/GenericDruidStrategy.cpp index c44668f7..ccd3be5e 100644 --- a/src/strategy/druid/GenericDruidStrategy.cpp +++ b/src/strategy/druid/GenericDruidStrategy.cpp @@ -130,6 +130,11 @@ void DruidCureStrategy::InitTriggers(std::vector& triggers) triggers.push_back( new TriggerNode("party member cure poison", NextAction::array(0, new NextAction("abolish poison on party", ACTION_DISPEL + 1), nullptr))); + + triggers.push_back( + new TriggerNode("party member remove curse", + NextAction::array(0, new NextAction("remove curse on party", ACTION_DISPEL + 7), NULL))); + } void DruidBoostStrategy::InitTriggers(std::vector& triggers) @@ -147,3 +152,13 @@ void DruidCcStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode( "hibernate", NextAction::array(0, new NextAction("hibernate on cc", ACTION_HIGH + 3), nullptr))); } + +void DruidAssistDpsStrategy::InitTriggers(std::vector& triggers) +{ + triggers.push_back( + new TriggerNode("healer should attack", + NextAction::array(0, + new NextAction("moonfire", ACTION_DEFAULT + 0.2f), + new NextAction("wrath", ACTION_DEFAULT + 0.1f), + nullptr))); +} diff --git a/src/strategy/druid/GenericDruidStrategy.h b/src/strategy/druid/GenericDruidStrategy.h index db6631b2..6099527e 100644 --- a/src/strategy/druid/GenericDruidStrategy.h +++ b/src/strategy/druid/GenericDruidStrategy.h @@ -46,4 +46,13 @@ public: std::string const getName() override { return "cc"; } }; +class DruidAssistDpsStrategy : public Strategy +{ +public: + DruidAssistDpsStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} + + void InitTriggers(std::vector& triggers) override; + std::string const getName() override { return "assist dps"; } +}; + #endif diff --git a/src/strategy/druid/HealDruidStrategy.cpp b/src/strategy/druid/HealDruidStrategy.cpp index 0da13563..8fb17f2c 100644 --- a/src/strategy/druid/HealDruidStrategy.cpp +++ b/src/strategy/druid/HealDruidStrategy.cpp @@ -10,7 +10,12 @@ class HealDruidStrategyActionNodeFactory : public NamedObjectFactory { public: - HealDruidStrategyActionNodeFactory() { creators["nourish on party"] = &nourtish_on_party; } + HealDruidStrategyActionNodeFactory() { + creators["nourish on party"] = &nourtish_on_party; + creators["wild growth on party"] = &wild_growth_on_party; + creators["rejuvenation on party"] = &rejuvenation_on_party; + creators["regrowth on party"] = ®rowth_on_party; + } private: static ActionNode* nourtish_on_party([[maybe_unused]] PlayerbotAI* botAI) @@ -20,6 +25,27 @@ private: /*A*/ NextAction::array(0, new NextAction("healing touch on party"), nullptr), /*C*/ nullptr); } + static ActionNode* wild_growth_on_party([[maybe_unused]] PlayerbotAI* botAI) + { + return new ActionNode("wild growth on party", + /*P*/ NextAction::array(0, new NextAction("tree form"), nullptr), + /*A*/ nullptr, + /*C*/ nullptr); + } + static ActionNode* rejuvenation_on_party([[maybe_unused]] PlayerbotAI* botAI) + { + return new ActionNode("rejuvenation on party", + /*P*/ NextAction::array(0, new NextAction("tree form"), nullptr), + /*A*/ nullptr, + /*C*/ nullptr); + } + static ActionNode* regrowth_on_party([[maybe_unused]] PlayerbotAI* botAI) + { + return new ActionNode("regrowth on party", + /*P*/ NextAction::array(0, new NextAction("tree form"), nullptr), + /*A*/ nullptr, + /*C*/ nullptr); + } }; HealDruidStrategy::HealDruidStrategy(PlayerbotAI* botAI) : GenericDruidStrategy(botAI) @@ -33,19 +59,18 @@ void HealDruidStrategy::InitTriggers(std::vector& triggers) // triggers.push_back(new TriggerNode("enemy out of spell", NextAction::array(0, new NextAction("reach spell", // ACTION_NORMAL + 9), nullptr))); - triggers.push_back( - new TriggerNode("tree form", NextAction::array(0, new NextAction("tree form", ACTION_HIGH + 1), nullptr))); + // triggers.push_back( + // new TriggerNode("tree form", NextAction::array(0, new NextAction("tree form", ACTION_HIGH + 1), nullptr))); + triggers.push_back(new TriggerNode( "party member to heal out of spell range", NextAction::array(0, new NextAction("reach party member to heal", ACTION_CRITICAL_HEAL + 9), nullptr))); - triggers.push_back( - new TriggerNode("party member remove curse", - NextAction::array(0, new NextAction("remove curse on party", ACTION_DISPEL + 7), NULL))); + // CRITICAL triggers.push_back( new TriggerNode("party member critical health", NextAction::array(0, new NextAction("swiftmend on party", ACTION_CRITICAL_HEAL + 4), - new NextAction("wild growth", ACTION_CRITICAL_HEAL + 3), + new NextAction("wild growth on party", ACTION_CRITICAL_HEAL + 3), new NextAction("regrowth on party", ACTION_CRITICAL_HEAL + 2), new NextAction("nourish on party", ACTION_CRITICAL_HEAL + 1), // new NextAction("healing touch on party", ACTION_CRITICAL_HEAL + 0), @@ -66,7 +91,6 @@ void HealDruidStrategy::InitTriggers(std::vector& triggers) new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 8), new NextAction("swiftmend on party", ACTION_MEDIUM_HEAL + 7), new NextAction("nourish on party", ACTION_MEDIUM_HEAL + 6), - // new NextAction("healing touch on party", ACTION_MEDIUM_HEAL + 5), NULL))); // MEDIUM diff --git a/src/strategy/paladin/GenericPaladinStrategy.cpp b/src/strategy/paladin/GenericPaladinStrategy.cpp index d25a7518..791b5cff 100644 --- a/src/strategy/paladin/GenericPaladinStrategy.cpp +++ b/src/strategy/paladin/GenericPaladinStrategy.cpp @@ -71,3 +71,16 @@ void PaladinCcStrategy::InitTriggers(std::vector& triggers) triggers.push_back( new TriggerNode("turn undead", NextAction::array(0, new NextAction("turn undead", ACTION_HIGH + 1), nullptr))); } + +void PaladinAssistDpsStrategy::InitTriggers(std::vector& triggers) +{ + triggers.push_back( + new TriggerNode("healer should attack", + NextAction::array(0, + new NextAction("hammer of wrath", ACTION_DEFAULT + 0.5f), + new NextAction("holy shock", ACTION_DEFAULT + 0.4f), + new NextAction("shield of righteousness", ACTION_DEFAULT + 0.3f), + new NextAction("judgement of light", ACTION_DEFAULT + 0.2f), + new NextAction("exorcism", ACTION_DEFAULT + 0.1f), + nullptr))); +} diff --git a/src/strategy/paladin/GenericPaladinStrategy.h b/src/strategy/paladin/GenericPaladinStrategy.h index 4922a1bc..fcdef3c6 100644 --- a/src/strategy/paladin/GenericPaladinStrategy.h +++ b/src/strategy/paladin/GenericPaladinStrategy.h @@ -46,4 +46,13 @@ public: std::string const getName() override { return "cc"; } }; +class PaladinAssistDpsStrategy : public Strategy +{ +public: + PaladinAssistDpsStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} + + void InitTriggers(std::vector& triggers) override; + std::string const getName() override { return "assist dps"; } +}; + #endif diff --git a/src/strategy/paladin/GenericPaladinStrategyActionNodeFactory.h b/src/strategy/paladin/GenericPaladinStrategyActionNodeFactory.h index 68617eba..3503d773 100644 --- a/src/strategy/paladin/GenericPaladinStrategyActionNodeFactory.h +++ b/src/strategy/paladin/GenericPaladinStrategyActionNodeFactory.h @@ -22,7 +22,7 @@ public: creators["cleanse magic"] = &cleanse_magic; creators["cleanse poison on party"] = &cleanse_poison_on_party; creators["cleanse disease on party"] = &cleanse_disease_on_party; - // creators["seal of wisdom"] = &seal_of_wisdom; + creators["seal of wisdom"] = &seal_of_wisdom; creators["seal of justice"] = &seal_of_justice; creators["hand of reckoning"] = &hand_of_reckoning; creators["judgement"] = &judgement; @@ -147,13 +147,13 @@ private: /*A*/ NextAction::array(0, new NextAction("purify disease on party"), nullptr), /*C*/ nullptr); } - // static ActionNode* seal_of_wisdom(PlayerbotAI* ai) - // { - // return new ActionNode ("seal of wisdom", - // /*P*/ NULL, - // /*A*/ NextAction::array(0, new NextAction("seal of justice"), NULL), - // /*C*/ NULL); - // } + static ActionNode* seal_of_wisdom(PlayerbotAI* ai) + { + return new ActionNode ("seal of wisdom", + /*P*/ NULL, + /*A*/ NextAction::array(0, new NextAction("seal of righteousness"), NULL), + /*C*/ NULL); + } static ActionNode* seal_of_justice(PlayerbotAI* ai) { return new ActionNode("seal of justice", diff --git a/src/strategy/paladin/HealPaladinStrategy.cpp b/src/strategy/paladin/HealPaladinStrategy.cpp index 70c461ce..42517630 100644 --- a/src/strategy/paladin/HealPaladinStrategy.cpp +++ b/src/strategy/paladin/HealPaladinStrategy.cpp @@ -27,7 +27,7 @@ HealPaladinStrategy::HealPaladinStrategy(PlayerbotAI* botAI) : GenericPaladinStr NextAction** HealPaladinStrategy::getDefaultActions() { - return NextAction::array(0, new NextAction("judgement of light", ACTION_DEFAULT + 2), nullptr); + return NextAction::array(0, new NextAction("judgement of light", ACTION_DEFAULT), nullptr); } void HealPaladinStrategy::InitTriggers(std::vector& triggers) diff --git a/src/strategy/paladin/PaladinAiObjectContext.cpp b/src/strategy/paladin/PaladinAiObjectContext.cpp index 094e2fb7..5bc056ba 100644 --- a/src/strategy/paladin/PaladinAiObjectContext.cpp +++ b/src/strategy/paladin/PaladinAiObjectContext.cpp @@ -25,6 +25,7 @@ public: creators["boost"] = &PaladinStrategyFactoryInternal::boost; creators["cc"] = &PaladinStrategyFactoryInternal::cc; creators["bthreat"] = &PaladinStrategyFactoryInternal::bthreat; + creators["assist dps"] = &PaladinStrategyFactoryInternal::assist_dps; } private: @@ -33,6 +34,7 @@ private: static Strategy* boost(PlayerbotAI* botAI) { return new PaladinBoostStrategy(botAI); } static Strategy* cc(PlayerbotAI* botAI) { return new PaladinCcStrategy(botAI); } static Strategy* bthreat(PlayerbotAI* botAI) { return new PaladinBuffThreatStrategy(botAI); } + static Strategy* assist_dps(PlayerbotAI* botAI) { return new PaladinAssistDpsStrategy(botAI); } }; class PaladinResistanceStrategyFactoryInternal : public NamedObjectContext diff --git a/src/strategy/paladin/TankPaladinStrategy.cpp b/src/strategy/paladin/TankPaladinStrategy.cpp index 553d2a7d..1cb2c5c4 100644 --- a/src/strategy/paladin/TankPaladinStrategy.cpp +++ b/src/strategy/paladin/TankPaladinStrategy.cpp @@ -73,7 +73,7 @@ void TankPaladinStrategy::InitTriggers(std::vector& triggers) GenericPaladinStrategy::InitTriggers(triggers); triggers.push_back( - new TriggerNode("seal", NextAction::array(0, new NextAction("seal of command", ACTION_HIGH), nullptr))); + new TriggerNode("seal", NextAction::array(0, new NextAction("seal of corruption", ACTION_HIGH), nullptr))); triggers.push_back( new TriggerNode("low mana", NextAction::array(0, new NextAction("seal of wisdom", ACTION_HIGH + 9), nullptr))); // triggers.push_back(new TriggerNode("devotion aura", NextAction::array(0, new NextAction("devotion aura", 90.0f), diff --git a/src/strategy/priest/GenericPriestStrategy.cpp b/src/strategy/priest/GenericPriestStrategy.cpp index 40bae42d..42826fcd 100644 --- a/src/strategy/priest/GenericPriestStrategy.cpp +++ b/src/strategy/priest/GenericPriestStrategy.cpp @@ -87,3 +87,23 @@ void PriestCcStrategy::InitTriggers(std::vector& triggers) triggers.push_back( new TriggerNode("shackle undead", NextAction::array(0, new NextAction("shackle undead", 31.0f), nullptr))); } + +void PriestAssistDpsStrategy::InitTriggers(std::vector& triggers) +{ + triggers.push_back( + new TriggerNode("healer should attack", + NextAction::array(0, + new NextAction("shadow word: pain", ACTION_DEFAULT + 0.5f), + new NextAction("holy fire", ACTION_DEFAULT + 0.4f), + // new NextAction("mind blast", ACTION_DEFAULT + 0.3f), + new NextAction("smite", ACTION_DEFAULT + 0.1f), + new NextAction("shoot", ACTION_DEFAULT), + nullptr))); + + triggers.push_back( + new TriggerNode("medium aoe and healer should attack", + NextAction::array(0, + new NextAction("mind sear", ACTION_DEFAULT + 0.5f), + nullptr))); +} + diff --git a/src/strategy/priest/GenericPriestStrategy.h b/src/strategy/priest/GenericPriestStrategy.h index 7c6684af..f18e2b00 100644 --- a/src/strategy/priest/GenericPriestStrategy.h +++ b/src/strategy/priest/GenericPriestStrategy.h @@ -46,4 +46,13 @@ public: std::string const getName() override { return "cc"; } }; +class PriestAssistDpsStrategy : public Strategy +{ +public: + PriestAssistDpsStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} + + void InitTriggers(std::vector& triggers) override; + std::string const getName() override { return "assist dps"; } +}; + #endif diff --git a/src/strategy/priest/PriestActions.h b/src/strategy/priest/PriestActions.h index 468694c8..d5af0509 100644 --- a/src/strategy/priest/PriestActions.h +++ b/src/strategy/priest/PriestActions.h @@ -68,7 +68,13 @@ CURE_PARTY_ACTION(CastCureDiseaseOnPartyAction, "cure disease", DISPEL_DISEASE); CURE_ACTION(CastAbolishDiseaseAction, "abolish disease"); CURE_PARTY_ACTION(CastAbolishDiseaseOnPartyAction, "abolish disease", DISPEL_DISEASE); -DEBUFF_CHECKISOWNER_ACTION(CastHolyFireAction, "holy fire"); +// DEBUFF_CHECKISOWNER_ACTION(CastHolyFireAction, "holy fire"); +class CastHolyFireAction : public CastDebuffSpellAction +{ +public: + CastHolyFireAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "holy fire", true, 0.0f) {} +}; + // shadow 2.4.3 // BUFF_ACTION(CastShadowfiendAction, "shadowfiend"); SPELL_ACTION(CastShadowWordDeathAction, "shadow word: death"); diff --git a/src/strategy/priest/PriestAiObjectContext.cpp b/src/strategy/priest/PriestAiObjectContext.cpp index 0df50eec..caf4eb79 100644 --- a/src/strategy/priest/PriestAiObjectContext.cpp +++ b/src/strategy/priest/PriestAiObjectContext.cpp @@ -5,6 +5,7 @@ #include "PriestAiObjectContext.h" +#include "GenericPriestStrategy.h" #include "HolyPriestStrategy.h" #include "NamedObjectContext.h" #include "Playerbots.h" @@ -30,6 +31,7 @@ public: creators["boost"] = &PriestStrategyFactoryInternal::boost; creators["rshadow"] = &PriestStrategyFactoryInternal::rshadow; creators["cc"] = &PriestStrategyFactoryInternal::cc; + creators["assist dps"] = &PriestStrategyFactoryInternal::assist_dps; } private: @@ -42,6 +44,7 @@ private: static Strategy* pull(PlayerbotAI* botAI) { return new PullStrategy(botAI, "shoot"); } static Strategy* shadow_debuff(PlayerbotAI* botAI) { return new ShadowPriestDebuffStrategy(botAI); } static Strategy* cure(PlayerbotAI* botAI) { return new PriestCureStrategy(botAI); } + static Strategy* assist_dps(PlayerbotAI* botAI) { return new PriestAssistDpsStrategy(botAI); } }; class PriestCombatStrategyFactoryInternal : public NamedObjectContext diff --git a/src/strategy/priest/PriestNonCombatStrategy.cpp b/src/strategy/priest/PriestNonCombatStrategy.cpp index 161ac361..5283fb75 100644 --- a/src/strategy/priest/PriestNonCombatStrategy.cpp +++ b/src/strategy/priest/PriestNonCombatStrategy.cpp @@ -55,7 +55,7 @@ void PriestNonCombatStrategy::InitTriggers(std::vector& triggers) NextAction::array(0, new NextAction("renew on party", ACTION_LIGHT_HEAL + 3), NULL))); triggers.push_back( - new TriggerNode("medium aoe heal", NextAction::array(0, new NextAction("circle of healing", 27.0f), NULL))); + new TriggerNode("group heal occasion", NextAction::array(0, new NextAction("circle of healing", 27.0f), NULL))); } void PriestBuffStrategy::InitTriggers(std::vector& triggers) diff --git a/src/strategy/shaman/CasterShamanStrategy.cpp b/src/strategy/shaman/CasterShamanStrategy.cpp index 746abf86..c11c523d 100644 --- a/src/strategy/shaman/CasterShamanStrategy.cpp +++ b/src/strategy/shaman/CasterShamanStrategy.cpp @@ -51,8 +51,8 @@ void CasterShamanStrategy::InitTriggers(std::vector& triggers) // triggers.push_back(new TriggerNode("enemy out of spell", NextAction::array(0, new NextAction("reach spell", // ACTION_NORMAL + 9), nullptr))); triggers.push_back(new TriggerNode("shaman weapon", NextAction::array(0, new // NextAction("flametongue weapon", 23.0f), nullptr))); - // triggers.push_back(new TriggerNode( - // "enough mana", NextAction::array(0, new NextAction("chain lightning", ACTION_DEFAULT + 0.1f), nullptr))); + triggers.push_back(new TriggerNode( + "enough mana", NextAction::array(0, new NextAction("chain lightning", ACTION_DEFAULT + 0.1f), nullptr))); triggers.push_back(new TriggerNode("main hand weapon no imbue", NextAction::array(0, new NextAction("flametongue weapon", 22.0f), nullptr))); diff --git a/src/strategy/shaman/GenericShamanStrategy.cpp b/src/strategy/shaman/GenericShamanStrategy.cpp index 7110036d..d84b07a7 100644 --- a/src/strategy/shaman/GenericShamanStrategy.cpp +++ b/src/strategy/shaman/GenericShamanStrategy.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 2 of the License, or (at your option), any later version. */ +#include "GenericShamanStrategy.h" #include "HealShamanStrategy.h" #include "Playerbots.h" #include "Strategy.h" @@ -162,3 +163,18 @@ void ShamanCureStrategy::InitTriggers(std::vector& triggers) new TriggerNode("party member cleanse spirit curse", NextAction::array(0, new NextAction("cleanse spirit curse on party", 23.0f), nullptr))); } + +void ShamanAssistDpsStrategy::InitTriggers(std::vector& triggers) +{ + triggers.push_back( + new TriggerNode("healer should attack", + NextAction::array(0, + new NextAction("flame shock", ACTION_DEFAULT + 0.2f), + new NextAction("lava burst", ACTION_DEFAULT + 0.1f), + new NextAction("lightning bolt", ACTION_DEFAULT), nullptr))); + + triggers.push_back( + new TriggerNode("medium aoe and healer should attack", + NextAction::array(0, + new NextAction("chain lightning", ACTION_DEFAULT + 0.3f), nullptr))); +} \ No newline at end of file diff --git a/src/strategy/shaman/GenericShamanStrategy.h b/src/strategy/shaman/GenericShamanStrategy.h index 49753e7b..d41b8c04 100644 --- a/src/strategy/shaman/GenericShamanStrategy.h +++ b/src/strategy/shaman/GenericShamanStrategy.h @@ -45,4 +45,13 @@ public: std::string const getName() override { return "cure"; } }; +class ShamanAssistDpsStrategy : public Strategy +{ +public: + ShamanAssistDpsStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} + + void InitTriggers(std::vector& triggers) override; + std::string const getName() override { return "assist dps"; } +}; + #endif diff --git a/src/strategy/shaman/ShamanActions.h b/src/strategy/shaman/ShamanActions.h index c4f6ce38..9d3b8b69 100644 --- a/src/strategy/shaman/ShamanActions.h +++ b/src/strategy/shaman/ShamanActions.h @@ -335,7 +335,7 @@ public: class CastFlameShockAction : public CastDebuffSpellAction { public: - CastFlameShockAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "flame shock", true) {} + CastFlameShockAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "flame shock", true, 6.0f) {} }; class CastEarthShockAction : public CastSpellAction diff --git a/src/strategy/shaman/ShamanAiObjectContext.cpp b/src/strategy/shaman/ShamanAiObjectContext.cpp index e67f4230..bf9e76e3 100644 --- a/src/strategy/shaman/ShamanAiObjectContext.cpp +++ b/src/strategy/shaman/ShamanAiObjectContext.cpp @@ -6,6 +6,7 @@ #include "ShamanAiObjectContext.h" #include "CasterShamanStrategy.h" +#include "GenericShamanStrategy.h" #include "HealShamanStrategy.h" #include "MeleeShamanStrategy.h" #include "NamedObjectContext.h" @@ -25,6 +26,7 @@ public: creators["melee aoe"] = &ShamanStrategyFactoryInternal::melee_aoe; creators["caster aoe"] = &ShamanStrategyFactoryInternal::caster_aoe; creators["cure"] = &ShamanStrategyFactoryInternal::cure; + creators["assist dps"] = &ShamanStrategyFactoryInternal::assist_dps; } private: @@ -33,6 +35,7 @@ private: static Strategy* melee_aoe(PlayerbotAI* botAI) { return new MeleeAoeShamanStrategy(botAI); } static Strategy* caster_aoe(PlayerbotAI* botAI) { return new CasterAoeShamanStrategy(botAI); } static Strategy* cure(PlayerbotAI* botAI) { return new ShamanCureStrategy(botAI); } + static Strategy* assist_dps(PlayerbotAI* botAI) { return new ShamanAssistDpsStrategy(botAI); } }; class ShamanBuffStrategyFactoryInternal : public NamedObjectContext diff --git a/src/strategy/shaman/ShamanNonCombatStrategy.cpp b/src/strategy/shaman/ShamanNonCombatStrategy.cpp index 3d544419..f3380cb5 100644 --- a/src/strategy/shaman/ShamanNonCombatStrategy.cpp +++ b/src/strategy/shaman/ShamanNonCombatStrategy.cpp @@ -39,7 +39,7 @@ void ShamanNonCombatStrategy::InitTriggers(std::vector& triggers) new NextAction("lesser healing wave on party", 24.0f), NULL))); triggers.push_back( - new TriggerNode("medium aoe heal", NextAction::array(0, new NextAction("chain heal", 27.0f), NULL))); + new TriggerNode("group heal occasion", NextAction::array(0, new NextAction("chain heal", 27.0f), NULL))); triggers.push_back( new TriggerNode("cure poison", NextAction::array(0, new NextAction("cure poison", 21.0f), nullptr))); diff --git a/src/strategy/triggers/GenericTriggers.cpp b/src/strategy/triggers/GenericTriggers.cpp index f700e472..0adbc077 100644 --- a/src/strategy/triggers/GenericTriggers.cpp +++ b/src/strategy/triggers/GenericTriggers.cpp @@ -76,7 +76,7 @@ bool AlmostFullManaTrigger::IsActive() bool EnoughManaTrigger::IsActive() { - return AI_VALUE2(bool, "has mana", "self target") && AI_VALUE2(uint8, "mana", "self target") > 65; + return AI_VALUE2(bool, "has mana", "self target") && AI_VALUE2(uint8, "mana", "self target") > sPlayerbotAIConfig->highMana; } bool RageAvailable::IsActive() { return AI_VALUE2(uint8, "rage", "self target") >= amount; } @@ -366,6 +366,34 @@ bool GenericBoostTrigger::IsActive() return AI_VALUE(uint8, "balance") <= balance; } +bool HealerShouldAttackTrigger::IsActive() +{ + // nobody can help me + if (botAI->GetNearGroupMemberCount(sPlayerbotAIConfig->sightDistance) <= 1) + return true; + + bool almostFullMana = AI_VALUE2(bool, "has mana", "self target") && + AI_VALUE2(uint8, "mana", "self target") < 85; + + // high pressure + if (AI_VALUE(uint8, "balance") <= 50 && almostFullMana) + return false; + + bool highMana = AI_VALUE2(bool, "has mana", "self target") && + AI_VALUE2(uint8, "mana", "self target") < sPlayerbotAIConfig->highMana; + + if (AI_VALUE(uint8, "balance") <= 100 && highMana) + return false; + + bool mediumMana = AI_VALUE2(bool, "has mana", "self target") && + AI_VALUE2(uint8, "mana", "self target") < sPlayerbotAIConfig->mediumMana; + + if (mediumMana) + return false; + + return true; +} + bool ItemCountTrigger::IsActive() { return AI_VALUE2(uint32, "item count", item) < count; } bool InterruptSpellTrigger::IsActive() diff --git a/src/strategy/triggers/GenericTriggers.h b/src/strategy/triggers/GenericTriggers.h index e9990c3e..f93cb156 100644 --- a/src/strategy/triggers/GenericTriggers.h +++ b/src/strategy/triggers/GenericTriggers.h @@ -459,6 +459,16 @@ protected: float balance; }; +class HealerShouldAttackTrigger : public Trigger +{ +public: + HealerShouldAttackTrigger(PlayerbotAI* botAI) + : Trigger(botAI, "healer should attack", 1) + { + } + + bool IsActive() override; +}; class RandomTrigger : public Trigger { diff --git a/src/strategy/triggers/HealthTriggers.cpp b/src/strategy/triggers/HealthTriggers.cpp index d4c971ac..3bc8d4f3 100644 --- a/src/strategy/triggers/HealthTriggers.cpp +++ b/src/strategy/triggers/HealthTriggers.cpp @@ -24,7 +24,18 @@ bool AoeHealTrigger::IsActive() { return AI_VALUE2(uint8, "aoe heal", type) >= c bool AoeInGroupTrigger::IsActive() { - Group* group = bot->GetGroup(); - return group && group->GetMembersCount() >= 5 && - AI_VALUE2(uint8, "aoe heal", type) >= (group->GetMembersCount() * ratio); + int32 member = botAI->GetNearGroupMemberCount(); + if (member < 5) + return false; + int threshold = member * 0.5; + if (member <= 5) + threshold = 3; + else if (member <= 10) + threshold = std::min(threshold, 5); + else if (member <= 25) + threshold = std::min(threshold, 10); + else + threshold = std::min(threshold, 15); + + return AI_VALUE2(uint8, "aoe heal", type) >= threshold; } \ No newline at end of file diff --git a/src/strategy/triggers/HealthTriggers.h b/src/strategy/triggers/HealthTriggers.h index d9f3253c..279bc3fe 100644 --- a/src/strategy/triggers/HealthTriggers.h +++ b/src/strategy/triggers/HealthTriggers.h @@ -186,14 +186,13 @@ protected: class AoeInGroupTrigger : public Trigger { public: - AoeInGroupTrigger(PlayerbotAI* ai, std::string name, std::string type, float ratio) - : Trigger(ai, name), ratio(ratio), type(type) + AoeInGroupTrigger(PlayerbotAI* ai, std::string name, std::string type) + : Trigger(ai, name), type(type) { } bool IsActive() override; protected: - float ratio; std::string type; }; diff --git a/src/strategy/triggers/TriggerContext.h b/src/strategy/triggers/TriggerContext.h index 860ee682..4bfea1d4 100644 --- a/src/strategy/triggers/TriggerContext.h +++ b/src/strategy/triggers/TriggerContext.h @@ -48,6 +48,7 @@ public: creators["almost full mana"] = &TriggerContext::AlmostFullMana; creators["enough mana"] = &TriggerContext::EnoughMana; + creators["party member critical health"] = &TriggerContext::PartyMemberCriticalHealth; creators["party member low health"] = &TriggerContext::PartyMemberLowHealth; creators["party member medium health"] = &TriggerContext::PartyMemberMediumHealth; @@ -83,6 +84,9 @@ public: creators["medium aoe"] = &TriggerContext::MediumAoe; creators["high aoe"] = &TriggerContext::HighAoe; + creators["healer should attack"] = &TriggerContext::healer_should_attack; + creators["medium aoe and healer should attack"] = &TriggerContext::medium_aoe_and_healer_should_attack; + creators["has area debuff"] = &TriggerContext::HasAreaDebuff; creators["enemy out of melee"] = &TriggerContext::EnemyOutOfMelee; @@ -242,11 +246,11 @@ private: } static Trigger* group_heal_occasion(PlayerbotAI* ai) { - return new AoeInGroupTrigger(ai, "group heal occasion", "almost full", 0.6); + return new AoeInGroupTrigger(ai, "group heal occasion", "almost full"); } static Trigger* medium_group_heal_occasion(PlayerbotAI* ai) { - return new AoeInGroupTrigger(ai, "group heal occasion", "medium", 0.6); + return new AoeInGroupTrigger(ai, "medium group heal occasion", "medium"); } static Trigger* target_changed(PlayerbotAI* botAI) { return new TargetChangedTrigger(botAI); } static Trigger* swimming(PlayerbotAI* botAI) { return new IsSwimmingTrigger(botAI); } @@ -265,6 +269,8 @@ private: static Trigger* LightAoe(PlayerbotAI* botAI) { return new LightAoeTrigger(botAI); } static Trigger* MediumAoe(PlayerbotAI* botAI) { return new MediumAoeTrigger(botAI); } static Trigger* HighAoe(PlayerbotAI* botAI) { return new HighAoeTrigger(botAI); } + static Trigger* healer_should_attack(PlayerbotAI* botAI) { return new HealerShouldAttackTrigger(botAI); } + static Trigger* medium_aoe_and_healer_should_attack(PlayerbotAI* botAI) { return new TwoTriggers(botAI, "medium aoe", "healer should attack"); } static Trigger* HasAreaDebuff(PlayerbotAI* botAI) { return new HasAreaDebuffTrigger(botAI); } static Trigger* LoseAggro(PlayerbotAI* botAI) { return new LoseAggroTrigger(botAI); } static Trigger* HasAggro(PlayerbotAI* botAI) { return new HasAggroTrigger(botAI); } diff --git a/src/strategy/values/AttackerCountValues.cpp b/src/strategy/values/AttackerCountValues.cpp index 59de8995..17ba4562 100644 --- a/src/strategy/values/AttackerCountValues.cpp +++ b/src/strategy/values/AttackerCountValues.cpp @@ -94,7 +94,7 @@ uint8 BalancePercentValue::Calculate() level *= 3; break; case CREATURE_ELITE_WORLDBOSS: - level *= 30; + level *= 20; break; } From a0dd00bba1ef711881bffce1b03de10dd5e044ae Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Fri, 4 Oct 2024 01:49:57 +0800 Subject: [PATCH 05/40] [Spell] Handle tree of life and assist dps --- src/AiFactory.cpp | 4 +- src/strategy/druid/CasterDruidStrategy.cpp | 2 +- src/strategy/druid/DruidAiObjectContext.cpp | 2 + src/strategy/druid/DruidShapeshiftActions.cpp | 11 +++ src/strategy/druid/DruidShapeshiftActions.h | 10 +++ .../druid/GenericDruidNonCombatStrategy.cpp | 24 ++++--- src/strategy/druid/GenericDruidStrategy.cpp | 8 +++ src/strategy/druid/HealDruidStrategy.cpp | 71 ++++++++++--------- .../GenericPaladinNonCombatStrategy.cpp | 2 +- .../paladin/GenericPaladinStrategy.cpp | 11 +-- src/strategy/priest/GenericPriestStrategy.cpp | 4 +- src/strategy/triggers/GenericTriggers.cpp | 33 +++++---- .../GenericWarlockNonCombatStrategy.cpp | 2 +- 13 files changed, 117 insertions(+), 67 deletions(-) diff --git a/src/AiFactory.cpp b/src/AiFactory.cpp index ce3fc272..c03cd615 100644 --- a/src/AiFactory.cpp +++ b/src/AiFactory.cpp @@ -484,8 +484,8 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa engine->removeStrategy("threat", false); engine->addStrategy("boost", false); - if ((player->getClass() == CLASS_DRUID && tab == 2) || (player->getClass() == CLASS_SHAMAN && tab == 2)) - engine->addStrategiesNoInit("caster", "caster aoe", nullptr); + // if ((player->getClass() == CLASS_DRUID && tab == 2) || (player->getClass() == CLASS_SHAMAN && tab == 2)) + // engine->addStrategiesNoInit("caster", "caster aoe", nullptr); // if (player->getClass() == CLASS_DRUID && tab == 1) // engine->addStrategiesNoInit(/*"behind",*/ "dps", nullptr); diff --git a/src/strategy/druid/CasterDruidStrategy.cpp b/src/strategy/druid/CasterDruidStrategy.cpp index 61e7ab3d..27080efe 100644 --- a/src/strategy/druid/CasterDruidStrategy.cpp +++ b/src/strategy/druid/CasterDruidStrategy.cpp @@ -150,7 +150,7 @@ void CasterDruidStrategy::InitTriggers(std::vector& triggers) void CasterDruidAoeStrategy::InitTriggers(std::vector& triggers) { triggers.push_back( - new TriggerNode("high aoe", NextAction::array(0, new NextAction("hurricane", ACTION_HIGH + 1), nullptr))); + new TriggerNode("medium aoe", NextAction::array(0, new NextAction("hurricane", ACTION_HIGH + 1), nullptr))); triggers.push_back(new TriggerNode( "light aoe", NextAction::array(0, new NextAction("starfall", ACTION_NORMAL + 5), new NextAction("insect swarm on attacker", ACTION_NORMAL + 3), diff --git a/src/strategy/druid/DruidAiObjectContext.cpp b/src/strategy/druid/DruidAiObjectContext.cpp index fabd491b..a39fbc5b 100644 --- a/src/strategy/druid/DruidAiObjectContext.cpp +++ b/src/strategy/druid/DruidAiObjectContext.cpp @@ -164,6 +164,7 @@ public: creators["travel form"] = &DruidAiObjectContextInternal::travel_form; creators["aquatic form"] = &DruidAiObjectContextInternal::aquatic_form; creators["caster form"] = &DruidAiObjectContextInternal::caster_form; + creators["cancel tree form"] = &DruidAiObjectContextInternal::cancel_tree_form; creators["mangle (bear)"] = &DruidAiObjectContextInternal::mangle_bear; creators["maul"] = &DruidAiObjectContextInternal::maul; creators["bash"] = &DruidAiObjectContextInternal::bash; @@ -249,6 +250,7 @@ private: static Action* travel_form(PlayerbotAI* botAI) { return new CastTravelFormAction(botAI); } static Action* aquatic_form(PlayerbotAI* botAI) { return new CastAquaticFormAction(botAI); } static Action* caster_form(PlayerbotAI* botAI) { return new CastCasterFormAction(botAI); } + static Action* cancel_tree_form(PlayerbotAI* botAI) { return new CastCancelTreeFormAction(botAI); } static Action* mangle_bear(PlayerbotAI* botAI) { return new CastMangleBearAction(botAI); } static Action* maul(PlayerbotAI* botAI) { return new CastMaulAction(botAI); } static Action* bash(PlayerbotAI* botAI) { return new CastBashAction(botAI); } diff --git a/src/strategy/druid/DruidShapeshiftActions.cpp b/src/strategy/druid/DruidShapeshiftActions.cpp index 9894b1f5..4354328e 100644 --- a/src/strategy/druid/DruidShapeshiftActions.cpp +++ b/src/strategy/druid/DruidShapeshiftActions.cpp @@ -45,6 +45,17 @@ bool CastCasterFormAction::Execute(Event event) return true; } +bool CastCancelTreeFormAction::isUseful() +{ + return botAI->HasAura(33891, bot); +} + +bool CastCancelTreeFormAction::Execute(Event event) +{ + botAI->RemoveAura("tree of life"); + return true; +} + bool CastTreeFormAction::isUseful() { return GetTarget() && CastSpellAction::isUseful() && !botAI->HasAura(33891, bot); diff --git a/src/strategy/druid/DruidShapeshiftActions.h b/src/strategy/druid/DruidShapeshiftActions.h index 1f8fc082..6b24208b 100644 --- a/src/strategy/druid/DruidShapeshiftActions.h +++ b/src/strategy/druid/DruidShapeshiftActions.h @@ -70,4 +70,14 @@ public: bool Execute(Event event) override; }; +class CastCancelTreeFormAction : public CastBuffSpellAction +{ +public: + CastCancelTreeFormAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "cancel tree form") {} + + bool isUseful() override; + bool isPossible() override { return true; } + bool Execute(Event event) override; +}; + #endif diff --git a/src/strategy/druid/GenericDruidNonCombatStrategy.cpp b/src/strategy/druid/GenericDruidNonCombatStrategy.cpp index 398ba274..be5e822f 100644 --- a/src/strategy/druid/GenericDruidNonCombatStrategy.cpp +++ b/src/strategy/druid/GenericDruidNonCombatStrategy.cpp @@ -125,26 +125,34 @@ void GenericDruidNonCombatStrategy::InitTriggers(std::vector& trig triggers.push_back( new TriggerNode("party member critical health", - NextAction::array(0, new NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 5), - new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 6), NULL))); + NextAction::array(0, + new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 7), + new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 6), + new NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 5), + nullptr))); triggers.push_back( new TriggerNode("party member low health", - NextAction::array(0, new NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 3), - new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 4), NULL))); + NextAction::array(0, + new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 5), + new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 4), + new NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 3), + nullptr))); triggers.push_back( new TriggerNode("party member medium health", - NextAction::array(0, new NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 1), - new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 2), NULL))); + NextAction::array(0, new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 3), + new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 2), + new NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 1), + nullptr))); triggers.push_back( new TriggerNode("party member almost full health", - NextAction::array(0, new NextAction("rejuvenation on party", ACTION_LIGHT_HEAL + 2), NULL))); + NextAction::array(0, new NextAction("wild growth on party", ACTION_LIGHT_HEAL + 3), new NextAction("rejuvenation on party", ACTION_LIGHT_HEAL + 2), NULL))); triggers.push_back( new TriggerNode("party member remove curse", - NextAction::array(0, new NextAction("remove curse on party", ACTION_DISPEL + 7), NULL))); + NextAction::array(0, new NextAction("remove curse on party", ACTION_DISPEL + 7), nullptr))); } GenericDruidBuffStrategy::GenericDruidBuffStrategy(PlayerbotAI* botAI) : NonCombatStrategy(botAI) diff --git a/src/strategy/druid/GenericDruidStrategy.cpp b/src/strategy/druid/GenericDruidStrategy.cpp index ccd3be5e..c3dacdb3 100644 --- a/src/strategy/druid/GenericDruidStrategy.cpp +++ b/src/strategy/druid/GenericDruidStrategy.cpp @@ -158,7 +158,15 @@ void DruidAssistDpsStrategy::InitTriggers(std::vector& triggers) triggers.push_back( new TriggerNode("healer should attack", NextAction::array(0, + new NextAction("cancel tree form", ACTION_DEFAULT + 0.3f), new NextAction("moonfire", ACTION_DEFAULT + 0.2f), new NextAction("wrath", ACTION_DEFAULT + 0.1f), + new NextAction("starfire", ACTION_DEFAULT), + nullptr))); + + triggers.push_back( + new TriggerNode("medium aoe and healer should attack", + NextAction::array(0, + new NextAction("hurricane", ACTION_DEFAULT + 0.7f), nullptr))); } diff --git a/src/strategy/druid/HealDruidStrategy.cpp b/src/strategy/druid/HealDruidStrategy.cpp index 8fb17f2c..b1a28c6e 100644 --- a/src/strategy/druid/HealDruidStrategy.cpp +++ b/src/strategy/druid/HealDruidStrategy.cpp @@ -12,9 +12,9 @@ class HealDruidStrategyActionNodeFactory : public NamedObjectFactory public: HealDruidStrategyActionNodeFactory() { creators["nourish on party"] = &nourtish_on_party; - creators["wild growth on party"] = &wild_growth_on_party; - creators["rejuvenation on party"] = &rejuvenation_on_party; - creators["regrowth on party"] = ®rowth_on_party; + // creators["wild growth on party"] = &wild_growth_on_party; + // creators["rejuvenation on party"] = &rejuvenation_on_party; + // creators["regrowth on party"] = ®rowth_on_party; } private: @@ -25,27 +25,27 @@ private: /*A*/ NextAction::array(0, new NextAction("healing touch on party"), nullptr), /*C*/ nullptr); } - static ActionNode* wild_growth_on_party([[maybe_unused]] PlayerbotAI* botAI) - { - return new ActionNode("wild growth on party", - /*P*/ NextAction::array(0, new NextAction("tree form"), nullptr), - /*A*/ nullptr, - /*C*/ nullptr); - } - static ActionNode* rejuvenation_on_party([[maybe_unused]] PlayerbotAI* botAI) - { - return new ActionNode("rejuvenation on party", - /*P*/ NextAction::array(0, new NextAction("tree form"), nullptr), - /*A*/ nullptr, - /*C*/ nullptr); - } - static ActionNode* regrowth_on_party([[maybe_unused]] PlayerbotAI* botAI) - { - return new ActionNode("regrowth on party", - /*P*/ NextAction::array(0, new NextAction("tree form"), nullptr), - /*A*/ nullptr, - /*C*/ nullptr); - } + // static ActionNode* wild_growth_on_party([[maybe_unused]] PlayerbotAI* botAI) + // { + // return new ActionNode("wild growth on party", + // /*P*/ NextAction::array(0, new NextAction("tree form"), nullptr), + // /*A*/ nullptr, + // /*C*/ nullptr); + // } + // static ActionNode* rejuvenation_on_party([[maybe_unused]] PlayerbotAI* botAI) + // { + // return new ActionNode("rejuvenation on party", + // /*P*/ NextAction::array(0, new NextAction("tree form"), nullptr), + // /*A*/ nullptr, + // /*C*/ nullptr); + // } + // static ActionNode* regrowth_on_party([[maybe_unused]] PlayerbotAI* botAI) + // { + // return new ActionNode("regrowth on party", + // /*P*/ NextAction::array(0, new NextAction("tree form"), nullptr), + // /*A*/ nullptr, + // /*C*/ nullptr); + // } }; HealDruidStrategy::HealDruidStrategy(PlayerbotAI* botAI) : GenericDruidStrategy(botAI) @@ -57,8 +57,6 @@ void HealDruidStrategy::InitTriggers(std::vector& triggers) { GenericDruidStrategy::InitTriggers(triggers); - // triggers.push_back(new TriggerNode("enemy out of spell", NextAction::array(0, new NextAction("reach spell", - // ACTION_NORMAL + 9), nullptr))); // triggers.push_back( // new TriggerNode("tree form", NextAction::array(0, new NextAction("tree form", ACTION_HIGH + 1), nullptr))); @@ -69,12 +67,14 @@ void HealDruidStrategy::InitTriggers(std::vector& triggers) // CRITICAL triggers.push_back( new TriggerNode("party member critical health", - NextAction::array(0, new NextAction("swiftmend on party", ACTION_CRITICAL_HEAL + 4), + NextAction::array(0, + new NextAction("tree form", ACTION_CRITICAL_HEAL + 4.1f), + new NextAction("swiftmend on party", ACTION_CRITICAL_HEAL + 4), new NextAction("wild growth on party", ACTION_CRITICAL_HEAL + 3), new NextAction("regrowth on party", ACTION_CRITICAL_HEAL + 2), new NextAction("nourish on party", ACTION_CRITICAL_HEAL + 1), // new NextAction("healing touch on party", ACTION_CRITICAL_HEAL + 0), - NULL))); + nullptr))); triggers.push_back( new TriggerNode("party member critical health", @@ -87,29 +87,32 @@ void HealDruidStrategy::InitTriggers(std::vector& triggers) // LOW triggers.push_back( new TriggerNode("party member low health", - NextAction::array(0, new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 9), + NextAction::array(0, new NextAction("tree form", ACTION_MEDIUM_HEAL + 9.1f), + new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 9), new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 8), new NextAction("swiftmend on party", ACTION_MEDIUM_HEAL + 7), new NextAction("nourish on party", ACTION_MEDIUM_HEAL + 6), - NULL))); + nullptr))); // MEDIUM triggers.push_back( new TriggerNode("party member medium health", - NextAction::array(0, new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 4), + NextAction::array(0, + new NextAction("tree form", ACTION_MEDIUM_HEAL + 4.1f), + new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 4), new NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 3), new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 2), - new NextAction("nourish on party", ACTION_MEDIUM_HEAL + 1), NULL))); + new NextAction("nourish on party", ACTION_MEDIUM_HEAL + 1), nullptr))); // almost full triggers.push_back( new TriggerNode("party member almost full health", NextAction::array(0, new NextAction("wild growth on party", ACTION_LIGHT_HEAL + 3), new NextAction("rejuvenation on party", ACTION_LIGHT_HEAL + 2), - new NextAction("regrowth on party", ACTION_LIGHT_HEAL + 1), NULL))); + new NextAction("regrowth on party", ACTION_LIGHT_HEAL + 1), nullptr))); triggers.push_back( - new TriggerNode("medium mana", NextAction::array(0, new NextAction("innervate", ACTION_HIGH + 5), NULL))); + new TriggerNode("medium mana", NextAction::array(0, new NextAction("innervate", ACTION_HIGH + 5), nullptr))); triggers.push_back(new TriggerNode("enemy too close for spell", NextAction::array(0, new NextAction("flee", ACTION_MOVE + 9), nullptr))); diff --git a/src/strategy/paladin/GenericPaladinNonCombatStrategy.cpp b/src/strategy/paladin/GenericPaladinNonCombatStrategy.cpp index 80e23cd3..d19320bd 100644 --- a/src/strategy/paladin/GenericPaladinNonCombatStrategy.cpp +++ b/src/strategy/paladin/GenericPaladinNonCombatStrategy.cpp @@ -22,7 +22,7 @@ void GenericPaladinNonCombatStrategy::InitTriggers(std::vector& tr triggers.push_back(new TriggerNode("party member almost full health", NextAction::array(0, new NextAction("flash of light on party", 25.0f), NULL))); triggers.push_back(new TriggerNode("party member medium health", - NextAction::array(0, new NextAction("holy light on party", 26.0f), NULL))); + NextAction::array(0, new NextAction("flash of light on party", 26.0f), NULL))); triggers.push_back(new TriggerNode("party member low health", NextAction::array(0, new NextAction("holy light on party", 27.0f), NULL))); triggers.push_back(new TriggerNode("party member critical health", diff --git a/src/strategy/paladin/GenericPaladinStrategy.cpp b/src/strategy/paladin/GenericPaladinStrategy.cpp index 791b5cff..09eb2074 100644 --- a/src/strategy/paladin/GenericPaladinStrategy.cpp +++ b/src/strategy/paladin/GenericPaladinStrategy.cpp @@ -77,10 +77,11 @@ void PaladinAssistDpsStrategy::InitTriggers(std::vector& triggers) triggers.push_back( new TriggerNode("healer should attack", NextAction::array(0, - new NextAction("hammer of wrath", ACTION_DEFAULT + 0.5f), - new NextAction("holy shock", ACTION_DEFAULT + 0.4f), - new NextAction("shield of righteousness", ACTION_DEFAULT + 0.3f), - new NextAction("judgement of light", ACTION_DEFAULT + 0.2f), - new NextAction("exorcism", ACTION_DEFAULT + 0.1f), + new NextAction("hammer of wrath", ACTION_DEFAULT + 0.6f), + new NextAction("holy shock", ACTION_DEFAULT + 0.5f), + new NextAction("shield of righteousness", ACTION_DEFAULT + 0.4f), + new NextAction("judgement of light", ACTION_DEFAULT + 0.3f), + new NextAction("consecration", ACTION_DEFAULT + 0.2f), + new NextAction("exorcism", ACTION_DEFAULT+ 0.1f), nullptr))); } diff --git a/src/strategy/priest/GenericPriestStrategy.cpp b/src/strategy/priest/GenericPriestStrategy.cpp index 42826fcd..fa680424 100644 --- a/src/strategy/priest/GenericPriestStrategy.cpp +++ b/src/strategy/priest/GenericPriestStrategy.cpp @@ -95,8 +95,8 @@ void PriestAssistDpsStrategy::InitTriggers(std::vector& triggers) NextAction::array(0, new NextAction("shadow word: pain", ACTION_DEFAULT + 0.5f), new NextAction("holy fire", ACTION_DEFAULT + 0.4f), - // new NextAction("mind blast", ACTION_DEFAULT + 0.3f), - new NextAction("smite", ACTION_DEFAULT + 0.1f), + new NextAction("smite", ACTION_DEFAULT + 0.3f), + new NextAction("mind blast", ACTION_DEFAULT + 0.2f), new NextAction("shoot", ACTION_DEFAULT), nullptr))); diff --git a/src/strategy/triggers/GenericTriggers.cpp b/src/strategy/triggers/GenericTriggers.cpp index 0adbc077..26572394 100644 --- a/src/strategy/triggers/GenericTriggers.cpp +++ b/src/strategy/triggers/GenericTriggers.cpp @@ -9,6 +9,8 @@ #include "BattlegroundWS.h" #include "CreatureAI.h" +#include "GameTime.h" +#include "LastSpellCastValue.h" #include "ObjectGuid.h" #include "PlayerbotAIConfig.h" #include "Playerbots.h" @@ -372,23 +374,28 @@ bool HealerShouldAttackTrigger::IsActive() if (botAI->GetNearGroupMemberCount(sPlayerbotAIConfig->sightDistance) <= 1) return true; - bool almostFullMana = AI_VALUE2(bool, "has mana", "self target") && - AI_VALUE2(uint8, "mana", "self target") < 85; - - // high pressure - if (AI_VALUE(uint8, "balance") <= 50 && almostFullMana) + if (AI_VALUE2(uint8, "health", "party member to heal") < sPlayerbotAIConfig->almostFullHealth) return false; - bool highMana = AI_VALUE2(bool, "has mana", "self target") && - AI_VALUE2(uint8, "mana", "self target") < sPlayerbotAIConfig->highMana; + // special check for resto druid (dont remove tree of life frequently) + if (bot->GetAura(33891)) + { + LastSpellCast& lastSpell = botAI->GetAiObjectContext()->GetValue("last spell cast")->Get(); + if (lastSpell.timer + 5 > time(nullptr)) + return false; + } - if (AI_VALUE(uint8, "balance") <= 100 && highMana) - return false; - - bool mediumMana = AI_VALUE2(bool, "has mana", "self target") && - AI_VALUE2(uint8, "mana", "self target") < sPlayerbotAIConfig->mediumMana; + int manaThreshold; + int balance = AI_VALUE(uint8, "balance"); + // higher threshold in higher pressure + if (balance <= 50) + manaThreshold = 85; + else if (balance <= 100) + manaThreshold = sPlayerbotAIConfig->highMana; + else + manaThreshold = sPlayerbotAIConfig->mediumMana; - if (mediumMana) + if (AI_VALUE2(bool, "has mana", "self target") && AI_VALUE2(uint8, "mana", "self target") < manaThreshold) return false; return true; diff --git a/src/strategy/warlock/GenericWarlockNonCombatStrategy.cpp b/src/strategy/warlock/GenericWarlockNonCombatStrategy.cpp index 7ed900cf..a46346fe 100644 --- a/src/strategy/warlock/GenericWarlockNonCombatStrategy.cpp +++ b/src/strategy/warlock/GenericWarlockNonCombatStrategy.cpp @@ -55,7 +55,7 @@ private: { return new ActionNode("summon felhunter", /*P*/ nullptr, - /*A*/ NextAction::array(0, new NextAction("summon voidwalker"), nullptr), + /*A*/ NextAction::array(0, new NextAction("summon succubus"), nullptr), /*C*/ nullptr); } static ActionNode* summon_felguard([[maybe_unused]] PlayerbotAI* botAI) From 55cd5ab923f737433a1bc8c9e4b954e6baacc7b1 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Fri, 4 Oct 2024 01:50:21 +0800 Subject: [PATCH 06/40] [AI] Remove reset after near teleport --- src/PlayerbotAI.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 7b6b9887..a0a2c39b 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -703,8 +703,7 @@ void PlayerbotAI::HandleTeleportAck() p << (uint32)0; // supposed to be flags? not used currently p << (uint32)0; // time - not currently used bot->GetSession()->HandleMoveTeleportAck(p); - } - SetNextCheckDelay(urand(1000, 3000)); + }; } if (bot->IsBeingTeleportedFar()) { @@ -712,13 +711,13 @@ void PlayerbotAI::HandleTeleportAck() { bot->GetSession()->HandleMoveWorldportAck(); } - SetNextCheckDelay(urand(2000, 5000)); + // SetNextCheckDelay(urand(2000, 5000)); if (sPlayerbotAIConfig->applyInstanceStrategies) ApplyInstanceStrategies(bot->GetMapId(), true); + Reset(); } SetNextCheckDelay(sPlayerbotAIConfig->globalCoolDown); - Reset(); } void PlayerbotAI::Reset(bool full) @@ -1198,7 +1197,7 @@ void PlayerbotAI::HandleBotOutgoingPacket(WorldPacket const& packet) void PlayerbotAI::SpellInterrupted(uint32 spellid) { - for (uint8 type = CURRENT_MELEE_SPELL; type < CURRENT_CHANNELED_SPELL; type++) + for (uint8 type = CURRENT_MELEE_SPELL; type <= CURRENT_CHANNELED_SPELL; type++) { Spell* spell = bot->GetCurrentSpell((CurrentSpellTypes)type); if (!spell) @@ -1206,8 +1205,8 @@ void PlayerbotAI::SpellInterrupted(uint32 spellid) if (spell->GetSpellInfo()->Id == spellid) bot->InterruptSpell((CurrentSpellTypes)type); } - LastSpellCast& lastSpell = aiObjectContext->GetValue("last spell cast")->Get(); - lastSpell.id = 0; + // LastSpellCast& lastSpell = aiObjectContext->GetValue("last spell cast")->Get(); + // lastSpell.id = 0; } int32 PlayerbotAI::CalculateGlobalCooldown(uint32 spellid) From ff68a92fe944210e82196d571299a1f2b52954ef Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Fri, 4 Oct 2024 01:50:38 +0800 Subject: [PATCH 07/40] [Movement] Modify combat reach calculation --- src/strategy/actions/MovementActions.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/strategy/actions/MovementActions.cpp b/src/strategy/actions/MovementActions.cpp index e07fc79a..569d7fcc 100644 --- a/src/strategy/actions/MovementActions.cpp +++ b/src/strategy/actions/MovementActions.cpp @@ -827,11 +827,12 @@ bool MovementAction::ReachCombatTo(Unit* target, float distance) float shortenTo = distance; // Avoid walking too far when moving towards each other - if (bot->GetDistance(tx, ty, tz) >= 10.0f) - shortenTo = std::max(distance, bot->GetDistance(tx, ty, tz) / 2); + float disToGo = bot->GetExactDist(tx, ty, tz) - distance; + if (disToGo >= 10.0f) + shortenTo = disToGo / 2 + distance; - if (bot->GetExactDist(tx, ty, tz) <= shortenTo) - return false; + // if (bot->GetExactDist(tx, ty, tz) <= shortenTo) + // return false; path.ShortenPathUntilDist(G3D::Vector3(tx, ty, tz), shortenTo); G3D::Vector3 endPos = path.GetPath().back(); @@ -2313,7 +2314,7 @@ bool TankFaceAction::Execute(Event event) if (!bot->GetGroup()) return false; - if (!bot->IsWithinMeleeRange(target)) + if (!bot->IsWithinMeleeRange(target) || target->isMoving()) return false; if (!AI_VALUE2(bool, "has aggro", "current target")) @@ -2513,7 +2514,7 @@ bool SetBehindTargetAction::Execute(Event event) if (target->GetVictim() == bot) return false; - if (!bot->IsWithinMeleeRange(target)) + if (!bot->IsWithinMeleeRange(target) || target->isMoving()) return false; float deltaAngle = Position::NormalizeOrientation(target->GetOrientation() - target->GetAngle(bot)); From a0865cca140d5e610212bb8137a598fe9df41ca5 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Fri, 4 Oct 2024 14:14:00 +0800 Subject: [PATCH 08/40] [Spell] SpellInfo check on current spell --- src/PlayerbotAI.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index a0a2c39b..1200e050 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -317,13 +317,13 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal) Spell* currentSpell = bot->GetCurrentSpell(CURRENT_GENERIC_SPELL); if (!currentSpell) currentSpell = bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL); - if (currentSpell && currentSpell->getState() == SPELL_STATE_PREPARING) + if (currentSpell && currentSpell->GetSpellInfo() && currentSpell->getState() == SPELL_STATE_PREPARING) { const SpellInfo* spellInfo = currentSpell->GetSpellInfo(); // interrupt if target is dead if (currentSpell->m_targets.GetUnitTarget() && !currentSpell->m_targets.GetUnitTarget()->IsAlive() && - spellInfo && !spellInfo->IsAllowingDeadTarget()) + !spellInfo->IsAllowingDeadTarget()) { InterruptSpell(); SetNextCheckDelay(sPlayerbotAIConfig->reactDelay); From 37c5c1dcdf2e759db49c02182a105917632e2fec Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Fri, 4 Oct 2024 14:38:09 +0800 Subject: [PATCH 09/40] [Spell] Fix alternative healing spell --- src/strategy/druid/HealDruidStrategy.cpp | 4 ++-- src/strategy/priest/HealPriestStrategy.cpp | 19 ++++++++----------- src/strategy/priest/HolyPriestStrategy.cpp | 16 ++++++++-------- src/strategy/priest/PriestActions.h | 9 ++++++++- src/strategy/priest/PriestAiObjectContext.cpp | 2 +- .../priest/PriestNonCombatStrategy.cpp | 2 +- ...PriestNonCombatStrategyActionNodeFactory.h | 4 ++-- src/strategy/shaman/GenericShamanStrategy.cpp | 8 ++++---- src/strategy/shaman/HealShamanStrategy.cpp | 4 ++-- src/strategy/shaman/ShamanActions.h | 4 ++-- src/strategy/shaman/ShamanAiObjectContext.cpp | 2 +- .../shaman/ShamanNonCombatStrategy.cpp | 2 +- src/strategy/triggers/TriggerContext.h | 4 ++-- 13 files changed, 42 insertions(+), 38 deletions(-) diff --git a/src/strategy/druid/HealDruidStrategy.cpp b/src/strategy/druid/HealDruidStrategy.cpp index b1a28c6e..ec33dea3 100644 --- a/src/strategy/druid/HealDruidStrategy.cpp +++ b/src/strategy/druid/HealDruidStrategy.cpp @@ -78,11 +78,11 @@ void HealDruidStrategy::InitTriggers(std::vector& triggers) triggers.push_back( new TriggerNode("party member critical health", - NextAction::array(0, new NextAction("nature's swiftness", ACTION_CRITICAL_HEAL + 4), NULL))); + NextAction::array(0, new NextAction("nature's swiftness", ACTION_CRITICAL_HEAL + 4), nullptr))); triggers.push_back( new TriggerNode("medium group heal occasion", - NextAction::array(0, new NextAction("tranquility", ACTION_CRITICAL_HEAL + 5), NULL))); + NextAction::array(0, new NextAction("tranquility", ACTION_CRITICAL_HEAL + 5), nullptr))); // LOW triggers.push_back( diff --git a/src/strategy/priest/HealPriestStrategy.cpp b/src/strategy/priest/HealPriestStrategy.cpp index 25f8e8bd..52c12df6 100644 --- a/src/strategy/priest/HealPriestStrategy.cpp +++ b/src/strategy/priest/HealPriestStrategy.cpp @@ -29,15 +29,14 @@ void HealPriestStrategy::InitTriggers(std::vector& triggers) // triggers.push_back(new TriggerNode( // "medium aoe heal", // NextAction::array(0, - // new NextAction("circle of healing", ACTION_MEDIUM_HEAL + 8), + // new NextAction("circle of healing on party", ACTION_MEDIUM_HEAL + 8), // // new NextAction("power word: shield on almost full health below", ACTION_MEDIUM_HEAL + 7), // NULL))); triggers.push_back(new TriggerNode( - "group heal occasion", - NextAction::array(0, new NextAction("circle of healing", ACTION_MEDIUM_HEAL + 8), - new NextAction("power word: shield on almost full health below", ACTION_MEDIUM_HEAL + 7), - NULL))); + "group heal setting", + NextAction::array(0, new NextAction("power word: shield on almost full health below", ACTION_MEDIUM_HEAL + 7), + nullptr))); triggers.push_back(new TriggerNode( "medium group heal occasion", @@ -49,25 +48,23 @@ void HealPriestStrategy::InitTriggers(std::vector& triggers) NextAction::array(0, new NextAction("power word: shield on party", ACTION_CRITICAL_HEAL + 6), new NextAction("penance on party", ACTION_CRITICAL_HEAL + 4), new NextAction("flash heal on party", ACTION_CRITICAL_HEAL + 3), - new NextAction("prayer of mending on party", ACTION_CRITICAL_HEAL + 2), NULL))); + new NextAction("prayer of mending on party", ACTION_CRITICAL_HEAL + 2), nullptr))); triggers.push_back( new TriggerNode("party member low health", NextAction::array(0, new NextAction("power word: shield on party", ACTION_MEDIUM_HEAL + 4), new NextAction("penance on party", ACTION_MEDIUM_HEAL + 2), - new NextAction("circle of healing", ACTION_MEDIUM_HEAL + 2), new NextAction("prayer of mending on party", ACTION_MEDIUM_HEAL + 1), - new NextAction("flash heal on party", ACTION_MEDIUM_HEAL + 0), NULL))); + new NextAction("flash heal on party", ACTION_MEDIUM_HEAL + 0), nullptr))); triggers.push_back( new TriggerNode("party member medium health", NextAction::array(0, new NextAction("power word: shield on party", ACTION_LIGHT_HEAL + 9), new NextAction("penance on party", ACTION_LIGHT_HEAL + 7), - new NextAction("circle of healing", ACTION_LIGHT_HEAL + 7), new NextAction("prayer of mending on party", ACTION_LIGHT_HEAL + 6), new NextAction("flash heal on party", ACTION_LIGHT_HEAL + 5), // new NextAction("renew on party", ACTION_LIGHT_HEAL + 8), - NULL))); + nullptr))); triggers.push_back( new TriggerNode("party member almost full health", @@ -86,7 +83,7 @@ void HealPriestStrategy::InitTriggers(std::vector& triggers) NextAction::array(0, new NextAction("reach party member to heal", ACTION_CRITICAL_HEAL + 10), nullptr))); // triggers.push_back(new TriggerNode("medium aoe heal", NextAction::array(0, new NextAction("prayer of // mending", 49.0f), nullptr))); triggers.push_back(new TriggerNode("medium aoe heal", NextAction::array(0, new - // NextAction("circle of healing", 48.0f), nullptr))); triggers.push_back(new TriggerNode("binding heal", + // NextAction("circle of healing on party", 48.0f), nullptr))); triggers.push_back(new TriggerNode("binding heal", // NextAction::array(0, new NextAction("binding heal", 52.0f), nullptr))); triggers.push_back(new TriggerNode("low // mana", NextAction::array(0, new NextAction("shadowfiend", ACTION_HIGH), nullptr))); diff --git a/src/strategy/priest/HolyPriestStrategy.cpp b/src/strategy/priest/HolyPriestStrategy.cpp index 40f34a28..de29d7ea 100644 --- a/src/strategy/priest/HolyPriestStrategy.cpp +++ b/src/strategy/priest/HolyPriestStrategy.cpp @@ -63,8 +63,8 @@ void HolyHealPriestStrategy::InitTriggers(std::vector& triggers) GenericPriestStrategy::InitTriggers(triggers); triggers.push_back( - new TriggerNode("group heal occasion", - NextAction::array(0, new NextAction("circle of healing", ACTION_MEDIUM_HEAL + 8), NULL))); + new TriggerNode("group heal setting", + NextAction::array(0, new NextAction("circle of healing on party", ACTION_MEDIUM_HEAL + 8), nullptr))); triggers.push_back(new TriggerNode( "medium group heal occasion", @@ -77,28 +77,28 @@ void HolyHealPriestStrategy::InitTriggers(std::vector& triggers) new NextAction("guardian spirit on party", ACTION_CRITICAL_HEAL + 6), new NextAction("power word: shield on party", ACTION_CRITICAL_HEAL + 5), new NextAction("flash heal on party", ACTION_CRITICAL_HEAL + 3), - new NextAction("prayer of mending on party", ACTION_CRITICAL_HEAL + 2), NULL))); + new NextAction("prayer of mending on party", ACTION_CRITICAL_HEAL + 2), nullptr))); triggers.push_back( new TriggerNode("party member low health", - NextAction::array(0, new NextAction("circle of healing", ACTION_MEDIUM_HEAL + 4), + NextAction::array(0, new NextAction("circle of healing on party", ACTION_MEDIUM_HEAL + 4), new NextAction("greater heal on party", ACTION_MEDIUM_HEAL + 3), new NextAction("prayer of mending on party", ACTION_MEDIUM_HEAL + 2), - new NextAction("flash heal on party", ACTION_MEDIUM_HEAL + 1), NULL))); + new NextAction("flash heal on party", ACTION_MEDIUM_HEAL + 1), nullptr))); triggers.push_back( new TriggerNode("party member medium health", - NextAction::array(0, new NextAction("circle of healing", ACTION_LIGHT_HEAL + 7), + NextAction::array(0, new NextAction("circle of healing on party", ACTION_LIGHT_HEAL + 7), new NextAction("prayer of mending on party", ACTION_LIGHT_HEAL + 6), new NextAction("flash heal on party", ACTION_LIGHT_HEAL + 5), // new NextAction("renew on party", ACTION_LIGHT_HEAL + 8), - NULL))); + nullptr))); triggers.push_back( new TriggerNode("party member almost full health", NextAction::array(0, new NextAction("renew on party", ACTION_LIGHT_HEAL + 2), // new NextAction("flash heal on party", ACTION_LIGHT_HEAL + 1), - NULL))); + nullptr))); triggers.push_back(new TriggerNode( "party member to heal out of spell range", diff --git a/src/strategy/priest/PriestActions.h b/src/strategy/priest/PriestActions.h index d5af0509..bffea2bc 100644 --- a/src/strategy/priest/PriestActions.h +++ b/src/strategy/priest/PriestActions.h @@ -55,7 +55,14 @@ HEAL_PARTY_ACTION(CastRenewOnPartyAction, "renew", 15.0f, HealingManaEfficiency: HEAL_PARTY_ACTION(CastPrayerOfMendingAction, "prayer of mending", 15.0f, HealingManaEfficiency::MEDIUM); HEAL_PARTY_ACTION(CastBindingHealAction, "binding heal", 15.0f, HealingManaEfficiency::MEDIUM); HEAL_PARTY_ACTION(CastPrayerOfHealingAction, "prayer of healing", 15.0f, HealingManaEfficiency::MEDIUM); -AOE_HEAL_ACTION(CastCircleOfHealingAction, "circle of healing", 15.0f, HealingManaEfficiency::HIGH); +// AOE_HEAL_ACTION(CastCircleOfHealingAction, "circle of healing", 15.0f, HealingManaEfficiency::HIGH); +class CastCircleOfHealingAction : public HealPartyMemberAction +{ +public: + CastCircleOfHealingAction(PlayerbotAI* ai) : HealPartyMemberAction(ai, "circle of healing", 15.0f, HealingManaEfficiency::HIGH) + { + } +}; AOE_HEAL_ACTION(CastLightwellAction, "lightwell", 15.0f, HealingManaEfficiency::MEDIUM); SPELL_ACTION(CastSmiteAction, "smite"); diff --git a/src/strategy/priest/PriestAiObjectContext.cpp b/src/strategy/priest/PriestAiObjectContext.cpp index caf4eb79..d6c59233 100644 --- a/src/strategy/priest/PriestAiObjectContext.cpp +++ b/src/strategy/priest/PriestAiObjectContext.cpp @@ -195,7 +195,7 @@ public: creators["fade"] = &PriestAiObjectContextInternal::fade; creators["inner fire"] = &PriestAiObjectContextInternal::inner_fire; creators["resurrection"] = &PriestAiObjectContextInternal::resurrection; - creators["circle of healing"] = &PriestAiObjectContextInternal::circle_of_healing; + creators["circle of healing on party"] = &PriestAiObjectContextInternal::circle_of_healing; creators["psychic scream"] = &PriestAiObjectContextInternal::psychic_scream; creators["vampiric touch"] = &PriestAiObjectContextInternal::vampiric_touch; creators["vampiric touch on attacker"] = &PriestAiObjectContextInternal::vampiric_touch_on_attacker; diff --git a/src/strategy/priest/PriestNonCombatStrategy.cpp b/src/strategy/priest/PriestNonCombatStrategy.cpp index 5283fb75..be596054 100644 --- a/src/strategy/priest/PriestNonCombatStrategy.cpp +++ b/src/strategy/priest/PriestNonCombatStrategy.cpp @@ -55,7 +55,7 @@ void PriestNonCombatStrategy::InitTriggers(std::vector& triggers) NextAction::array(0, new NextAction("renew on party", ACTION_LIGHT_HEAL + 3), NULL))); triggers.push_back( - new TriggerNode("group heal occasion", NextAction::array(0, new NextAction("circle of healing", 27.0f), NULL))); + new TriggerNode("group heal setting", NextAction::array(0, new NextAction("circle of healing on party", 27.0f), NULL))); } void PriestBuffStrategy::InitTriggers(std::vector& triggers) diff --git a/src/strategy/priest/PriestNonCombatStrategyActionNodeFactory.h b/src/strategy/priest/PriestNonCombatStrategyActionNodeFactory.h index 84318fcb..c1084610 100644 --- a/src/strategy/priest/PriestNonCombatStrategyActionNodeFactory.h +++ b/src/strategy/priest/PriestNonCombatStrategyActionNodeFactory.h @@ -29,7 +29,7 @@ public: creators["lesser heal on party"] = &lesser_heal_on_party; creators["flash heal"] = &flash_heal; creators["flash heal on party"] = &flash_heal_on_party; - creators["circle of healing"] = &circle_of_healing; + creators["circle of healing on party"] = &circle_of_healing; creators["prayer of fortitude on party"] = &prayer_of_fortitude_on_party; creators["prayer of spirit on party"] = &prayer_of_spirit_on_party; } @@ -128,7 +128,7 @@ private: } static ActionNode* circle_of_healing(PlayerbotAI* ai) { - return new ActionNode("circle of healing", + return new ActionNode("circle of healing on party", /*P*/ NextAction::array(0, new NextAction("remove shadowform"), NULL), // /*A*/ NextAction::array(0, new NextAction("flash heal on party"), NULL), /*A*/ NULL, diff --git a/src/strategy/shaman/GenericShamanStrategy.cpp b/src/strategy/shaman/GenericShamanStrategy.cpp index d84b07a7..430a1bdc 100644 --- a/src/strategy/shaman/GenericShamanStrategy.cpp +++ b/src/strategy/shaman/GenericShamanStrategy.cpp @@ -18,7 +18,7 @@ public: creators["windfury weapon"] = &windfury_weapon; creators["lesser healing wave"] = &lesser_healing_wave; creators["lesser healing wave on party"] = &lesser_healing_wave_on_party; - creators["chain heal"] = &chain_heal; + creators["chain heal on party"] = &chain_heal; creators["riptide"] = &riptide; creators["riptide on party"] = &riptide_on_party; creators["earth shock"] = &earth_shock; @@ -75,9 +75,9 @@ private: static ActionNode* chain_heal([[maybe_unused]] PlayerbotAI* botAI) { - return new ActionNode("chain heal", + return new ActionNode("chain heal on party", /*P*/ nullptr, - /*A*/ NextAction::array(0, new NextAction("lesser healing wave"), nullptr), + /*A*/ NextAction::array(0, new NextAction("lesser healing wave on party"), nullptr), /*C*/ nullptr); } @@ -117,7 +117,7 @@ void GenericShamanStrategy::InitTriggers(std::vector& triggers) // triggers.push_back(new TriggerNode("party member medium health", NextAction::array(0, new NextAction("lesser // healing wave on party", 25.0f), nullptr))); triggers.push_back(new TriggerNode("party member low health", // NextAction::array(0, new NextAction("riptide on party", 25.0f), nullptr))); triggers.push_back(new - // TriggerNode("medium aoe heal", NextAction::array(0, new NextAction("chain heal", 27.0f), nullptr))); + // TriggerNode("medium aoe heal", NextAction::array(0, new NextAction("chain heal on party", 27.0f), nullptr))); // triggers.push_back(new TriggerNode("medium health", NextAction::array(0, new NextAction("lesser healing // wave", 26.0f), nullptr))); triggers.push_back(new TriggerNode("low health", NextAction::array(0, new // NextAction("riptide", 26.0f), nullptr))); diff --git a/src/strategy/shaman/HealShamanStrategy.cpp b/src/strategy/shaman/HealShamanStrategy.cpp index ff305559..17d4ff44 100644 --- a/src/strategy/shaman/HealShamanStrategy.cpp +++ b/src/strategy/shaman/HealShamanStrategy.cpp @@ -49,8 +49,8 @@ void HealShamanStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode("main hand weapon no imbue", NextAction::array(0, new NextAction("earthliving weapon", 22.0f), nullptr))); triggers.push_back(new TriggerNode( - "group heal occasion", - NextAction::array(0, new NextAction("riptide on party", 23.0f), new NextAction("chain heal", 22.0f), NULL))); + "group heal setting", + NextAction::array(0, new NextAction("riptide on party", 23.0f), new NextAction("chain heal on party", 22.0f), NULL))); triggers.push_back(new TriggerNode( "party member critical health", diff --git a/src/strategy/shaman/ShamanActions.h b/src/strategy/shaman/ShamanActions.h index 9d3b8b69..2d110e51 100644 --- a/src/strategy/shaman/ShamanActions.h +++ b/src/strategy/shaman/ShamanActions.h @@ -42,11 +42,11 @@ public: } }; -class CastChainHealAction : public CastAoeHealSpellAction +class CastChainHealAction : public HealPartyMemberAction { public: CastChainHealAction(PlayerbotAI* botAI) - : CastAoeHealSpellAction(botAI, "chain heal", 15.0f, HealingManaEfficiency::HIGH) + : HealPartyMemberAction(botAI, "chain heal", 15.0f, HealingManaEfficiency::HIGH) { } }; diff --git a/src/strategy/shaman/ShamanAiObjectContext.cpp b/src/strategy/shaman/ShamanAiObjectContext.cpp index bf9e76e3..193d292a 100644 --- a/src/strategy/shaman/ShamanAiObjectContext.cpp +++ b/src/strategy/shaman/ShamanAiObjectContext.cpp @@ -210,7 +210,7 @@ public: creators["lesser healing wave on party"] = &ShamanAiObjectContextInternal::lesser_healing_wave_on_party; creators["earth shield"] = &ShamanAiObjectContextInternal::earth_shield; creators["earth shield on party"] = &ShamanAiObjectContextInternal::earth_shield_on_party; - creators["chain heal"] = &ShamanAiObjectContextInternal::chain_heal; + creators["chain heal on party"] = &ShamanAiObjectContextInternal::chain_heal; creators["riptide"] = &ShamanAiObjectContextInternal::riptide; creators["riptide on party"] = &ShamanAiObjectContextInternal::riptide_on_party; creators["stormstrike"] = &ShamanAiObjectContextInternal::stormstrike; diff --git a/src/strategy/shaman/ShamanNonCombatStrategy.cpp b/src/strategy/shaman/ShamanNonCombatStrategy.cpp index f3380cb5..ad4dcb82 100644 --- a/src/strategy/shaman/ShamanNonCombatStrategy.cpp +++ b/src/strategy/shaman/ShamanNonCombatStrategy.cpp @@ -39,7 +39,7 @@ void ShamanNonCombatStrategy::InitTriggers(std::vector& triggers) new NextAction("lesser healing wave on party", 24.0f), NULL))); triggers.push_back( - new TriggerNode("group heal occasion", NextAction::array(0, new NextAction("chain heal", 27.0f), NULL))); + new TriggerNode("group heal setting", NextAction::array(0, new NextAction("chain heal on party", 27.0f), NULL))); triggers.push_back( new TriggerNode("cure poison", NextAction::array(0, new NextAction("cure poison", 21.0f), nullptr))); diff --git a/src/strategy/triggers/TriggerContext.h b/src/strategy/triggers/TriggerContext.h index 4bfea1d4..7baebc8d 100644 --- a/src/strategy/triggers/TriggerContext.h +++ b/src/strategy/triggers/TriggerContext.h @@ -139,7 +139,7 @@ public: creators["medium aoe heal"] = &TriggerContext::medium_aoe_heal; creators["almost full aoe heal"] = &TriggerContext::almost_full_aoe_heal; - creators["group heal occasion"] = &TriggerContext::group_heal_occasion; + creators["group heal setting"] = &TriggerContext::group_heal_occasion; creators["medium group heal occasion"] = &TriggerContext::medium_group_heal_occasion; creators["invalid target"] = &TriggerContext::invalid_target; creators["lfg proposal active"] = &TriggerContext::lfg_proposal_active; @@ -246,7 +246,7 @@ private: } static Trigger* group_heal_occasion(PlayerbotAI* ai) { - return new AoeInGroupTrigger(ai, "group heal occasion", "almost full"); + return new AoeInGroupTrigger(ai, "group heal setting", "almost full"); } static Trigger* medium_group_heal_occasion(PlayerbotAI* ai) { From db9b1115cb9de5f0cebbd8caad11ded1bd13b700 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Fri, 4 Oct 2024 18:56:44 +0800 Subject: [PATCH 10/40] [Spell] Priest prayer of mending buff owner --- src/strategy/priest/PriestActions.cpp | 35 +++++++++++++++++++++++++-- src/strategy/priest/PriestActions.h | 23 +++++++++++++++--- 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/src/strategy/priest/PriestActions.cpp b/src/strategy/priest/PriestActions.cpp index 3a94dafc..2364a5e2 100644 --- a/src/strategy/priest/PriestActions.cpp +++ b/src/strategy/priest/PriestActions.cpp @@ -18,7 +18,7 @@ bool CastRemoveShadowformAction::Execute(Event event) return true; } -Unit* CastPowerWordShieldOnAlmostFullHealthBelow::GetTarget() +Unit* CastPowerWordShieldOnAlmostFullHealthBelowAction::GetTarget() { Group* group = bot->GetGroup(); for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next()) @@ -47,7 +47,7 @@ Unit* CastPowerWordShieldOnAlmostFullHealthBelow::GetTarget() return nullptr; } -bool CastPowerWordShieldOnAlmostFullHealthBelow::isUseful() +bool CastPowerWordShieldOnAlmostFullHealthBelowAction::isUseful() { Group* group = bot->GetGroup(); for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next()) @@ -74,4 +74,35 @@ bool CastPowerWordShieldOnAlmostFullHealthBelow::isUseful() return true; } return false; +} + +Unit* CastPowerWordShieldOnNotFullAction::GetTarget() +{ + Group* group = bot->GetGroup(); + MinValueCalculator calc(100); + for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next()) + { + Player* player = gref->GetSource(); + if (!player) + continue; + if (player->isDead() || player->IsFullHealth()) + { + continue; + } + if (player->GetDistance2d(bot) > sPlayerbotAIConfig->spellDistance) + { + continue; + } + if (botAI->HasAnyAuraOf(player, "weakened soul", "power word: shield", nullptr)) + { + continue; + } + calc.probe(player->GetHealthPct(), player); + } + return (Unit*)calc.param; +} + +bool CastPowerWordShieldOnNotFullAction::isUseful() +{ + return GetTarget(); } \ No newline at end of file diff --git a/src/strategy/priest/PriestActions.h b/src/strategy/priest/PriestActions.h index bffea2bc..c84bfc7d 100644 --- a/src/strategy/priest/PriestActions.h +++ b/src/strategy/priest/PriestActions.h @@ -52,7 +52,13 @@ HEAL_PARTY_ACTION(CastGreaterHealOnPartyAction, "greater heal", 50.0f, HealingMa HEAL_PARTY_ACTION(CastPowerWordShieldOnPartyAction, "power word: shield", 15.0f, HealingManaEfficiency::VERY_HIGH); HEAL_PARTY_ACTION(CastFlashHealOnPartyAction, "flash heal", 15.0f, HealingManaEfficiency::LOW); HEAL_PARTY_ACTION(CastRenewOnPartyAction, "renew", 15.0f, HealingManaEfficiency::VERY_HIGH); -HEAL_PARTY_ACTION(CastPrayerOfMendingAction, "prayer of mending", 15.0f, HealingManaEfficiency::MEDIUM); +// HEAL_PARTY_ACTION(CastPrayerOfMendingAction, "prayer of mending", 10.0f, HealingManaEfficiency::HIGH); +class CastPrayerOfMendingAction : public HealPartyMemberAction +{ +public: + CastPrayerOfMendingAction(PlayerbotAI* botAI) : HealPartyMemberAction(botAI, "prayer of mending", 10.0f, HealingManaEfficiency::HIGH, false) {} +}; + HEAL_PARTY_ACTION(CastBindingHealAction, "binding heal", 15.0f, HealingManaEfficiency::MEDIUM); HEAL_PARTY_ACTION(CastPrayerOfHealingAction, "prayer of healing", 15.0f, HealingManaEfficiency::MEDIUM); // AOE_HEAL_ACTION(CastCircleOfHealingAction, "circle of healing", 15.0f, HealingManaEfficiency::HIGH); @@ -169,10 +175,10 @@ public: virtual std::string const GetTargetName() { return "current target"; } }; -class CastPowerWordShieldOnAlmostFullHealthBelow : public HealPartyMemberAction +class CastPowerWordShieldOnAlmostFullHealthBelowAction : public HealPartyMemberAction { public: - CastPowerWordShieldOnAlmostFullHealthBelow(PlayerbotAI* ai) + CastPowerWordShieldOnAlmostFullHealthBelowAction(PlayerbotAI* ai) : HealPartyMemberAction(ai, "power word: shield", 15.0f, HealingManaEfficiency::HIGH) { } @@ -180,6 +186,17 @@ public: Unit* GetTarget() override; }; +class CastPowerWordShieldOnNotFullAction : public HealPartyMemberAction +{ +public: + CastPowerWordShieldOnNotFullAction(PlayerbotAI* ai) + : HealPartyMemberAction(ai, "power word: shield", 5.0f, HealingManaEfficiency::HIGH) + { + } + bool isUseful() override; + Unit* GetTarget() override; +}; + class CastMindSearAction : public CastSpellAction { public: From 21e17e163bba6bfd068ab4599518bafaf41c50b1 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Fri, 4 Oct 2024 20:48:29 +0800 Subject: [PATCH 11/40] [Spell] Healer spell enhancement --- src/strategy/Engine.cpp | 8 ++-- src/strategy/actions/GenericActions.cpp | 2 +- src/strategy/actions/GenericSpellActions.cpp | 4 +- src/strategy/actions/GenericSpellActions.h | 6 +-- src/strategy/druid/DruidActions.cpp | 31 +++++++++++++ src/strategy/druid/DruidActions.h | 12 +++++ src/strategy/druid/DruidAiObjectContext.cpp | 2 + src/strategy/druid/GenericDruidStrategy.cpp | 11 ++--- src/strategy/druid/HealDruidStrategy.cpp | 44 ++++++++++++------- src/strategy/paladin/HealPaladinStrategy.cpp | 2 +- src/strategy/paladin/TankPaladinStrategy.cpp | 2 +- src/strategy/priest/HealPriestStrategy.cpp | 30 ++++++++----- src/strategy/priest/HolyPriestStrategy.cpp | 30 ++++++++----- src/strategy/priest/PriestAiObjectContext.cpp | 8 +++- src/strategy/triggers/TriggerContext.h | 4 +- 15 files changed, 137 insertions(+), 59 deletions(-) diff --git a/src/strategy/Engine.cpp b/src/strategy/Engine.cpp index d298aaf5..a9569a20 100644 --- a/src/strategy/Engine.cpp +++ b/src/strategy/Engine.cpp @@ -202,9 +202,9 @@ bool Engine::DoNextAction(Unit* unit, uint32 depth, bool minimal) { LogAction("A:%s - PREREQ", action->getName().c_str()); - if (MultiplyAndPush(actionNode->getPrerequisites(), relevance + 0.02, false, event, "prereq")) + if (MultiplyAndPush(actionNode->getPrerequisites(), relevance + 0.002f, false, event, "prereq")) { - PushAgain(actionNode, relevance + 0.01, event); + PushAgain(actionNode, relevance + 0.001f, event); continue; } } @@ -226,7 +226,7 @@ bool Engine::DoNextAction(Unit* unit, uint32 depth, bool minimal) else { LogAction("A:%s - FAILED", action->getName().c_str()); - MultiplyAndPush(actionNode->getAlternatives(), relevance + 0.03, false, event, "alt"); + MultiplyAndPush(actionNode->getAlternatives(), relevance + 0.003f, false, event, "alt"); } } else @@ -246,7 +246,7 @@ bool Engine::DoNextAction(Unit* unit, uint32 depth, bool minimal) botAI->TellMasterNoFacing(out); } LogAction("A:%s - IMPOSSIBLE", action->getName().c_str()); - MultiplyAndPush(actionNode->getAlternatives(), relevance + 0.03, false, event, "alt"); + MultiplyAndPush(actionNode->getAlternatives(), relevance + 0.003f, false, event, "alt"); } } else diff --git a/src/strategy/actions/GenericActions.cpp b/src/strategy/actions/GenericActions.cpp index 9682d2ef..84997212 100644 --- a/src/strategy/actions/GenericActions.cpp +++ b/src/strategy/actions/GenericActions.cpp @@ -38,7 +38,7 @@ bool TogglePetSpellAutoCastAction::Execute(Event event) bool shouldApply = true; // imp's spell, felhunte's intelligence, cat stealth if (spellId == 4511 || spellId == 1742 || spellId == 54424 || spellId == 57564 || spellId == 57565 || - spellId == 57566 || spellId == 57567 || spellId == 24450 || spellId == 53477) + spellId == 57566 || spellId == 57567 || spellId == 24450) { shouldApply = false; } diff --git a/src/strategy/actions/GenericSpellActions.cpp b/src/strategy/actions/GenericSpellActions.cpp index 9285f20a..749bed14 100644 --- a/src/strategy/actions/GenericSpellActions.cpp +++ b/src/strategy/actions/GenericSpellActions.cpp @@ -196,8 +196,8 @@ bool CastEnchantItemAction::isPossible() } CastHealingSpellAction::CastHealingSpellAction(PlayerbotAI* botAI, std::string const spell, uint8 estAmount, - HealingManaEfficiency manaEfficiency) - : CastAuraSpellAction(botAI, spell, true), estAmount(estAmount), manaEfficiency(manaEfficiency) + HealingManaEfficiency manaEfficiency, bool isOwner) + : CastAuraSpellAction(botAI, spell, isOwner), estAmount(estAmount), manaEfficiency(manaEfficiency) { range = botAI->GetRange("heal"); } diff --git a/src/strategy/actions/GenericSpellActions.h b/src/strategy/actions/GenericSpellActions.h index 2ab49bb0..be1dc200 100644 --- a/src/strategy/actions/GenericSpellActions.h +++ b/src/strategy/actions/GenericSpellActions.h @@ -129,7 +129,7 @@ class CastHealingSpellAction : public CastAuraSpellAction { public: CastHealingSpellAction(PlayerbotAI* botAI, std::string const spell, uint8 estAmount = 15.0f, - HealingManaEfficiency manaEfficiency = HealingManaEfficiency::MEDIUM); + HealingManaEfficiency manaEfficiency = HealingManaEfficiency::MEDIUM, bool isOwner = true); std::string const GetTargetName() override { return "self target"; } bool isUseful() override; @@ -177,8 +177,8 @@ class HealPartyMemberAction : public CastHealingSpellAction, public PartyMemberA { public: HealPartyMemberAction(PlayerbotAI* botAI, std::string const spell, uint8 estAmount = 15.0f, - HealingManaEfficiency manaEfficiency = HealingManaEfficiency::MEDIUM) - : CastHealingSpellAction(botAI, spell, estAmount, manaEfficiency), PartyMemberActionNameSupport(spell) + HealingManaEfficiency manaEfficiency = HealingManaEfficiency::MEDIUM, bool isOwner = true) + : CastHealingSpellAction(botAI, spell, estAmount, manaEfficiency, isOwner), PartyMemberActionNameSupport(spell) { } diff --git a/src/strategy/druid/DruidActions.cpp b/src/strategy/druid/DruidActions.cpp index 739ac941..1743a339 100644 --- a/src/strategy/druid/DruidActions.cpp +++ b/src/strategy/druid/DruidActions.cpp @@ -47,4 +47,35 @@ bool CastRebirthAction::isUseful() { return CastSpellAction::isUseful() && AI_VALUE2(float, "distance", GetTargetName()) <= sPlayerbotAIConfig->spellDistance; +} + +Unit* CastRejuvenationOnNotFullAction::GetTarget() +{ + Group* group = bot->GetGroup(); + MinValueCalculator calc(100); + for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next()) + { + Player* player = gref->GetSource(); + if (!player) + continue; + if (player->isDead() || player->IsFullHealth()) + { + continue; + } + if (player->GetDistance2d(bot) > sPlayerbotAIConfig->spellDistance) + { + continue; + } + if (botAI->HasAura("rejuvenation", player)) + { + continue; + } + calc.probe(player->GetHealthPct(), player); + } + return (Unit*)calc.param; +} + +bool CastRejuvenationOnNotFullAction::isUseful() +{ + return GetTarget(); } \ No newline at end of file diff --git a/src/strategy/druid/DruidActions.h b/src/strategy/druid/DruidActions.h index 749ba3ea..ee90788a 100644 --- a/src/strategy/druid/DruidActions.h +++ b/src/strategy/druid/DruidActions.h @@ -311,4 +311,16 @@ public: CastEnrageAction(PlayerbotAI* ai) : CastBuffSpellAction(ai, "enrage") {} }; + +class CastRejuvenationOnNotFullAction : public HealPartyMemberAction +{ +public: + CastRejuvenationOnNotFullAction(PlayerbotAI* ai) + : HealPartyMemberAction(ai, "rejuvenation", 5.0f, HealingManaEfficiency::VERY_HIGH) + { + } + bool isUseful() override; + Unit* GetTarget() override; +}; + #endif diff --git a/src/strategy/druid/DruidAiObjectContext.cpp b/src/strategy/druid/DruidAiObjectContext.cpp index a39fbc5b..d9882c76 100644 --- a/src/strategy/druid/DruidAiObjectContext.cpp +++ b/src/strategy/druid/DruidAiObjectContext.cpp @@ -209,6 +209,7 @@ public: creators["healing touch"] = &DruidAiObjectContextInternal::healing_touch; creators["regrowth on party"] = &DruidAiObjectContextInternal::regrowth_on_party; creators["rejuvenation on party"] = &DruidAiObjectContextInternal::rejuvenation_on_party; + creators["rejuvenation on not full"] = &DruidAiObjectContextInternal::rejuvenation_on_not_full; creators["healing touch on party"] = &DruidAiObjectContextInternal::healing_touch_on_party; creators["rebirth"] = &DruidAiObjectContextInternal::rebirth; creators["revive"] = &DruidAiObjectContextInternal::revive; @@ -295,6 +296,7 @@ private: static Action* healing_touch(PlayerbotAI* botAI) { return new CastHealingTouchAction(botAI); } static Action* regrowth_on_party(PlayerbotAI* botAI) { return new CastRegrowthOnPartyAction(botAI); } static Action* rejuvenation_on_party(PlayerbotAI* botAI) { return new CastRejuvenationOnPartyAction(botAI); } + static Action* rejuvenation_on_not_full(PlayerbotAI* botAI) { return new CastRejuvenationOnNotFullAction(botAI); } static Action* healing_touch_on_party(PlayerbotAI* botAI) { return new CastHealingTouchOnPartyAction(botAI); } static Action* rebirth(PlayerbotAI* botAI) { return new CastRebirthAction(botAI); } static Action* revive(PlayerbotAI* botAI) { return new CastReviveAction(botAI); } diff --git a/src/strategy/druid/GenericDruidStrategy.cpp b/src/strategy/druid/GenericDruidStrategy.cpp index c3dacdb3..cbe3ad7a 100644 --- a/src/strategy/druid/GenericDruidStrategy.cpp +++ b/src/strategy/druid/GenericDruidStrategy.cpp @@ -164,9 +164,10 @@ void DruidAssistDpsStrategy::InitTriggers(std::vector& triggers) new NextAction("starfire", ACTION_DEFAULT), nullptr))); - triggers.push_back( - new TriggerNode("medium aoe and healer should attack", - NextAction::array(0, - new NextAction("hurricane", ACTION_DEFAULT + 0.7f), - nullptr))); + // long cast time + // triggers.push_back( + // new TriggerNode("medium aoe and healer should attack", + // NextAction::array(0, + // new NextAction("hurricane", ACTION_DEFAULT + 0.7f), + // nullptr))); } diff --git a/src/strategy/druid/HealDruidStrategy.cpp b/src/strategy/druid/HealDruidStrategy.cpp index ec33dea3..b1e2f125 100644 --- a/src/strategy/druid/HealDruidStrategy.cpp +++ b/src/strategy/druid/HealDruidStrategy.cpp @@ -70,8 +70,8 @@ void HealDruidStrategy::InitTriggers(std::vector& triggers) NextAction::array(0, new NextAction("tree form", ACTION_CRITICAL_HEAL + 4.1f), new NextAction("swiftmend on party", ACTION_CRITICAL_HEAL + 4), - new NextAction("wild growth on party", ACTION_CRITICAL_HEAL + 3), - new NextAction("regrowth on party", ACTION_CRITICAL_HEAL + 2), + new NextAction("regrowth on party", ACTION_CRITICAL_HEAL + 3), + new NextAction("wild growth on party", ACTION_CRITICAL_HEAL + 2), new NextAction("nourish on party", ACTION_CRITICAL_HEAL + 1), // new NextAction("healing touch on party", ACTION_CRITICAL_HEAL + 0), nullptr))); @@ -80,36 +80,46 @@ void HealDruidStrategy::InitTriggers(std::vector& triggers) new TriggerNode("party member critical health", NextAction::array(0, new NextAction("nature's swiftness", ACTION_CRITICAL_HEAL + 4), nullptr))); + triggers.push_back(new TriggerNode( + "group heal setting", + NextAction::array(0, + new NextAction("tree form", ACTION_MEDIUM_HEAL + 2.3f), + new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 2.2f), + new NextAction("rejuvenation on not full", ACTION_MEDIUM_HEAL + 2.1f), + nullptr))); + triggers.push_back( - new TriggerNode("medium group heal occasion", - NextAction::array(0, new NextAction("tranquility", ACTION_CRITICAL_HEAL + 5), nullptr))); + new TriggerNode("medium group heal setting", + NextAction::array(0, + new NextAction("tree form", ACTION_CRITICAL_HEAL + 0.6f), + new NextAction("tranquility", ACTION_CRITICAL_HEAL + 0.5f), nullptr))); // LOW triggers.push_back( new TriggerNode("party member low health", - NextAction::array(0, new NextAction("tree form", ACTION_MEDIUM_HEAL + 9.1f), - new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 9), - new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 8), - new NextAction("swiftmend on party", ACTION_MEDIUM_HEAL + 7), - new NextAction("nourish on party", ACTION_MEDIUM_HEAL + 6), + NextAction::array(0, new NextAction("tree form", ACTION_MEDIUM_HEAL + 1.5f), + new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 1.4f), + new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 1.3f), + new NextAction("swiftmend on party", ACTION_MEDIUM_HEAL + 1.2), + new NextAction("nourish on party", ACTION_MEDIUM_HEAL + 1.1f), nullptr))); // MEDIUM triggers.push_back( new TriggerNode("party member medium health", NextAction::array(0, - new NextAction("tree form", ACTION_MEDIUM_HEAL + 4.1f), - new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 4), - new NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 3), - new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 2), - new NextAction("nourish on party", ACTION_MEDIUM_HEAL + 1), nullptr))); + new NextAction("tree form", ACTION_MEDIUM_HEAL + 0.5f), + new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 0.4f), + new NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 0.3f), + new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 0.2f), + new NextAction("nourish on party", ACTION_MEDIUM_HEAL + 0.1f), nullptr))); // almost full triggers.push_back( new TriggerNode("party member almost full health", - NextAction::array(0, new NextAction("wild growth on party", ACTION_LIGHT_HEAL + 3), - new NextAction("rejuvenation on party", ACTION_LIGHT_HEAL + 2), - new NextAction("regrowth on party", ACTION_LIGHT_HEAL + 1), nullptr))); + NextAction::array(0, new NextAction("wild growth on party", ACTION_LIGHT_HEAL + 0.3f), + new NextAction("rejuvenation on party", ACTION_LIGHT_HEAL + 0.2f), + new NextAction("regrowth on party", ACTION_LIGHT_HEAL + 0.1f), nullptr))); triggers.push_back( new TriggerNode("medium mana", NextAction::array(0, new NextAction("innervate", ACTION_HIGH + 5), nullptr))); diff --git a/src/strategy/paladin/HealPaladinStrategy.cpp b/src/strategy/paladin/HealPaladinStrategy.cpp index 42517630..5f1767dd 100644 --- a/src/strategy/paladin/HealPaladinStrategy.cpp +++ b/src/strategy/paladin/HealPaladinStrategy.cpp @@ -49,7 +49,7 @@ void HealPaladinStrategy::InitTriggers(std::vector& triggers) NextAction::array(0, new NextAction("reach party member to heal", ACTION_EMERGENCY + 3), nullptr))); triggers.push_back( - new TriggerNode("medium group heal occasion", + new TriggerNode("medium group heal setting", NextAction::array(0, new NextAction("divine sacrifice", ACTION_CRITICAL_HEAL + 5), new NextAction("avenging wrath", ACTION_HIGH + 4), nullptr))); diff --git a/src/strategy/paladin/TankPaladinStrategy.cpp b/src/strategy/paladin/TankPaladinStrategy.cpp index 1cb2c5c4..25e761b7 100644 --- a/src/strategy/paladin/TankPaladinStrategy.cpp +++ b/src/strategy/paladin/TankPaladinStrategy.cpp @@ -106,7 +106,7 @@ void TankPaladinStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode( "righteous fury", NextAction::array(0, new NextAction("righteous fury", ACTION_HIGH + 8), nullptr))); triggers.push_back( - new TriggerNode("medium group heal occasion", + new TriggerNode("medium group heal setting", NextAction::array(0, new NextAction("divine sacrifice", ACTION_HIGH + 5), nullptr))); triggers.push_back(new TriggerNode( "enough mana", NextAction::array(0, new NextAction("consecration", ACTION_HIGH + 4), nullptr))); diff --git a/src/strategy/priest/HealPriestStrategy.cpp b/src/strategy/priest/HealPriestStrategy.cpp index 52c12df6..64c747fd 100644 --- a/src/strategy/priest/HealPriestStrategy.cpp +++ b/src/strategy/priest/HealPriestStrategy.cpp @@ -35,33 +35,39 @@ void HealPriestStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode( "group heal setting", - NextAction::array(0, new NextAction("power word: shield on almost full health below", ACTION_MEDIUM_HEAL + 7), + NextAction::array(0, + new NextAction("prayer of mending on party", ACTION_MEDIUM_HEAL + 8), + new NextAction("power word: shield on not full", ACTION_MEDIUM_HEAL + 7), nullptr))); triggers.push_back(new TriggerNode( - "medium group heal occasion", - NextAction::array(0, new NextAction("divine hymn", ACTION_CRITICAL_HEAL + 6), - new NextAction("prayer of healing on party", ACTION_CRITICAL_HEAL + 5), nullptr))); + "medium group heal setting", + NextAction::array(0, new NextAction("divine hymn", ACTION_CRITICAL_HEAL + 7), + new NextAction("prayer of mending on party", ACTION_CRITICAL_HEAL + 6), + new NextAction("power word: shield on not full", ACTION_CRITICAL_HEAL + 5), + new NextAction("prayer of healing on party", ACTION_CRITICAL_HEAL + 4), + nullptr))); triggers.push_back(new TriggerNode( "party member critical health", - NextAction::array(0, new NextAction("power word: shield on party", ACTION_CRITICAL_HEAL + 6), + NextAction::array(0, new NextAction("power word: shield on party", ACTION_CRITICAL_HEAL + 5), new NextAction("penance on party", ACTION_CRITICAL_HEAL + 4), - new NextAction("flash heal on party", ACTION_CRITICAL_HEAL + 3), - new NextAction("prayer of mending on party", ACTION_CRITICAL_HEAL + 2), nullptr))); + new NextAction("prayer of mending on party", ACTION_CRITICAL_HEAL + 3), + new NextAction("flash heal on party", ACTION_CRITICAL_HEAL + 2), + nullptr))); triggers.push_back( new TriggerNode("party member low health", NextAction::array(0, new NextAction("power word: shield on party", ACTION_MEDIUM_HEAL + 4), + new NextAction("prayer of mending on party", ACTION_MEDIUM_HEAL + 3), new NextAction("penance on party", ACTION_MEDIUM_HEAL + 2), - new NextAction("prayer of mending on party", ACTION_MEDIUM_HEAL + 1), new NextAction("flash heal on party", ACTION_MEDIUM_HEAL + 0), nullptr))); triggers.push_back( new TriggerNode("party member medium health", NextAction::array(0, new NextAction("power word: shield on party", ACTION_LIGHT_HEAL + 9), - new NextAction("penance on party", ACTION_LIGHT_HEAL + 7), - new NextAction("prayer of mending on party", ACTION_LIGHT_HEAL + 6), + new NextAction("prayer of mending on party", ACTION_LIGHT_HEAL + 7), + new NextAction("penance on party", ACTION_LIGHT_HEAL + 6), new NextAction("flash heal on party", ACTION_LIGHT_HEAL + 5), // new NextAction("renew on party", ACTION_LIGHT_HEAL + 8), nullptr))); @@ -70,7 +76,9 @@ void HealPriestStrategy::InitTriggers(std::vector& triggers) new TriggerNode("party member almost full health", NextAction::array(0, // new NextAction("penance on party", ACTION_LIGHT_HEAL + 3), - new NextAction("renew on party", ACTION_LIGHT_HEAL + 2), NULL))); + new NextAction("prayer of mending on party", ACTION_LIGHT_HEAL + 2), + new NextAction("renew on party", ACTION_LIGHT_HEAL + 1), + nullptr))); // triggers.push_back(new TriggerNode("almost full health", NextAction::array(0, new NextAction("renew", 43.f), // nullptr))); triggers.push_back(new TriggerNode("party member almost full health", NextAction::array(0, new diff --git a/src/strategy/priest/HolyPriestStrategy.cpp b/src/strategy/priest/HolyPriestStrategy.cpp index de29d7ea..7c073e05 100644 --- a/src/strategy/priest/HolyPriestStrategy.cpp +++ b/src/strategy/priest/HolyPriestStrategy.cpp @@ -64,40 +64,48 @@ void HolyHealPriestStrategy::InitTriggers(std::vector& triggers) triggers.push_back( new TriggerNode("group heal setting", - NextAction::array(0, new NextAction("circle of healing on party", ACTION_MEDIUM_HEAL + 8), nullptr))); + NextAction::array(0, + new NextAction("prayer of mending on party", ACTION_MEDIUM_HEAL + 9), + new NextAction("circle of healing on party", ACTION_MEDIUM_HEAL + 8), nullptr))); triggers.push_back(new TriggerNode( - "medium group heal occasion", - NextAction::array(0, new NextAction("divine hymn", ACTION_CRITICAL_HEAL + 6), - new NextAction("prayer of healing on party", ACTION_CRITICAL_HEAL + 5), nullptr))); + "medium group heal setting", + NextAction::array(0, new NextAction("divine hymn", ACTION_CRITICAL_HEAL + 7), + new NextAction("prayer of mending on party", ACTION_CRITICAL_HEAL + 6), + new NextAction("circle of healing on party", ACTION_CRITICAL_HEAL + 5), + new NextAction("prayer of healing on party", ACTION_CRITICAL_HEAL + 4), nullptr))); triggers.push_back(new TriggerNode( "party member critical health", NextAction::array(0, new NextAction("guardian spirit on party", ACTION_CRITICAL_HEAL + 6), new NextAction("power word: shield on party", ACTION_CRITICAL_HEAL + 5), - new NextAction("flash heal on party", ACTION_CRITICAL_HEAL + 3), - new NextAction("prayer of mending on party", ACTION_CRITICAL_HEAL + 2), nullptr))); + new NextAction("prayer of mending on party", ACTION_CRITICAL_HEAL + 3), + new NextAction("greater heal on party", ACTION_MEDIUM_HEAL + 2), + new NextAction("flash heal on party", ACTION_CRITICAL_HEAL + 1), + nullptr))); triggers.push_back( new TriggerNode("party member low health", NextAction::array(0, new NextAction("circle of healing on party", ACTION_MEDIUM_HEAL + 4), - new NextAction("greater heal on party", ACTION_MEDIUM_HEAL + 3), - new NextAction("prayer of mending on party", ACTION_MEDIUM_HEAL + 2), + new NextAction("prayer of mending on party", ACTION_MEDIUM_HEAL + 3), + new NextAction("greater heal on party", ACTION_MEDIUM_HEAL + 2), new NextAction("flash heal on party", ACTION_MEDIUM_HEAL + 1), nullptr))); triggers.push_back( new TriggerNode("party member medium health", NextAction::array(0, new NextAction("circle of healing on party", ACTION_LIGHT_HEAL + 7), new NextAction("prayer of mending on party", ACTION_LIGHT_HEAL + 6), - new NextAction("flash heal on party", ACTION_LIGHT_HEAL + 5), + new NextAction("greater heal on party", ACTION_MEDIUM_HEAL + 5), + new NextAction("flash heal on party", ACTION_LIGHT_HEAL + 4), // new NextAction("renew on party", ACTION_LIGHT_HEAL + 8), nullptr))); triggers.push_back( new TriggerNode("party member almost full health", - NextAction::array(0, new NextAction("renew on party", ACTION_LIGHT_HEAL + 2), - // new NextAction("flash heal on party", ACTION_LIGHT_HEAL + 1), + NextAction::array(0, + new NextAction("renew on party", ACTION_LIGHT_HEAL + 2), + new NextAction("prayer of mending on party", ACTION_LIGHT_HEAL + 1), nullptr))); triggers.push_back(new TriggerNode( diff --git a/src/strategy/priest/PriestAiObjectContext.cpp b/src/strategy/priest/PriestAiObjectContext.cpp index d6c59233..3f8dbacd 100644 --- a/src/strategy/priest/PriestAiObjectContext.cpp +++ b/src/strategy/priest/PriestAiObjectContext.cpp @@ -175,6 +175,8 @@ public: creators["power word: shield on party"] = &PriestAiObjectContextInternal::power_word_shield_on_party; creators["power word: shield on almost full health below"] = &PriestAiObjectContextInternal::power_word_shield_on_almost_full_health_below; + creators["power word: shield on not full"] = + &PriestAiObjectContextInternal::power_word_shield_on_not_full; creators["renew"] = &PriestAiObjectContextInternal::renew; creators["renew on party"] = &PriestAiObjectContextInternal::renew_on_party; creators["greater heal"] = &PriestAiObjectContextInternal::greater_heal; @@ -285,7 +287,11 @@ private: } static Action* power_word_shield_on_almost_full_health_below(PlayerbotAI* ai) { - return new CastPowerWordShieldOnAlmostFullHealthBelow(ai); + return new CastPowerWordShieldOnAlmostFullHealthBelowAction(ai); + } + static Action* power_word_shield_on_not_full(PlayerbotAI* ai) + { + return new CastPowerWordShieldOnNotFullAction(ai); } static Action* renew(PlayerbotAI* botAI) { return new CastRenewAction(botAI); } static Action* renew_on_party(PlayerbotAI* botAI) { return new CastRenewOnPartyAction(botAI); } diff --git a/src/strategy/triggers/TriggerContext.h b/src/strategy/triggers/TriggerContext.h index 7baebc8d..cbfeec6f 100644 --- a/src/strategy/triggers/TriggerContext.h +++ b/src/strategy/triggers/TriggerContext.h @@ -140,7 +140,7 @@ public: creators["almost full aoe heal"] = &TriggerContext::almost_full_aoe_heal; creators["group heal setting"] = &TriggerContext::group_heal_occasion; - creators["medium group heal occasion"] = &TriggerContext::medium_group_heal_occasion; + creators["medium group heal setting"] = &TriggerContext::medium_group_heal_occasion; creators["invalid target"] = &TriggerContext::invalid_target; creators["lfg proposal active"] = &TriggerContext::lfg_proposal_active; @@ -250,7 +250,7 @@ private: } static Trigger* medium_group_heal_occasion(PlayerbotAI* ai) { - return new AoeInGroupTrigger(ai, "medium group heal occasion", "medium"); + return new AoeInGroupTrigger(ai, "medium group heal setting", "medium"); } static Trigger* target_changed(PlayerbotAI* botAI) { return new TargetChangedTrigger(botAI); } static Trigger* swimming(PlayerbotAI* botAI) { return new IsSwimmingTrigger(botAI); } From 529db3e75429bfa5a85d815845b28207d906fca2 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Fri, 4 Oct 2024 21:43:38 +0800 Subject: [PATCH 12/40] [Core sync] Add DynamicObject.h --- src/strategy/AiObjectContext.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/strategy/AiObjectContext.h b/src/strategy/AiObjectContext.h index f7bd2694..07fa78a6 100644 --- a/src/strategy/AiObjectContext.h +++ b/src/strategy/AiObjectContext.h @@ -10,6 +10,7 @@ #include #include "Common.h" +#include "DynamicObject.h" #include "NamedObjectContext.h" #include "PlayerbotAIAware.h" #include "Strategy.h" From 11377436cd7281d7f0743c9acdb7ad19a9d28ec0 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Fri, 4 Oct 2024 21:44:39 +0800 Subject: [PATCH 13/40] [Strategy] Rename "assist dps" to "healer dps" --- src/AiFactory.cpp | 2 +- src/strategy/druid/DruidAiObjectContext.cpp | 4 ++-- src/strategy/druid/GenericDruidStrategy.cpp | 2 +- src/strategy/druid/GenericDruidStrategy.h | 6 +++--- src/strategy/paladin/GenericPaladinStrategy.cpp | 2 +- src/strategy/paladin/GenericPaladinStrategy.h | 6 +++--- src/strategy/paladin/PaladinAiObjectContext.cpp | 4 ++-- src/strategy/priest/GenericPriestStrategy.cpp | 2 +- src/strategy/priest/GenericPriestStrategy.h | 6 +++--- src/strategy/priest/PriestAiObjectContext.cpp | 4 ++-- src/strategy/shaman/GenericShamanStrategy.cpp | 2 +- src/strategy/shaman/GenericShamanStrategy.h | 6 +++--- src/strategy/shaman/ShamanAiObjectContext.cpp | 4 ++-- 13 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/AiFactory.cpp b/src/AiFactory.cpp index c03cd615..d3cd1ef3 100644 --- a/src/AiFactory.cpp +++ b/src/AiFactory.cpp @@ -394,7 +394,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa { if (sPlayerbotAIConfig->autoSaveMana) engine->addStrategy("save mana", false); - engine->addStrategy("assist dps", false); + engine->addStrategy("healer dps", false); } if (facade->IsRealPlayer() || sRandomPlayerbotMgr->IsRandomBot(player)) { diff --git a/src/strategy/druid/DruidAiObjectContext.cpp b/src/strategy/druid/DruidAiObjectContext.cpp index d9882c76..e0d8c7de 100644 --- a/src/strategy/druid/DruidAiObjectContext.cpp +++ b/src/strategy/druid/DruidAiObjectContext.cpp @@ -34,7 +34,7 @@ public: creators["buff"] = &DruidStrategyFactoryInternal::buff; creators["boost"] = &DruidStrategyFactoryInternal::boost; creators["cc"] = &DruidStrategyFactoryInternal::cc; - creators["assist dps"] = &DruidStrategyFactoryInternal::assist_dps; + creators["healer dps"] = &DruidStrategyFactoryInternal::healer_dps; } private: @@ -47,7 +47,7 @@ private: static Strategy* buff(PlayerbotAI* botAI) { return new GenericDruidBuffStrategy(botAI); } static Strategy* boost(PlayerbotAI* botAI) { return new DruidBoostStrategy(botAI); } static Strategy* cc(PlayerbotAI* botAI) { return new DruidCcStrategy(botAI); } - static Strategy* assist_dps(PlayerbotAI* botAI) { return new DruidAssistDpsStrategy(botAI); } + static Strategy* healer_dps(PlayerbotAI* botAI) { return new DruidHealerDpsStrategy(botAI); } }; class DruidDruidStrategyFactoryInternal : public NamedObjectContext diff --git a/src/strategy/druid/GenericDruidStrategy.cpp b/src/strategy/druid/GenericDruidStrategy.cpp index cbe3ad7a..d06a0e56 100644 --- a/src/strategy/druid/GenericDruidStrategy.cpp +++ b/src/strategy/druid/GenericDruidStrategy.cpp @@ -153,7 +153,7 @@ void DruidCcStrategy::InitTriggers(std::vector& triggers) "hibernate", NextAction::array(0, new NextAction("hibernate on cc", ACTION_HIGH + 3), nullptr))); } -void DruidAssistDpsStrategy::InitTriggers(std::vector& triggers) +void DruidHealerDpsStrategy::InitTriggers(std::vector& triggers) { triggers.push_back( new TriggerNode("healer should attack", diff --git a/src/strategy/druid/GenericDruidStrategy.h b/src/strategy/druid/GenericDruidStrategy.h index 6099527e..547b93b0 100644 --- a/src/strategy/druid/GenericDruidStrategy.h +++ b/src/strategy/druid/GenericDruidStrategy.h @@ -46,13 +46,13 @@ public: std::string const getName() override { return "cc"; } }; -class DruidAssistDpsStrategy : public Strategy +class DruidHealerDpsStrategy : public Strategy { public: - DruidAssistDpsStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} + DruidHealerDpsStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} void InitTriggers(std::vector& triggers) override; - std::string const getName() override { return "assist dps"; } + std::string const getName() override { return "healer dps"; } }; #endif diff --git a/src/strategy/paladin/GenericPaladinStrategy.cpp b/src/strategy/paladin/GenericPaladinStrategy.cpp index 09eb2074..b5b77f58 100644 --- a/src/strategy/paladin/GenericPaladinStrategy.cpp +++ b/src/strategy/paladin/GenericPaladinStrategy.cpp @@ -72,7 +72,7 @@ void PaladinCcStrategy::InitTriggers(std::vector& triggers) new TriggerNode("turn undead", NextAction::array(0, new NextAction("turn undead", ACTION_HIGH + 1), nullptr))); } -void PaladinAssistDpsStrategy::InitTriggers(std::vector& triggers) +void PaladinHealerDpsStrategy::InitTriggers(std::vector& triggers) { triggers.push_back( new TriggerNode("healer should attack", diff --git a/src/strategy/paladin/GenericPaladinStrategy.h b/src/strategy/paladin/GenericPaladinStrategy.h index fcdef3c6..737048cc 100644 --- a/src/strategy/paladin/GenericPaladinStrategy.h +++ b/src/strategy/paladin/GenericPaladinStrategy.h @@ -46,13 +46,13 @@ public: std::string const getName() override { return "cc"; } }; -class PaladinAssistDpsStrategy : public Strategy +class PaladinHealerDpsStrategy : public Strategy { public: - PaladinAssistDpsStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} + PaladinHealerDpsStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} void InitTriggers(std::vector& triggers) override; - std::string const getName() override { return "assist dps"; } + std::string const getName() override { return "healer dps"; } }; #endif diff --git a/src/strategy/paladin/PaladinAiObjectContext.cpp b/src/strategy/paladin/PaladinAiObjectContext.cpp index 5bc056ba..42e89a8a 100644 --- a/src/strategy/paladin/PaladinAiObjectContext.cpp +++ b/src/strategy/paladin/PaladinAiObjectContext.cpp @@ -25,7 +25,7 @@ public: creators["boost"] = &PaladinStrategyFactoryInternal::boost; creators["cc"] = &PaladinStrategyFactoryInternal::cc; creators["bthreat"] = &PaladinStrategyFactoryInternal::bthreat; - creators["assist dps"] = &PaladinStrategyFactoryInternal::assist_dps; + creators["healer dps"] = &PaladinStrategyFactoryInternal::healer_dps; } private: @@ -34,7 +34,7 @@ private: static Strategy* boost(PlayerbotAI* botAI) { return new PaladinBoostStrategy(botAI); } static Strategy* cc(PlayerbotAI* botAI) { return new PaladinCcStrategy(botAI); } static Strategy* bthreat(PlayerbotAI* botAI) { return new PaladinBuffThreatStrategy(botAI); } - static Strategy* assist_dps(PlayerbotAI* botAI) { return new PaladinAssistDpsStrategy(botAI); } + static Strategy* healer_dps(PlayerbotAI* botAI) { return new PaladinHealerDpsStrategy(botAI); } }; class PaladinResistanceStrategyFactoryInternal : public NamedObjectContext diff --git a/src/strategy/priest/GenericPriestStrategy.cpp b/src/strategy/priest/GenericPriestStrategy.cpp index fa680424..82ca65e9 100644 --- a/src/strategy/priest/GenericPriestStrategy.cpp +++ b/src/strategy/priest/GenericPriestStrategy.cpp @@ -88,7 +88,7 @@ void PriestCcStrategy::InitTriggers(std::vector& triggers) new TriggerNode("shackle undead", NextAction::array(0, new NextAction("shackle undead", 31.0f), nullptr))); } -void PriestAssistDpsStrategy::InitTriggers(std::vector& triggers) +void PriestHealerDpsStrategy::InitTriggers(std::vector& triggers) { triggers.push_back( new TriggerNode("healer should attack", diff --git a/src/strategy/priest/GenericPriestStrategy.h b/src/strategy/priest/GenericPriestStrategy.h index f18e2b00..2668723e 100644 --- a/src/strategy/priest/GenericPriestStrategy.h +++ b/src/strategy/priest/GenericPriestStrategy.h @@ -46,13 +46,13 @@ public: std::string const getName() override { return "cc"; } }; -class PriestAssistDpsStrategy : public Strategy +class PriestHealerDpsStrategy : public Strategy { public: - PriestAssistDpsStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} + PriestHealerDpsStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} void InitTriggers(std::vector& triggers) override; - std::string const getName() override { return "assist dps"; } + std::string const getName() override { return "healer dps"; } }; #endif diff --git a/src/strategy/priest/PriestAiObjectContext.cpp b/src/strategy/priest/PriestAiObjectContext.cpp index 3f8dbacd..4f057718 100644 --- a/src/strategy/priest/PriestAiObjectContext.cpp +++ b/src/strategy/priest/PriestAiObjectContext.cpp @@ -31,7 +31,7 @@ public: creators["boost"] = &PriestStrategyFactoryInternal::boost; creators["rshadow"] = &PriestStrategyFactoryInternal::rshadow; creators["cc"] = &PriestStrategyFactoryInternal::cc; - creators["assist dps"] = &PriestStrategyFactoryInternal::assist_dps; + creators["healer dps"] = &PriestStrategyFactoryInternal::healer_dps; } private: @@ -44,7 +44,7 @@ private: static Strategy* pull(PlayerbotAI* botAI) { return new PullStrategy(botAI, "shoot"); } static Strategy* shadow_debuff(PlayerbotAI* botAI) { return new ShadowPriestDebuffStrategy(botAI); } static Strategy* cure(PlayerbotAI* botAI) { return new PriestCureStrategy(botAI); } - static Strategy* assist_dps(PlayerbotAI* botAI) { return new PriestAssistDpsStrategy(botAI); } + static Strategy* healer_dps(PlayerbotAI* botAI) { return new PriestHealerDpsStrategy(botAI); } }; class PriestCombatStrategyFactoryInternal : public NamedObjectContext diff --git a/src/strategy/shaman/GenericShamanStrategy.cpp b/src/strategy/shaman/GenericShamanStrategy.cpp index 430a1bdc..95ed14f2 100644 --- a/src/strategy/shaman/GenericShamanStrategy.cpp +++ b/src/strategy/shaman/GenericShamanStrategy.cpp @@ -164,7 +164,7 @@ void ShamanCureStrategy::InitTriggers(std::vector& triggers) NextAction::array(0, new NextAction("cleanse spirit curse on party", 23.0f), nullptr))); } -void ShamanAssistDpsStrategy::InitTriggers(std::vector& triggers) +void ShamanHealerDpsStrategy::InitTriggers(std::vector& triggers) { triggers.push_back( new TriggerNode("healer should attack", diff --git a/src/strategy/shaman/GenericShamanStrategy.h b/src/strategy/shaman/GenericShamanStrategy.h index d41b8c04..5fa0ff83 100644 --- a/src/strategy/shaman/GenericShamanStrategy.h +++ b/src/strategy/shaman/GenericShamanStrategy.h @@ -45,13 +45,13 @@ public: std::string const getName() override { return "cure"; } }; -class ShamanAssistDpsStrategy : public Strategy +class ShamanHealerDpsStrategy : public Strategy { public: - ShamanAssistDpsStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} + ShamanHealerDpsStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} void InitTriggers(std::vector& triggers) override; - std::string const getName() override { return "assist dps"; } + std::string const getName() override { return "healer dps"; } }; #endif diff --git a/src/strategy/shaman/ShamanAiObjectContext.cpp b/src/strategy/shaman/ShamanAiObjectContext.cpp index 193d292a..9e79f792 100644 --- a/src/strategy/shaman/ShamanAiObjectContext.cpp +++ b/src/strategy/shaman/ShamanAiObjectContext.cpp @@ -26,7 +26,7 @@ public: creators["melee aoe"] = &ShamanStrategyFactoryInternal::melee_aoe; creators["caster aoe"] = &ShamanStrategyFactoryInternal::caster_aoe; creators["cure"] = &ShamanStrategyFactoryInternal::cure; - creators["assist dps"] = &ShamanStrategyFactoryInternal::assist_dps; + creators["healer dps"] = &ShamanStrategyFactoryInternal::healer_dps; } private: @@ -35,7 +35,7 @@ private: static Strategy* melee_aoe(PlayerbotAI* botAI) { return new MeleeAoeShamanStrategy(botAI); } static Strategy* caster_aoe(PlayerbotAI* botAI) { return new CasterAoeShamanStrategy(botAI); } static Strategy* cure(PlayerbotAI* botAI) { return new ShamanCureStrategy(botAI); } - static Strategy* assist_dps(PlayerbotAI* botAI) { return new ShamanAssistDpsStrategy(botAI); } + static Strategy* healer_dps(PlayerbotAI* botAI) { return new ShamanHealerDpsStrategy(botAI); } }; class ShamanBuffStrategyFactoryInternal : public NamedObjectContext From 5f50b6fddc3481fc403cb818587317ebc4d30e3c Mon Sep 17 00:00:00 2001 From: Revision Date: Fri, 4 Oct 2024 21:17:19 +0200 Subject: [PATCH 14/40] Add check to make sure there are races available for a class --- src/RandomPlayerbotFactory.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/RandomPlayerbotFactory.cpp b/src/RandomPlayerbotFactory.cpp index 8c350130..ea6c55dd 100644 --- a/src/RandomPlayerbotFactory.cpp +++ b/src/RandomPlayerbotFactory.cpp @@ -165,6 +165,13 @@ Player* RandomPlayerbotFactory::CreateRandomBot(WorldSession* session, uint8 cls raceOptions.push_back(race); } } + + if (raceOptions.size() == 0) + { + LOG_ERROR("playerbots", "No race available for class: {}", cls); + return nullptr; + } + uint8 race = raceOptions[urand(0, raceOptions.size() - 1)]; const auto raceAndGender = CombineRaceAndGender(gender, race); From eee8c96a128495d8f7d485b29fe0c7ca37420cc3 Mon Sep 17 00:00:00 2001 From: Revision Date: Fri, 4 Oct 2024 21:19:11 +0200 Subject: [PATCH 15/40] Fix typo in log message --- src/RandomPlayerbotFactory.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/RandomPlayerbotFactory.cpp b/src/RandomPlayerbotFactory.cpp index ea6c55dd..e923f2a8 100644 --- a/src/RandomPlayerbotFactory.cpp +++ b/src/RandomPlayerbotFactory.cpp @@ -168,7 +168,7 @@ Player* RandomPlayerbotFactory::CreateRandomBot(WorldSession* session, uint8 cls if (raceOptions.size() == 0) { - LOG_ERROR("playerbots", "No race available for class: {}", cls); + LOG_ERROR("playerbots", "No races available for class: {}", cls); return nullptr; } From 8f1f79a3b1b53501d3c08f41be0d5a695d2a1e8e Mon Sep 17 00:00:00 2001 From: Revision Date: Fri, 4 Oct 2024 22:23:08 +0200 Subject: [PATCH 16/40] Use empty() instead of size() Thank you @mtm84 for pointing that out, I forgot to check properly. --- src/RandomPlayerbotFactory.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/RandomPlayerbotFactory.cpp b/src/RandomPlayerbotFactory.cpp index e923f2a8..57b42bda 100644 --- a/src/RandomPlayerbotFactory.cpp +++ b/src/RandomPlayerbotFactory.cpp @@ -166,7 +166,7 @@ Player* RandomPlayerbotFactory::CreateRandomBot(WorldSession* session, uint8 cls } } - if (raceOptions.size() == 0) + if (raceOptions.empty()) { LOG_ERROR("playerbots", "No races available for class: {}", cls); return nullptr; From d0f5bb7cc78f6182986c8705d01534e9d97c96d9 Mon Sep 17 00:00:00 2001 From: Bobblybook Date: Sat, 5 Oct 2024 19:02:17 +1000 Subject: [PATCH 17/40] Correct fingers of frost mage AI --- src/strategy/mage/MageTriggers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/strategy/mage/MageTriggers.cpp b/src/strategy/mage/MageTriggers.cpp index e935d51b..95c22e4d 100644 --- a/src/strategy/mage/MageTriggers.cpp +++ b/src/strategy/mage/MageTriggers.cpp @@ -29,7 +29,7 @@ bool FingersOfFrostSingleTrigger::IsActive() { // Fingers of Frost "stack" count is always 1. // The value is instead stored in the charges. - Aura* aura = botAI->GetAura(getName(), GetTarget(), false, true, -1); + Aura* aura = botAI->GetAura("fingers of frost", bot, false, true, -1); return (aura && aura->GetCharges() == 1); } From 23ff699724dad9895746452c4e41f99bd811095a Mon Sep 17 00:00:00 2001 From: Bobblybook Date: Sat, 5 Oct 2024 19:04:05 +1000 Subject: [PATCH 18/40] Utgarde Keep strat cleanup --- .../dungeons/wotlk/utgardekeep/UtgardeKeepActions.cpp | 1 + .../dungeons/wotlk/utgardekeep/UtgardeKeepActions.h | 5 +++-- .../dungeons/wotlk/utgardekeep/UtgardeKeepMultipliers.h | 4 ++-- .../dungeons/wotlk/utgardekeep/UtgardeKeepStrategy.h | 4 ++-- .../dungeons/wotlk/utgardekeep/UtgardeKeepTriggers.h | 6 +----- 5 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepActions.cpp b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepActions.cpp index 2c17366d..f26823f4 100644 --- a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepActions.cpp +++ b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepActions.cpp @@ -2,6 +2,7 @@ #include "UtgardeKeepActions.h" #include "UtgardeKeepStrategy.h" +bool AttackFrostTombAction::isUseful() { return !botAI->IsHeal(bot); } bool AttackFrostTombAction::Execute(Event event) { Unit* frostTomb = nullptr; diff --git a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepActions.h b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepActions.h index 3774f374..c9c77d52 100644 --- a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepActions.h +++ b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepActions.h @@ -12,6 +12,7 @@ class AttackFrostTombAction : public AttackAction public: AttackFrostTombAction(PlayerbotAI* ai) : AttackAction(ai, "attack frost tomb") {} bool Execute(Event event) override; + bool isUseful() override; }; class AttackDalronnAction : public AttackAction @@ -32,16 +33,16 @@ class IngvarDodgeSmashAction : public MovementAction { public: IngvarDodgeSmashAction(PlayerbotAI* ai) : MovementAction(ai, "ingvar dodge smash") {} - bool isUseful() override; bool Execute(Event event) override; + bool isUseful() override; }; class IngvarSmashReturnAction : public MovementAction { public: IngvarSmashReturnAction(PlayerbotAI* ai) : MovementAction(ai, "ingvar smash return") {} - bool isUseful() override; bool Execute(Event event) override; + bool isUseful() override; }; #endif diff --git a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepMultipliers.h b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepMultipliers.h index 0b42300e..e0b8e7af 100644 --- a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepMultipliers.h +++ b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepMultipliers.h @@ -1,5 +1,5 @@ -#ifndef _PLAYERRBOT_WOTLKDUNGEONUKMULTIPLIERS_H -#define _PLAYERRBOT_WOTLKDUNGEONUKMULTIPLIERS_H +#ifndef _PLAYERBOT_WOTLKDUNGEONUKMULTIPLIERS_H +#define _PLAYERBOT_WOTLKDUNGEONUKMULTIPLIERS_H #include "Multiplier.h" diff --git a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepStrategy.h b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepStrategy.h index f4c21aa5..90873feb 100644 --- a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepStrategy.h +++ b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepStrategy.h @@ -1,5 +1,5 @@ -#ifndef _PLAYERBOT_WOTLKDUNGEONUKSTRATEGY_H_ -#define _PLAYERBOT_WOTLKDUNGEONUKSTRATEGY_H_ +#ifndef _PLAYERBOT_WOTLKDUNGEONUKSTRATEGY_H +#define _PLAYERBOT_WOTLKDUNGEONUKSTRATEGY_H #include "Multiplier.h" #include "AiObjectContext.h" diff --git a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepTriggers.h b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepTriggers.h index 0f0903be..db2032aa 100644 --- a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepTriggers.h +++ b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepTriggers.h @@ -1,15 +1,12 @@ #ifndef _PLAYERBOT_WOTLKDUNGEONUKTRIGGERS_H #define _PLAYERBOT_WOTLKDUNGEONUKTRIGGERS_H -#include "EventMap.h" #include "Trigger.h" #include "PlayerbotAIConfig.h" #include "GenericTriggers.h" #include "DungeonStrategyUtils.h" -// Taken from: -// src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_ingvar_the_plunderer.cpp -enum Spells +enum UtgardeKeepIDs { SPELL_SUMMON_VALKYR = 42912, SPELL_RESURRECTION_BEAM = 42857, @@ -32,7 +29,6 @@ enum Spells SPELL_DARK_SMASH = 42723, SPELL_SHADOW_AXE = 42749, - // Added DEBUFF_FROST_TOMB = 48400, }; From 45303d55c1d40b959f3b5696e46722e765c96e79 Mon Sep 17 00:00:00 2001 From: Bobblybook Date: Sun, 6 Oct 2024 00:24:45 +1000 Subject: [PATCH 19/40] Nexus implementation & dragon flank code --- src/strategy/AiObjectContext.cpp | 2 + .../dungeons/DungeonStrategyContext.h | 5 +- .../wotlk/WotlkDungeonActionContext.h | 2 +- .../wotlk/WotlkDungeonTriggerContext.h | 2 +- .../dungeons/wotlk/nexus/NexusActionContext.h | 30 +++ .../dungeons/wotlk/nexus/NexusActions.cpp | 206 ++++++++++++++++++ .../dungeons/wotlk/nexus/NexusActions.h | 79 +++++++ .../dungeons/wotlk/nexus/NexusMultipliers.cpp | 99 +++++++++ .../dungeons/wotlk/nexus/NexusMultipliers.h | 51 +++++ .../dungeons/wotlk/nexus/NexusStrategy.cpp | 54 +++++ .../dungeons/wotlk/nexus/NexusStrategy.h | 18 ++ .../wotlk/nexus/NexusTriggerContext.h | 33 +++ .../dungeons/wotlk/nexus/NexusTriggers.cpp | 105 +++++++++ .../dungeons/wotlk/nexus/NexusTriggers.h | 83 ++++--- 14 files changed, 720 insertions(+), 49 deletions(-) create mode 100644 src/strategy/dungeons/wotlk/nexus/NexusActionContext.h create mode 100644 src/strategy/dungeons/wotlk/nexus/NexusActions.cpp create mode 100644 src/strategy/dungeons/wotlk/nexus/NexusActions.h create mode 100644 src/strategy/dungeons/wotlk/nexus/NexusMultipliers.cpp create mode 100644 src/strategy/dungeons/wotlk/nexus/NexusMultipliers.h create mode 100644 src/strategy/dungeons/wotlk/nexus/NexusStrategy.cpp create mode 100644 src/strategy/dungeons/wotlk/nexus/NexusStrategy.h create mode 100644 src/strategy/dungeons/wotlk/nexus/NexusTriggerContext.h create mode 100644 src/strategy/dungeons/wotlk/nexus/NexusTriggers.cpp diff --git a/src/strategy/AiObjectContext.cpp b/src/strategy/AiObjectContext.cpp index c6fcbc47..0742d363 100644 --- a/src/strategy/AiObjectContext.cpp +++ b/src/strategy/AiObjectContext.cpp @@ -51,6 +51,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI) actionContexts.Add(new RaidUlduarActionContext()); actionContexts.Add(new RaidIccActionContext()); actionContexts.Add(new WotlkDungeonUKActionContext()); + actionContexts.Add(new WotlkDungeonNexActionContext()); triggerContexts.Add(new TriggerContext()); triggerContexts.Add(new ChatTriggerContext()); @@ -62,6 +63,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI) triggerContexts.Add(new RaidUlduarTriggerContext()); triggerContexts.Add(new RaidIccTriggerContext()); triggerContexts.Add(new WotlkDungeonUKTriggerContext()); + triggerContexts.Add(new WotlkDungeonNexTriggerContext()); valueContexts.Add(new ValueContext()); diff --git a/src/strategy/dungeons/DungeonStrategyContext.h b/src/strategy/dungeons/DungeonStrategyContext.h index 4715de4e..afa2616f 100644 --- a/src/strategy/dungeons/DungeonStrategyContext.h +++ b/src/strategy/dungeons/DungeonStrategyContext.h @@ -3,12 +3,11 @@ #include "Strategy.h" #include "wotlk/utgardekeep/UtgardeKeepStrategy.h" +#include "wotlk/nexus/NexusStrategy.h" /* Full list/TODO: -The Nexus - Nex -Grand Magus Telestra, Anomalus, Ormorok the Tree-Shaper, Keristrasza, Commander Stoutbeard (Horde Heroic Only)/Commander Kolurg (Alliance Heroic Only) Azjol-Nerub: Azjol-Nerub - AN Krik'thir the Gatewatcher, Hadronox, Anub'arak Ahn'kahet: The Old Kingdom - OK @@ -76,7 +75,7 @@ class DungeonStrategyContext : public NamedObjectContext } private: static Strategy* wotlk_uk(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } - static Strategy* wotlk_nex(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } + static Strategy* wotlk_nex(PlayerbotAI* botAI) { return new WotlkDungeonNexStrategy(botAI); } static Strategy* wotlk_an(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } static Strategy* wotlk_ok(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } static Strategy* wotlk_dtk(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } diff --git a/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h b/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h index 419229dd..d9a18ae8 100644 --- a/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h +++ b/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h @@ -2,7 +2,7 @@ #define _PLAYERBOT_WOTLKDUNGEONACTIONCONTEXT_H #include "utgardekeep/UtgardeKeepActionContext.h" -// #include "nexus/NexusActionContext.h" +#include "nexus/NexusActionContext.h" // #include "azjolnerub/AzjolNerubActionContext.h" // #include "oldkingdom/OldKingdomActionContext.h" // #include "draktharonkeep/DraktharonKeepActionContext.h" diff --git a/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h b/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h index 28a42d87..4cbb41e2 100644 --- a/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h +++ b/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h @@ -2,7 +2,7 @@ #define _PLAYERBOT_WOTLKDUNGEONTRIGGERCONTEXT_H #include "utgardekeep/UtgardeKeepTriggerContext.h" -// #include "nexus/NexusTriggerContext.h" +#include "nexus/NexusTriggerContext.h" // #include "azjolnerub/AzjolNerubTriggerContext.h" // #include "oldkingdom/OldKingdomTriggerContext.h" // #include "draktharonkeep/DraktharonKeepTriggerContext.h" diff --git a/src/strategy/dungeons/wotlk/nexus/NexusActionContext.h b/src/strategy/dungeons/wotlk/nexus/NexusActionContext.h new file mode 100644 index 00000000..61dd91d8 --- /dev/null +++ b/src/strategy/dungeons/wotlk/nexus/NexusActionContext.h @@ -0,0 +1,30 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONNEXACTIONCONTEXT_H +#define _PLAYERBOT_WOTLKDUNGEONNEXACTIONCONTEXT_H + +#include "Action.h" +#include "NamedObjectContext.h" +#include "NexusActions.h" + +class WotlkDungeonNexActionContext : public NamedObjectContext +{ + public: + WotlkDungeonNexActionContext() { + creators["move from whirlwind"] = &WotlkDungeonNexActionContext::move_from_whirlwind; + creators["firebomb spread"] = &WotlkDungeonNexActionContext::firebomb_spread; + creators["telestra split target"] = &WotlkDungeonNexActionContext::telestra_split_target; + creators["chaotic rift target"] = &WotlkDungeonNexActionContext::chaotic_rift_target; + creators["dodge spikes"] = &WotlkDungeonNexActionContext::dodge_spikes; + creators["intense cold jump"] = &WotlkDungeonNexActionContext::intense_cold_jump; + creators["rear flank position"] = &WotlkDungeonNexActionContext::rear_flank_position; + } + private: + static Action* move_from_whirlwind(PlayerbotAI* ai) { return new MoveFromWhirlwindAction(ai); } + static Action* firebomb_spread(PlayerbotAI* ai) { return new FirebombSpreadAction(ai); } + static Action* telestra_split_target(PlayerbotAI* ai) { return new TelestraSplitTargetAction(ai); } + static Action* chaotic_rift_target(PlayerbotAI* ai) { return new ChaoticRiftTargetAction(ai); } + static Action* dodge_spikes(PlayerbotAI* ai) { return new DodgeSpikesAction(ai); } + static Action* intense_cold_jump(PlayerbotAI* ai) { return new IntenseColdJumpAction(ai); } + static Action* rear_flank_position(PlayerbotAI* ai) { return new RearFlankPositionAction(ai); } +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/nexus/NexusActions.cpp b/src/strategy/dungeons/wotlk/nexus/NexusActions.cpp new file mode 100644 index 00000000..543fadba --- /dev/null +++ b/src/strategy/dungeons/wotlk/nexus/NexusActions.cpp @@ -0,0 +1,206 @@ +#include "Playerbots.h" +#include "NexusActions.h" +#include "NexusStrategy.h" + +bool MoveFromWhirlwindAction::Execute(Event event) +{ + Unit* boss = nullptr; + uint8 faction = bot->GetTeamId(); + float targetDist = 10.0f; // Whirlwind has range of 8, add a couple for safety buffer + + switch (bot->GetMap()->GetDifficulty()) + { + case DUNGEON_DIFFICULTY_NORMAL: + if (faction == TEAM_ALLIANCE) + { + boss = AI_VALUE2(Unit*, "find target", "horde commander"); + } + else //if (faction == TEAM_HORDE) + { + boss = AI_VALUE2(Unit*, "find target", "alliance commander"); + } + break; + case DUNGEON_DIFFICULTY_HEROIC: + if (faction == TEAM_ALLIANCE) + { + boss = AI_VALUE2(Unit*, "find target", "commander kolurg"); + } + else //if (faction == TEAM_HORDE) + { + boss = AI_VALUE2(Unit*, "find target", "commander stoutbeard"); + } + break; + default: + break; + } + if (!boss || bot->GetExactDist2d(boss->GetPosition()) > targetDist) + { + return false; + } + return MoveAway(boss, targetDist - bot->GetExactDist2d(boss->GetPosition())); +} + +bool FirebombSpreadAction::Execute(Event event) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "grand magus telestra"); + float radius = 5.0f; + float targetDist = radius + 1.0f; + if (!boss) { return false; } + + GuidVector members = AI_VALUE(GuidVector, "group members"); + for (auto& member : members) + { + if (bot->GetGUID() == member) + { + continue; + } + if (bot->GetExactDist2d(botAI->GetUnit(member)) < targetDist) + { + return MoveAway(botAI->GetUnit(member), targetDist); + } + } + return false; +} + +bool TelestraSplitTargetAction::isUseful() { return !botAI->IsHeal(bot); } +bool TelestraSplitTargetAction::Execute(Event event) +{ + GuidVector attackers = AI_VALUE(GuidVector, "attackers"); + Unit* splitTargets[3] = {nullptr, nullptr, nullptr}; + + for (auto& attacker : attackers) + { + Unit* npc = botAI->GetUnit(attacker); + if (!npc) + { + continue; + } + switch (npc->GetEntry()) + { + // Focus arcane clone first + case NPC_ARCANE_MAGUS: + splitTargets[0] = npc; + break; + // Then the frost clone + case NPC_FROST_MAGUS: + splitTargets[1] = npc; + break; + // Fire clone last + case NPC_FIRE_MAGUS: + splitTargets[2] = npc; + break; + } + } + + for (Unit* target : splitTargets) + { + // Attack the first valid split target in the priority list + if (target) + { + if (AI_VALUE(Unit*, "current target") != target) + { + return Attack(target); + } + // Don't continue loop here, the target exists so we don't + // want to move down the prio list. We just don't need to send attack + // command again, just return false and exit the loop that way + return false; + } + } + + return false; +} + +bool ChaoticRiftTargetAction::isUseful() { return !botAI->IsHeal(bot); } +bool ChaoticRiftTargetAction::Execute(Event event) +{ + Unit* chaoticRift = nullptr; + + // Target is not findable from threat table using AI_VALUE2(), + // therefore need to search manually for the unit name + GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); + + for (auto i = targets.begin(); i != targets.end(); ++i) + { + Unit* unit = botAI->GetUnit(*i); + if (unit && unit->GetName() == "Chaotic Rift") + { + chaoticRift = unit; + break; + } + } + if (!chaoticRift || AI_VALUE(Unit*, "current target") == chaoticRift) + { + return false; + } + return Attack(chaoticRift); +} + +bool DodgeSpikesAction::isUseful() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "ormorok the tree-shaper"); + return bot->GetExactDist2d(boss) > 0.5f; +} +bool DodgeSpikesAction::Execute(Event event) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "ormorok the tree-shaper"); + return Move(bot->GetAngle(boss), bot->GetExactDist2d(boss) - 0.3f); +} + +bool IntenseColdJumpAction::Execute(Event event) +{ + // This needs improving but maybe it should be done in the playerbot core. + // Jump doesn't seem to support zero offset (eg. jump on the spot) so need to add a tiny delta. + // This does a tiny bunnyhop that takes a couple of ms, it doesn't do a natural jump. + // Adding extra Z offset causes floating, and appears to scale the jump speed based on Z difference. + // Probably best to revisit once bot movement is improved + return JumpTo(bot->GetMap()->GetId(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ() + 0.01f); + // bot->GetMotionMaster()->MoveFall(); +} + +bool RearFlankPositionAction::isUseful() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "keristrasza"); + if (!boss) { return false; } + + // Need to double the front angle check to account for mirrored angle. + // Total 180 degrees (whole front half) + bool inFront = boss->HasInArc(2.f * DRAGON_MELEE_MIN_ANGLE, bot); + // Rear check does not need to double this angle as the logic is inverted + // and we are subtracing from 2pi. + bool inBack = !boss->HasInArc((2.f * M_PI) - DRAGON_MELEE_MAX_ANGLE, bot); + + return inFront || inBack; +} +bool RearFlankPositionAction::Execute(Event event) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "keristrasza"); + if (!boss) { return false; } + + // float angleToMove = minAngle + rand_norm() * (maxAngle - minAngle); + float angle = frand(DRAGON_MELEE_MIN_ANGLE, DRAGON_MELEE_MAX_ANGLE); + // Need to reduce this value very slightly, or the bots get the jitters - + // may be due to rounding errors. Need to bring them just inside their attack range. + // This boss has a big hitbox so we can reduce by 50% and it's still fine and looks better. + float distance = bot->GetMeleeRange(boss) * 0.5f; + // Alternatively, summing both unit's melee ranges seems to give a fairly natural range. + // Use whichever gives the best results.. + // float distanceOffset = bot->GetMeleeReach() + boss->GetMeleeReach(); + + Position leftFlank = boss->GetPosition(); + Position rightFlank = boss->GetPosition(); + Position* destination = nullptr; + leftFlank.RelocatePolarOffset(angle, distance); + rightFlank.RelocatePolarOffset(-angle, distance); + + if (bot->GetExactDist2d(leftFlank) < bot->GetExactDist2d(rightFlank)) + { + destination = &leftFlank; + } + else + { + destination = &rightFlank; + } + + return MoveTo(bot->GetMapId(), destination->GetPositionX(), destination->GetPositionY(), destination->GetPositionZ()); +} diff --git a/src/strategy/dungeons/wotlk/nexus/NexusActions.h b/src/strategy/dungeons/wotlk/nexus/NexusActions.h new file mode 100644 index 00000000..a7d31677 --- /dev/null +++ b/src/strategy/dungeons/wotlk/nexus/NexusActions.h @@ -0,0 +1,79 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONNEXACTIONS_H +#define _PLAYERBOT_WOTLKDUNGEONNEXACTIONS_H + +#include "Action.h" +#include "AttackAction.h" +#include "PlayerbotAI.h" +#include "Playerbots.h" +#include "NexusTriggers.h" + +#define ANGLE_45_DEG (static_cast(M_PI) / 4.f) +#define ANGLE_90_DEG M_PI_2 +#define ANGLE_120_DEG (2.f * static_cast(M_PI) / 3.f) + +// Slice of the circle that we want melee dps to attack from. +// Measured from boss orientation, on one side. + +// Even though the breath cone is not the full 180 degrees, +// avoid melee dps from the front due to parry potential. +// Bots should learn good dps etiquette :) +#define DRAGON_MELEE_MIN_ANGLE ANGLE_90_DEG +// This leaves a danger zone of 60 degrees at the tail end on both sides. +// This is a total of 120 degrees tail arc that bots will avoid - +// number just happens to be the same in this case, but this is always measured from the front. +#define DRAGON_MELEE_MAX_ANGLE ANGLE_120_DEG + +class MoveFromWhirlwindAction : public MovementAction +{ +public: + MoveFromWhirlwindAction(PlayerbotAI* ai) : MovementAction(ai, "move from whirlwind") {} + bool Execute(Event event) override; +}; + +class FirebombSpreadAction : public MovementAction +{ +public: + FirebombSpreadAction(PlayerbotAI* ai) : MovementAction(ai, "firebomb spread") {} + bool Execute(Event event) override; +}; + +class TelestraSplitTargetAction : public AttackAction +{ +public: + TelestraSplitTargetAction(PlayerbotAI* ai) : AttackAction(ai, "telestra split target") {} + bool Execute(Event event) override; + bool isUseful() override; +}; + +class ChaoticRiftTargetAction : public AttackAction +{ +public: + ChaoticRiftTargetAction(PlayerbotAI* ai) : AttackAction(ai, "chaotic rift target") {} + bool Execute(Event event) override; + bool isUseful() override; +}; + +class DodgeSpikesAction : public MovementAction +{ +public: + DodgeSpikesAction(PlayerbotAI* ai) : MovementAction(ai, "dodge spikes") {} + bool Execute(Event event) override; + bool isUseful() override; +}; + +class IntenseColdJumpAction : public MovementAction +{ +public: + IntenseColdJumpAction(PlayerbotAI* ai) : MovementAction(ai, "intense cold jump") {} + bool Execute(Event event) override; +}; + +class RearFlankPositionAction : public MovementAction +{ +public: + RearFlankPositionAction(PlayerbotAI* ai) : MovementAction(ai, "rear flank position") {} + bool Execute(Event event) override; + bool isUseful() override; +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/nexus/NexusMultipliers.cpp b/src/strategy/dungeons/wotlk/nexus/NexusMultipliers.cpp new file mode 100644 index 00000000..4b8ab97a --- /dev/null +++ b/src/strategy/dungeons/wotlk/nexus/NexusMultipliers.cpp @@ -0,0 +1,99 @@ +#include "NexusMultipliers.h" +#include "NexusActions.h" +#include "GenericSpellActions.h" +#include "ChooseTargetActions.h" +#include "MovementActions.h" +#include "NexusTriggers.h" + +float FactionCommanderMultiplier::GetValue(Action* action) +{ + Unit* boss = nullptr; + uint8 faction = bot->GetTeamId(); + + switch (bot->GetMap()->GetDifficulty()) + { + case DUNGEON_DIFFICULTY_NORMAL: + if (faction == TEAM_ALLIANCE) + { + boss = AI_VALUE2(Unit*, "find target", "horde commander"); + } + else //if (faction == TEAM_HORDE) + { + boss = AI_VALUE2(Unit*, "find target", "alliance commander"); + } + break; + case DUNGEON_DIFFICULTY_HEROIC: + if (faction == TEAM_ALLIANCE) + { + boss = AI_VALUE2(Unit*, "find target", "commander kolurg"); + } + else //if (faction == TEAM_HORDE) + { + boss = AI_VALUE2(Unit*, "find target", "commander stoutbeard"); + } + break; + default: + break; + } + if (boss && boss->HasUnitState(UNIT_STATE_CASTING) && + boss->FindCurrentSpellBySpellId(SPELL_WHIRLWIND)) + { + // Prevent movement actions other than flee during a whirlwind, to prevent running back in early. + if (dynamic_cast(action) && !dynamic_cast(action)) + { + return 0.0f; + } + } + return 1.0f; +} + +float TelestraMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "grand magus telestra"); + if (boss && boss->GetEntry() != NPC_TELESTRA) + { + // boss is split into clones, do not auto acquire target + if (dynamic_cast(action)) + { + return 0.0f; + } + } + return 1.0f; +} + +float AnomalusMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "anomalus"); + if (boss && boss->HasAura(BUFF_RIFT_SHIELD)) + { + if (dynamic_cast(action)) + { + return 0.0f; + } + } + return 1.0f; +} + +float OrmorokMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "ormorok the tree-shaper"); + if (!boss) + { + return 1.0f; + } + // These are used for auto ranged repositioning, need to suppress so ranged dps don't ping-pong + if (dynamic_cast(action)) + { + return 0.0f; + } + // This boss is annoying and shuffles around a lot. Don't let tank move once fight has started. + // Extra checks are to allow the tank to close distance and engage the boss initially + if (dynamic_cast(action) && !dynamic_cast(action) + && botAI->IsTank(bot) && bot->IsWithinMeleeRange(boss) + && AI_VALUE2(bool, "facing", "current target")) + { + return 0.0f; + } + return 1.0f; +} + diff --git a/src/strategy/dungeons/wotlk/nexus/NexusMultipliers.h b/src/strategy/dungeons/wotlk/nexus/NexusMultipliers.h new file mode 100644 index 00000000..c7321a40 --- /dev/null +++ b/src/strategy/dungeons/wotlk/nexus/NexusMultipliers.h @@ -0,0 +1,51 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONNEXMULTIPLIERS_H +#define _PLAYERBOT_WOTLKDUNGEONNEXMULTIPLIERS_H + +#include "Multiplier.h" + +class FactionCommanderMultiplier : public Multiplier +{ + public: + FactionCommanderMultiplier(PlayerbotAI* ai) : Multiplier(ai, "faction commander") {} + + public: + virtual float GetValue(Action* action); +}; + +class TelestraMultiplier : public Multiplier +{ + public: + TelestraMultiplier(PlayerbotAI* ai) : Multiplier(ai, "grand magus telestra") {} + + public: + virtual float GetValue(Action* action); +}; + +class AnomalusMultiplier : public Multiplier +{ + public: + AnomalusMultiplier(PlayerbotAI* ai) : Multiplier(ai, "anomalus") {} + + public: + virtual float GetValue(Action* action); +}; + +class OrmorokMultiplier : public Multiplier +{ + public: + OrmorokMultiplier(PlayerbotAI* ai) : Multiplier(ai, "ormorok the tree-shaper") {} + + public: + virtual float GetValue(Action* action); +}; + +class KeristraszaMultiplier : public Multiplier +{ + public: + KeristraszaMultiplier(PlayerbotAI* ai) : Multiplier(ai, "keristrasza") {} + + public: + virtual float GetValue(Action* action); +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/nexus/NexusStrategy.cpp b/src/strategy/dungeons/wotlk/nexus/NexusStrategy.cpp new file mode 100644 index 00000000..44140b6e --- /dev/null +++ b/src/strategy/dungeons/wotlk/nexus/NexusStrategy.cpp @@ -0,0 +1,54 @@ +#include "NexusStrategy.h" +#include "NexusMultipliers.h" + + +void WotlkDungeonNexStrategy::InitTriggers(std::vector &triggers) +{ + // Horde Commander (Alliance N)/Commander Kolurg (Alliance H) + // or + // Alliance Commander (Horde N)/Commander Stoutbeard (Horde H) + triggers.push_back(new TriggerNode("faction commander whirlwind", + NextAction::array(0, new NextAction("move from whirlwind", ACTION_MOVE + 5), nullptr))); + // TODO: Handle fear? (tremor totems, fear ward etc.) + + // Grand Magus Telestra + triggers.push_back(new TriggerNode("telestra firebomb", + NextAction::array(0, new NextAction("firebomb spread", ACTION_MOVE + 5), nullptr))); + triggers.push_back(new TriggerNode("telestra split phase", + NextAction::array(0, new NextAction("telestra split target", ACTION_RAID + 1), nullptr))); + // TODO: Add priority interrupt on the frost split's Blizzard casts + + // Anomalus + triggers.push_back(new TriggerNode("chaotic rift", + NextAction::array(0, new NextAction("chaotic rift target", ACTION_RAID + 1), nullptr))); + + // Ormorok the Tree-Shaper + // Tank trigger to stack inside boss. Can also add return action to prevent boss repositioning + // if it becomes too much of a problem. He usually dies before he's up against a wall though + triggers.push_back(new TriggerNode("ormorok spikes", + NextAction::array(0, new NextAction("dodge spikes", ACTION_MOVE + 5), nullptr))); + // Non-tank trigger to stack. Avoiding the spikes at range is.. harder than it seems. + // TODO: This turns hunters into melee marshmallows, have not come up with a better solution yet + triggers.push_back(new TriggerNode("ormorok stack", + NextAction::array(0, new NextAction("dodge spikes", ACTION_MOVE + 5), nullptr))); + // TODO: Add handling for spell reflect... best to spam low level/weak spells but don't want + // to hardcode spells per class, might be difficult to dynamically generate this. + // Will revisit if I find my altbots killing themselves in heroic, just heal through it for now + + // Keristrasza + triggers.push_back(new TriggerNode("intense cold", + NextAction::array(0, new NextAction("intense cold jump", ACTION_MOVE + 5), nullptr))); + // Flank dragon positioning for non-tank melee + triggers.push_back(new TriggerNode("dragon positioning", + NextAction::array(0, new NextAction("rear flank position", ACTION_MOVE + 4), nullptr))); + // TODO: Add frost resist aura for paladins? +} + +void WotlkDungeonNexStrategy::InitMultipliers(std::vector &multipliers) +{ + multipliers.push_back(new FactionCommanderMultiplier(botAI)); + multipliers.push_back(new TelestraMultiplier(botAI)); + multipliers.push_back(new AnomalusMultiplier(botAI)); + multipliers.push_back(new OrmorokMultiplier(botAI)); + // multipliers.push_back(new KeristraszaMultiplier(botAI)); +} diff --git a/src/strategy/dungeons/wotlk/nexus/NexusStrategy.h b/src/strategy/dungeons/wotlk/nexus/NexusStrategy.h new file mode 100644 index 00000000..f9ecf426 --- /dev/null +++ b/src/strategy/dungeons/wotlk/nexus/NexusStrategy.h @@ -0,0 +1,18 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONNEXSTRATEGY_H +#define _PLAYERBOT_WOTLKDUNGEONNEXSTRATEGY_H + +#include "Multiplier.h" +#include "AiObjectContext.h" +#include "Strategy.h" + + +class WotlkDungeonNexStrategy : public Strategy +{ +public: + WotlkDungeonNexStrategy(PlayerbotAI* ai) : Strategy(ai) {} + virtual std::string const getName() override { return "nexus"; } + virtual void InitTriggers(std::vector &triggers) override; + virtual void InitMultipliers(std::vector &multipliers) override; +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/nexus/NexusTriggerContext.h b/src/strategy/dungeons/wotlk/nexus/NexusTriggerContext.h new file mode 100644 index 00000000..72815f91 --- /dev/null +++ b/src/strategy/dungeons/wotlk/nexus/NexusTriggerContext.h @@ -0,0 +1,33 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONNEXTRIGGERCONTEXT_H +#define _PLAYERBOT_WOTLKDUNGEONNEXTRIGGERCONTEXT_H + +#include "NamedObjectContext.h" +#include "AiObjectContext.h" +#include "NexusTriggers.h" + +class WotlkDungeonNexTriggerContext : public NamedObjectContext +{ + public: + WotlkDungeonNexTriggerContext() + { + creators["faction commander whirlwind"] = &WotlkDungeonNexTriggerContext::faction_commander_whirlwind; + creators["telestra firebomb"] = &WotlkDungeonNexTriggerContext::telestra_firebomb; + creators["telestra split phase"] = &WotlkDungeonNexTriggerContext::telestra_split_phase; + creators["chaotic rift"] = &WotlkDungeonNexTriggerContext::chaotic_rift; + creators["ormorok spikes"] = &WotlkDungeonNexTriggerContext::ormorok_spikes; + creators["ormorok stack"] = &WotlkDungeonNexTriggerContext::ormorok_stack; + creators["intense cold"] = &WotlkDungeonNexTriggerContext::intense_cold; + creators["dragon positioning"] = &WotlkDungeonNexTriggerContext::dragon_positioning; + } + private: + static Trigger* faction_commander_whirlwind(PlayerbotAI* ai) { return new FactionCommanderWhirlwindTrigger(ai); } + static Trigger* telestra_firebomb(PlayerbotAI* ai) { return new TelestraFirebombTrigger(ai); } + static Trigger* telestra_split_phase(PlayerbotAI* ai) { return new TelestraSplitPhaseTrigger(ai); } + static Trigger* chaotic_rift(PlayerbotAI* ai) { return new ChaoticRiftTrigger(ai); } + static Trigger* ormorok_spikes(PlayerbotAI* ai) { return new OrmorokSpikesTrigger(ai); } + static Trigger* ormorok_stack(PlayerbotAI* ai) { return new OrmorokStackTrigger(ai); } + static Trigger* intense_cold(PlayerbotAI* ai) { return new IntenseColdTrigger(ai); } + static Trigger* dragon_positioning(PlayerbotAI* ai) { return new DragonPositioningTrigger(ai); } +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/nexus/NexusTriggers.cpp b/src/strategy/dungeons/wotlk/nexus/NexusTriggers.cpp new file mode 100644 index 00000000..22f1b29d --- /dev/null +++ b/src/strategy/dungeons/wotlk/nexus/NexusTriggers.cpp @@ -0,0 +1,105 @@ +#include "Playerbots.h" +#include "NexusTriggers.h" +#include "AiObject.h" +#include "AiObjectContext.h" + +bool FactionCommanderWhirlwindTrigger::IsActive() +{ + Unit* boss = nullptr; + uint8 faction = bot->GetTeamId(); + + switch (bot->GetMap()->GetDifficulty()) + { + case DUNGEON_DIFFICULTY_NORMAL: + if (faction == TEAM_ALLIANCE) + { + boss = AI_VALUE2(Unit*, "find target", "horde commander"); + } + else //if (faction == TEAM_HORDE) + { + boss = AI_VALUE2(Unit*, "find target", "alliance commander"); + } + break; + case DUNGEON_DIFFICULTY_HEROIC: + if (faction == TEAM_ALLIANCE) + { + boss = AI_VALUE2(Unit*, "find target", "commander kolurg"); + } + else //if (faction == TEAM_HORDE) + { + boss = AI_VALUE2(Unit*, "find target", "commander stoutbeard"); + } + break; + default: + break; + } + + if (boss && boss->HasUnitState(UNIT_STATE_CASTING)) + { + if (boss->FindCurrentSpellBySpellId(SPELL_WHIRLWIND)) + { + return true; + } + } + return false; +} + +bool TelestraFirebombTrigger::IsActive() +{ + if (botAI->IsMelee(bot)) { return false; } + + Unit* boss = AI_VALUE2(Unit*, "find target", "grand magus telestra"); + // Avoid split phase with the fake Telestra units, only match the true boss id + return boss && boss->GetEntry() == NPC_TELESTRA; +} + +bool TelestraSplitPhaseTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "grand magus telestra"); + // Only match split phase with the fake Telestra units + return boss && boss->GetEntry() != NPC_TELESTRA; +} + +bool ChaoticRiftTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "anomalus"); + return boss && boss->HasAura(BUFF_RIFT_SHIELD); +} + +bool OrmorokSpikesTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "ormorok the tree-shaper"); + if (!boss || !botAI->IsTank(bot)) { return false; } + + GuidVector objects = AI_VALUE(GuidVector, "closest game objects"); + for (auto i = objects.begin(); i != objects.end(); ++i) + { + GameObject* go = botAI->GetGameObject(*i); + if (go && go->GetEntry() == GO_CRYSTAL_SPIKE) + { + return true; + } + } + return false; +} + +bool OrmorokStackTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "ormorok the tree-shaper"); + return (boss && !botAI->IsTank(bot)); +} + +bool IntenseColdTrigger::IsActive() +{ + // Adjust as needed - too much interrupting loses dps time, + // but too many stacks is deadly. Assuming 3-5 is a good number to clear + int stackThreshold = 5; + Unit* boss = AI_VALUE2(Unit*, "find target", "keristrasza"); + return boss && botAI->GetAura("intense cold", bot, false, false, stackThreshold); +} + +bool DragonPositioningTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "keristrasza"); + return boss && botAI->IsMelee(bot) && !botAI->IsTank(bot); +} diff --git a/src/strategy/dungeons/wotlk/nexus/NexusTriggers.h b/src/strategy/dungeons/wotlk/nexus/NexusTriggers.h index 894b86f7..c9f14038 100644 --- a/src/strategy/dungeons/wotlk/nexus/NexusTriggers.h +++ b/src/strategy/dungeons/wotlk/nexus/NexusTriggers.h @@ -1,93 +1,88 @@ #ifndef _PLAYERBOT_WOTLKDUNGEONNEXTRIGGERS_H #define _PLAYERBOT_WOTLKDUNGEONNEXTRIGGERS_H -#include "EventMap.h" #include "Trigger.h" #include "PlayerbotAIConfig.h" #include "GenericTriggers.h" #include "DungeonStrategyUtils.h" -// Taken from: -// src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_ingvar_the_plunderer.cpp -enum Spells +enum NexusIDs { - SPELL_SUMMON_VALKYR = 42912, - SPELL_RESURRECTION_BEAM = 42857, - SPELL_RESURRECTION_BALL = 42862, - SPELL_RESURRECTION_HEAL = 42704, - SPELL_INGVAR_TRANSFORM = 42796, + // Faction Commander + NPC_ALLIANCE_COMMANDER = 27949, + NPC_HORDE_COMMANDER = 27947, + NPC_COMMANDER_STOUTBEARD = 26796, + NPC_COMMANDER_KOLURG = 26798, + // SPELL_FRIGHTENING_SHOUT = 19134, + SPELL_WHIRLWIND = 38618, - SPELL_STAGGERING_ROAR_N = 42708, - SPELL_STAGGERING_ROAR_H = 59708, - SPELL_CLEAVE = 42724, - SPELL_SMASH_N = 42669, - SPELL_SMASH_H = 59706, - SPELL_ENRAGE_N = 42705, - SPELL_ENRAGE_H = 59707, + // Grand Magus Telestra + NPC_TELESTRA = 26731, + NPC_FIRE_MAGUS = 26928, + NPC_FROST_MAGUS = 26930, + NPC_ARCANE_MAGUS = 26929, - SPELL_DREADFUL_ROAR_N = 42729, - SPELL_DREADFUL_ROAR_H = 59734, - SPELL_WOE_STRIKE_N = 42730, - SPELL_WOE_STRIKE_H = 59735, - SPELL_DARK_SMASH = 42723, - SPELL_SHADOW_AXE = 42749, + // Anomalus + BUFF_RIFT_SHIELD = 47748, - // Added - DEBUFF_FROST_TOMB = 48400, + // Ormorok the Tree Shaper + // NPC_CRYSTAL_SPIKE = 27099, + GO_CRYSTAL_SPIKE = 188537, }; -#define SPELL_STAGGERING_ROAR DUNGEON_MODE(bot, SPELL_STAGGERING_ROAR_N, SPELL_STAGGERING_ROAR_H) -#define SPELL_DREADFUL_ROAR DUNGEON_MODE(bot, SPELL_DREADFUL_ROAR_N, SPELL_DREADFUL_ROAR_H) -#define SPELL_WOE_STRIKE DUNGEON_MODE(bot, SPELL_WOE_STRIKE_N, SPELL_WOE_STRIKE_H) -#define SPELL_SMASH DUNGEON_MODE(bot, SPELL_SMASH_N, SPELL_SMASH_H) -#define SPELL_ENRAGE DUNGEON_MODE(bot, SPELL_ENRAGE_N, SPELL_ENRAGE_H) - -class KelesethFrostTombTrigger : public Trigger +class FactionCommanderWhirlwindTrigger : public Trigger { public: - KelesethFrostTombTrigger(PlayerbotAI* ai) : Trigger(ai, "keleseth frost tomb") {} + FactionCommanderWhirlwindTrigger(PlayerbotAI* ai) : Trigger(ai, "faction commander whirlwind") {} bool IsActive() override; }; -class DalronnNontankTrigger : public Trigger +class TelestraFirebombTrigger : public Trigger { public: - DalronnNontankTrigger(PlayerbotAI* ai) : Trigger(ai, "dalronn non-tank") {} + TelestraFirebombTrigger(PlayerbotAI* ai) : Trigger(ai, "telestra firebomb spread") {} bool IsActive() override; }; -class IngvarStaggeringRoarTrigger : public Trigger +class TelestraSplitPhaseTrigger : public Trigger { public: - IngvarStaggeringRoarTrigger(PlayerbotAI* ai) : Trigger(ai, "ingvar staggering roar") {} + TelestraSplitPhaseTrigger(PlayerbotAI* ai) : Trigger(ai, "telestra split phase") {} bool IsActive() override; }; -class IngvarDreadfulRoarTrigger : public Trigger +class ChaoticRiftTrigger : public Trigger { public: - IngvarDreadfulRoarTrigger(PlayerbotAI* ai) : Trigger(ai, "ingvar dreadful roar") {} + ChaoticRiftTrigger(PlayerbotAI* ai) : Trigger(ai, "chaotic rift") {} bool IsActive() override; }; -class IngvarSmashTankTrigger : public Trigger +class OrmorokSpikesTrigger : public Trigger { public: - IngvarSmashTankTrigger(PlayerbotAI* ai) : Trigger(ai, "ingvar smash tank") {} + OrmorokSpikesTrigger(PlayerbotAI* ai) : Trigger(ai, "ormorok spikes") {} bool IsActive() override; }; -class IngvarSmashTankReturnTrigger : public Trigger +class OrmorokStackTrigger : public Trigger { public: - IngvarSmashTankReturnTrigger(PlayerbotAI* ai) : Trigger(ai, "ingvar smash tank return") {} + OrmorokStackTrigger(PlayerbotAI* ai) : Trigger(ai, "ormorok stack") {} bool IsActive() override; }; -class NotBehindIngvarTrigger : public Trigger +class IntenseColdTrigger : public Trigger { public: - NotBehindIngvarTrigger(PlayerbotAI* ai) : Trigger(ai, "not behind ingvar") {} + IntenseColdTrigger(PlayerbotAI* ai) : Trigger(ai, "intense cold") {} + bool IsActive() override; +}; + +class DragonPositioningTrigger : public Trigger +{ +public: + DragonPositioningTrigger(PlayerbotAI* ai) : Trigger(ai, "dragon positioning") {} bool IsActive() override; }; From 40213873872636eed5b47ef0df150797e9dc9f50 Mon Sep 17 00:00:00 2001 From: Bobblybook Date: Sun, 6 Oct 2024 04:31:37 +1100 Subject: [PATCH 20/40] Typo --- src/strategy/dungeons/wotlk/nexus/NexusActions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/strategy/dungeons/wotlk/nexus/NexusActions.cpp b/src/strategy/dungeons/wotlk/nexus/NexusActions.cpp index 543fadba..8c0b1311 100644 --- a/src/strategy/dungeons/wotlk/nexus/NexusActions.cpp +++ b/src/strategy/dungeons/wotlk/nexus/NexusActions.cpp @@ -167,7 +167,7 @@ bool RearFlankPositionAction::isUseful() // Total 180 degrees (whole front half) bool inFront = boss->HasInArc(2.f * DRAGON_MELEE_MIN_ANGLE, bot); // Rear check does not need to double this angle as the logic is inverted - // and we are subtracing from 2pi. + // and we are subtracting from 2pi. bool inBack = !boss->HasInArc((2.f * M_PI) - DRAGON_MELEE_MAX_ANGLE, bot); return inFront || inBack; From 6e7404d7fc519b75cce0b04d9db2db281e4bde4b Mon Sep 17 00:00:00 2001 From: bash Date: Sun, 6 Oct 2024 00:06:59 +0000 Subject: [PATCH 21/40] increased interval to 20 to mitigate memory issues --- conf/playerbots.conf.dist | 2 +- src/PlayerbotAIConfig.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index f158327a..6da1c7a3 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -728,7 +728,7 @@ AiPlayerbot.FastReactInBG = 1 # # All In seconds -AiPlayerbot.RandomBotUpdateInterval = 10 +AiPlayerbot.RandomBotUpdateInterval = 20 AiPlayerbot.RandomBotCountChangeMinInterval = 1800 AiPlayerbot.RandomBotCountChangeMaxInterval = 7200 AiPlayerbot.MinRandomBotInWorldTime = 3600 diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index 84a8f405..10c680ae 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -156,7 +156,7 @@ bool PlayerbotAIConfig::Initialize() randomBotAutologin = sConfigMgr->GetOption("AiPlayerbot.RandomBotAutologin", true); minRandomBots = sConfigMgr->GetOption("AiPlayerbot.MinRandomBots", 50); maxRandomBots = sConfigMgr->GetOption("AiPlayerbot.MaxRandomBots", 200); - randomBotUpdateInterval = sConfigMgr->GetOption("AiPlayerbot.RandomBotUpdateInterval", 10); + randomBotUpdateInterval = sConfigMgr->GetOption("AiPlayerbot.RandomBotUpdateInterval", 20); randomBotCountChangeMinInterval = sConfigMgr->GetOption("AiPlayerbot.RandomBotCountChangeMinInterval", 30 * MINUTE); randomBotCountChangeMaxInterval = From b2dae08221805246df2a5ab8b3267bd6472c40ab Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Sun, 6 Oct 2024 12:21:33 +0800 Subject: [PATCH 22/40] [Performance] Pull up CanUpdateAI() check --- src/PlayerbotAI.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 1200e050..a8c7e8c0 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -314,6 +314,10 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal) AllowActivity(); + + if (!CanUpdateAI()) + return; + Spell* currentSpell = bot->GetCurrentSpell(CURRENT_GENERIC_SPELL); if (!currentSpell) currentSpell = bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL); From 69ef0d46cd6c1ab461c53246ff6fb2567ea2d034 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Sun, 6 Oct 2024 12:22:48 +0800 Subject: [PATCH 23/40] [Performance] YieldThread when casting spell --- src/PlayerbotAI.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index a8c7e8c0..599289ba 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -360,6 +360,7 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal) } // wait for spell cast + YieldThread(GetReactDelay()); return; } @@ -386,9 +387,6 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal) bot->StopMovingOnCurrentPos(); } } - - if (!CanUpdateAI()) - return; if (!bot->InBattleground() && !bot->inRandomLfgDungeon() && bot->GetGroup()) { From eb0d222598186f31012222d6823d4b455e2182ff Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Sun, 6 Oct 2024 12:35:04 +0800 Subject: [PATCH 24/40] [Spell] Dont interrupt spell on drop target --- src/PlayerbotAI.cpp | 2 +- src/strategy/actions/ChooseTargetActions.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 1200e050..0656e721 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -1515,7 +1515,7 @@ void PlayerbotAI::DoNextAction(bool min) void PlayerbotAI::ReInitCurrentEngine() { - InterruptSpell(); + // InterruptSpell(); currentEngine->Init(); } diff --git a/src/strategy/actions/ChooseTargetActions.cpp b/src/strategy/actions/ChooseTargetActions.cpp index dddb3627..c06f675d 100644 --- a/src/strategy/actions/ChooseTargetActions.cpp +++ b/src/strategy/actions/ChooseTargetActions.cpp @@ -82,7 +82,7 @@ bool DropTargetAction::Execute(Event event) bot->SetTarget(ObjectGuid::Empty); bot->SetSelection(ObjectGuid()); botAI->ChangeEngine(BOT_STATE_NON_COMBAT); - botAI->InterruptSpell(); + // botAI->InterruptSpell(); bot->AttackStop(); // if (Pet* pet = bot->GetPet()) From b895ac17e5619670c81962c78b018250387509bb Mon Sep 17 00:00:00 2001 From: Bobblybook Date: Mon, 7 Oct 2024 01:32:00 +1100 Subject: [PATCH 25/40] Azjol-Nerub implementation --- src/strategy/AiObjectContext.cpp | 2 + .../dungeons/DungeonStrategyContext.h | 7 +- .../wotlk/WotlkDungeonActionContext.h | 2 +- .../wotlk/WotlkDungeonTriggerContext.h | 2 +- .../azjolnerub/AzjolNerubActionContext.h | 22 ++++ .../wotlk/azjolnerub/AzjolNerubActions.cpp | 111 ++++++++++++++++++ .../wotlk/azjolnerub/AzjolNerubActions.h | 34 ++++++ .../azjolnerub/AzjolNerubMultipliers.cpp | 54 +++++++++ .../wotlk/azjolnerub/AzjolNerubMultipliers.h | 15 +++ .../wotlk/azjolnerub/AzjolNerubStrategy.cpp | 31 +++++ .../wotlk/azjolnerub/AzjolNerubStrategy.h | 18 +++ .../azjolnerub/AzjolNerubTriggerContext.h | 25 ++++ .../wotlk/azjolnerub/AzjolNerubTriggers.cpp | 69 +++++++++++ .../wotlk/azjolnerub/AzjolNerubTriggers.h | 61 ++++++++++ src/strategy/dungeons/wotlk/azjolnerub/TODO | 0 15 files changed, 448 insertions(+), 5 deletions(-) create mode 100644 src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubActionContext.h create mode 100644 src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubActions.cpp create mode 100644 src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubActions.h create mode 100644 src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubMultipliers.cpp create mode 100644 src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubMultipliers.h create mode 100644 src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubStrategy.cpp create mode 100644 src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubStrategy.h create mode 100644 src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubTriggerContext.h create mode 100644 src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubTriggers.cpp create mode 100644 src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubTriggers.h delete mode 100644 src/strategy/dungeons/wotlk/azjolnerub/TODO diff --git a/src/strategy/AiObjectContext.cpp b/src/strategy/AiObjectContext.cpp index 0742d363..819ea98a 100644 --- a/src/strategy/AiObjectContext.cpp +++ b/src/strategy/AiObjectContext.cpp @@ -52,6 +52,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI) actionContexts.Add(new RaidIccActionContext()); actionContexts.Add(new WotlkDungeonUKActionContext()); actionContexts.Add(new WotlkDungeonNexActionContext()); + actionContexts.Add(new WotlkDungeonANActionContext()); triggerContexts.Add(new TriggerContext()); triggerContexts.Add(new ChatTriggerContext()); @@ -64,6 +65,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI) triggerContexts.Add(new RaidIccTriggerContext()); triggerContexts.Add(new WotlkDungeonUKTriggerContext()); triggerContexts.Add(new WotlkDungeonNexTriggerContext()); + triggerContexts.Add(new WotlkDungeonANTriggerContext()); valueContexts.Add(new ValueContext()); diff --git a/src/strategy/dungeons/DungeonStrategyContext.h b/src/strategy/dungeons/DungeonStrategyContext.h index afa2616f..8764d351 100644 --- a/src/strategy/dungeons/DungeonStrategyContext.h +++ b/src/strategy/dungeons/DungeonStrategyContext.h @@ -4,12 +4,12 @@ #include "Strategy.h" #include "wotlk/utgardekeep/UtgardeKeepStrategy.h" #include "wotlk/nexus/NexusStrategy.h" +#include "wotlk/azjolnerub/AzjolNerubStrategy.h" /* Full list/TODO: -Azjol-Nerub: Azjol-Nerub - AN -Krik'thir the Gatewatcher, Hadronox, Anub'arak + Ahn'kahet: The Old Kingdom - OK Elder Nadox, Prince Taldaram, Jedoga Shadowseeker, Herald Volazj, Amanitar (Heroic Only) Drak'Tharon Keep - DTK @@ -76,7 +76,8 @@ class DungeonStrategyContext : public NamedObjectContext private: static Strategy* wotlk_uk(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } static Strategy* wotlk_nex(PlayerbotAI* botAI) { return new WotlkDungeonNexStrategy(botAI); } - static Strategy* wotlk_an(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } + static Strategy* wotlk_an(PlayerbotAI* botAI) { return new WotlkDungeonANStrategy(botAI); } + static Strategy* wotlk_ok(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } static Strategy* wotlk_dtk(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } static Strategy* wotlk_vh(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } diff --git a/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h b/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h index d9a18ae8..40529513 100644 --- a/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h +++ b/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h @@ -3,7 +3,7 @@ #include "utgardekeep/UtgardeKeepActionContext.h" #include "nexus/NexusActionContext.h" -// #include "azjolnerub/AzjolNerubActionContext.h" +#include "azjolnerub/AzjolNerubActionContext.h" // #include "oldkingdom/OldKingdomActionContext.h" // #include "draktharonkeep/DraktharonKeepActionContext.h" // #include "violethold/VioletHoldActionContext.h" diff --git a/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h b/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h index 4cbb41e2..b53d0fa5 100644 --- a/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h +++ b/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h @@ -3,7 +3,7 @@ #include "utgardekeep/UtgardeKeepTriggerContext.h" #include "nexus/NexusTriggerContext.h" -// #include "azjolnerub/AzjolNerubTriggerContext.h" +#include "azjolnerub/AzjolNerubTriggerContext.h" // #include "oldkingdom/OldKingdomTriggerContext.h" // #include "draktharonkeep/DraktharonKeepTriggerContext.h" // #include "violethold/VioletHoldTriggerContext.h" diff --git a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubActionContext.h b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubActionContext.h new file mode 100644 index 00000000..6306643b --- /dev/null +++ b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubActionContext.h @@ -0,0 +1,22 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONANACTIONCONTEXT_H +#define _PLAYERBOT_WOTLKDUNGEONANACTIONCONTEXT_H + +#include "Action.h" +#include "NamedObjectContext.h" +#include "AzjolNerubActions.h" + +class WotlkDungeonANActionContext : public NamedObjectContext +{ + public: + WotlkDungeonANActionContext() { + creators["attack web wrap"] = &WotlkDungeonANActionContext::attack_web_wrap; + creators["krik'thir priority"] = &WotlkDungeonANActionContext::krikthir_priority; + creators["dodge pound"] = &WotlkDungeonANActionContext::dodge_pound; + } + private: + static Action* attack_web_wrap(PlayerbotAI* ai) { return new AttackWebWrapAction(ai); } + static Action* krikthir_priority(PlayerbotAI* ai) { return new WatchersTargetAction(ai); } + static Action* dodge_pound(PlayerbotAI* ai) { return new AnubarakDodgePoundAction(ai); } +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubActions.cpp b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubActions.cpp new file mode 100644 index 00000000..161dd0d5 --- /dev/null +++ b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubActions.cpp @@ -0,0 +1,111 @@ +#include "Playerbots.h" +#include "AzjolNerubActions.h" +#include "AzjolNerubStrategy.h" + + +bool AttackWebWrapAction::isUseful() { return !botAI->IsHeal(bot); } +bool AttackWebWrapAction::Execute(Event event) +{ + Unit* webWrap = nullptr; + + // Target is not findable from threat table using AI_VALUE2(), + // therefore need to search manually for the unit name + GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); + + for (auto i = targets.begin(); i != targets.end(); ++i) + { + Unit* unit = botAI->GetUnit(*i); + if (unit && unit->GetEntry() == NPC_WEB_WRAP) + { + webWrap = unit; + break; + } + } + if (!webWrap || AI_VALUE(Unit*, "current target") == webWrap) + { + return false; + } + bot->Yell("ATTACKING WRAP", LANG_UNIVERSAL); + return Attack(webWrap); +} + +bool WatchersTargetAction::isUseful() { return !botAI->IsHeal(bot); } +bool WatchersTargetAction::Execute(Event event) +{ + // Always prioritise web wraps + Unit* currTarget = AI_VALUE(Unit*, "current target"); + if (currTarget && currTarget->GetEntry() == NPC_WEB_WRAP) { return false; } + + // Do not search all units in range! + // There are many adds we don't want to aggro in close proximity, + // only check in-combat adds now. + GuidVector attackers = AI_VALUE(GuidVector, "attackers"); + Unit* priorityTargets[4] = {nullptr, nullptr, nullptr, nullptr}; + + for (auto& attacker : attackers) + { + Unit* npc = botAI->GetUnit(attacker); + if (!npc) + { + continue; + } + switch (npc->GetEntry()) + { + // Focus skirmishers first + case NPC_WATCHER_SKIRMISHER: + priorityTargets[0] = npc; + break; + // Then shadowcaster. This doesn't work so well for the shadowcaster + // + skirmisher pack - ideally we would kill the watcher second. + // But don't want to make this unnecessarily complex and rigid... + // Will revisit if this causes problems in heroic. + case NPC_WATCHER_SHADOWCASTER: + priorityTargets[1] = npc; + break; + // Named watcher next + case NPC_WATCHER_SILTHIK: + case NPC_WATCHER_GASHRA: + case NPC_WATCHER_NARJIL: + priorityTargets[2] = npc; + break; + // Warrior last + case NPC_WATCHER_WARRIOR: + priorityTargets[3] = npc; + break; + } + } + + for (Unit* target : priorityTargets) + { + // Attack the first valid split target in the priority list + if (target) + { + if (currTarget != target) + { + // bot->Yell("ATTACKING "+target->GetName(), LANG_UNIVERSAL); + return Attack(target); + } + // Don't continue loop here, the target exists so we don't + // want to move down the prio list. We just don't need to send attack + // command again, just return false and exit the loop that way + return false; + } + } + + return false; +} + +bool AnubarakDodgePoundAction::isUseful() { return !AI_VALUE2(bool, "behind", "current target"); } +bool AnubarakDodgePoundAction::Execute(Event event) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "anub'arak"); + if (!boss) { return false; } + + float distance = bot->GetExactDist2d(boss->GetPosition()); + // Extra units to move into the boss, instead of being just 1 pixel past his midpoint. + // Can be adjusted - this value tends to mirror how a human would play, + // and visibly ensures you won't get hit while not creating excessive movements. + float distanceExtra = 2.0f; + bot->Yell("MOVING", LANG_UNIVERSAL); + return Move(bot->GetAngle(boss), distance + distanceExtra); +} diff --git a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubActions.h b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubActions.h new file mode 100644 index 00000000..ef388cbd --- /dev/null +++ b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubActions.h @@ -0,0 +1,34 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONANACTIONS_H +#define _PLAYERBOT_WOTLKDUNGEONANACTIONS_H + +#include "Action.h" +#include "AttackAction.h" +#include "PlayerbotAI.h" +#include "Playerbots.h" +#include "AzjolNerubTriggers.h" + +class AttackWebWrapAction : public AttackAction +{ +public: + AttackWebWrapAction(PlayerbotAI* ai) : AttackAction(ai, "attack web wrap") {} + bool Execute(Event event) override; + bool isUseful() override; +}; + +class WatchersTargetAction : public AttackAction +{ +public: + WatchersTargetAction(PlayerbotAI* ai) : AttackAction(ai, "krik'thir priority") {} + bool Execute(Event event) override; + bool isUseful() override; +}; + +class AnubarakDodgePoundAction : public AttackAction +{ +public: + AnubarakDodgePoundAction(PlayerbotAI* ai) : AttackAction(ai, "anub'arak dodge pound") {} + bool Execute(Event event) override; + bool isUseful() override; +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubMultipliers.cpp b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubMultipliers.cpp new file mode 100644 index 00000000..e8cece04 --- /dev/null +++ b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubMultipliers.cpp @@ -0,0 +1,54 @@ +#include "AzjolNerubMultipliers.h" +#include "AzjolNerubActions.h" +#include "GenericSpellActions.h" +#include "ChooseTargetActions.h" +#include "MovementActions.h" +#include "AzjolNerubTriggers.h" +#include "Action.h" + +float KrikthirMultiplier::GetValue(Action* action) +{ + // Target is not findable from threat table using AI_VALUE2(), + // therefore need to search manually for the unit name + Unit* boss = nullptr; + Unit* watcher = nullptr; + GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); + + for (auto i = targets.begin(); i != targets.end(); ++i) + { + Unit* unit = botAI->GetUnit(*i); + if (!unit) { continue; } + + switch (unit->GetEntry()) + { + case NPC_KRIKTHIR: + boss = unit; + continue; + case NPC_WATCHER_SILTHIK: + case NPC_WATCHER_GASHRA: + case NPC_WATCHER_NARJIL: + case NPC_WATCHER_SKIRMISHER: + case NPC_WATCHER_SHADOWCASTER: + case NPC_WATCHER_WARRIOR: + watcher = unit; + continue; + } + } + + if (boss && watcher) + { + // Do not target swap + // TODO: Need to suppress AoE actions but unsure how to identify them + if (dynamic_cast(action)) + { + return 0.0f; + } + // Doesn't seem to work + // if (action->getThreatType() == Action::ActionThreatType::Aoe) + // { + // bot->Yell("Suppressed AoE", LANG_UNIVERSAL); + // return 0.0f; + // } + } + return 1.0f; +} diff --git a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubMultipliers.h b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubMultipliers.h new file mode 100644 index 00000000..bb01115e --- /dev/null +++ b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubMultipliers.h @@ -0,0 +1,15 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONANMULTIPLIERS_H +#define _PLAYERBOT_WOTLKDUNGEONANMULTIPLIERS_H + +#include "Multiplier.h" + +class KrikthirMultiplier : public Multiplier +{ + public: + KrikthirMultiplier(PlayerbotAI* ai) : Multiplier(ai, "krik'thir the gatewatcher") {} + + public: + virtual float GetValue(Action* action); +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubStrategy.cpp b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubStrategy.cpp new file mode 100644 index 00000000..1f0dd038 --- /dev/null +++ b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubStrategy.cpp @@ -0,0 +1,31 @@ +#include "AzjolNerubStrategy.h" +#include "AzjolNerubMultipliers.h" + + +void WotlkDungeonANStrategy::InitTriggers(std::vector &triggers) +{ + // Krik'thir the Gatewatcher + // TODO: Add CC trigger while web wraps are casting? + // TODO: Bring healer closer than ranged dps to avoid fixates? + triggers.push_back(new TriggerNode("krik'thir web wrap", + NextAction::array(0, new NextAction("attack web wrap", ACTION_RAID + 5), nullptr))); + triggers.push_back(new TriggerNode("krik'thir watchers", + NextAction::array(0, new NextAction("krik'thir priority", ACTION_RAID + 4), nullptr))); + + // Hadronox + // The core AC triggers are very buggy with this boss, but default strat seems to play correctly + + //Anub'arak + // TODO: No clear way to track these spikes. They don't seem to appear as gameobjects or triggers, + // and cast time is instant so no way to check currently casting location. + // May need to hook boss AI.. might be able to just heal through it for now. + // triggers.push_back(new TriggerNode("anub'arak impale", + // NextAction::array(0, new NextAction("TODO", ACTION_MOVE + 5), nullptr))); + triggers.push_back(new TriggerNode("anub'arak pound", + NextAction::array(0, new NextAction("dodge pound", ACTION_MOVE + 5), nullptr))); +} + +void WotlkDungeonANStrategy::InitMultipliers(std::vector &multipliers) +{ + multipliers.push_back(new KrikthirMultiplier(botAI)); +} diff --git a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubStrategy.h b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubStrategy.h new file mode 100644 index 00000000..47d5d635 --- /dev/null +++ b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubStrategy.h @@ -0,0 +1,18 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONANSTRATEGY_H +#define _PLAYERBOT_WOTLKDUNGEONANSTRATEGY_H + +#include "Multiplier.h" +#include "AiObjectContext.h" +#include "Strategy.h" + + +class WotlkDungeonANStrategy : public Strategy +{ +public: + WotlkDungeonANStrategy(PlayerbotAI* ai) : Strategy(ai) {} + virtual std::string const getName() override { return "azjol-nerub"; } + virtual void InitTriggers(std::vector &triggers) override; + virtual void InitMultipliers(std::vector &multipliers) override; +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubTriggerContext.h b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubTriggerContext.h new file mode 100644 index 00000000..d0e988c0 --- /dev/null +++ b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubTriggerContext.h @@ -0,0 +1,25 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONANTRIGGERCONTEXT_H +#define _PLAYERBOT_WOTLKDUNGEONANTRIGGERCONTEXT_H + +#include "NamedObjectContext.h" +#include "AiObjectContext.h" +#include "AzjolNerubTriggers.h" + +class WotlkDungeonANTriggerContext : public NamedObjectContext +{ + public: + WotlkDungeonANTriggerContext() + { + creators["krik'thir web wrap"] = &WotlkDungeonANTriggerContext::krikthir_web_wrap; + creators["krik'thir watchers"] = &WotlkDungeonANTriggerContext::krikthir_watchers; + // creators["anub'arak impale"] = &WotlkDungeonANTriggerContext::anubarak_impale; + creators["anub'arak pound"] = &WotlkDungeonANTriggerContext::anubarak_pound; + } + private: + static Trigger* krikthir_web_wrap(PlayerbotAI* ai) { return new KrikthirWebWrapTrigger(ai); } + static Trigger* krikthir_watchers(PlayerbotAI* ai) { return new KrikthirWatchersTrigger(ai); } + // static Trigger* anubarak_impale(PlayerbotAI* ai) { return new AnubarakImpaleTrigger(ai); } + static Trigger* anubarak_pound(PlayerbotAI* ai) { return new AnubarakPoundTrigger(ai); } +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubTriggers.cpp b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubTriggers.cpp new file mode 100644 index 00000000..584269da --- /dev/null +++ b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubTriggers.cpp @@ -0,0 +1,69 @@ +#include "Playerbots.h" +#include "AzjolNerubTriggers.h" +#include "AiObject.h" +#include "AiObjectContext.h" + + +bool KrikthirWebWrapTrigger::IsActive() +{ + if (!botAI->IsDps(bot)) { return false; } + + // Target is not findable from threat table using AI_VALUE2(), + // therefore need to search manually for the unit name + GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); + + for (auto i = targets.begin(); i != targets.end(); ++i) + { + Unit* unit = botAI->GetUnit(*i); + if (unit && unit->GetEntry() == NPC_WEB_WRAP) + { + return true; + } + } + + return false; +} + +bool KrikthirWatchersTrigger::IsActive() +{ + if (!botAI->IsDps(bot)) { return false; } + + // Target is not findable from threat table using AI_VALUE2(), + // therefore need to search manually for the unit name + GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); + + for (auto i = targets.begin(); i != targets.end(); ++i) + { + Unit* unit = botAI->GetUnit(*i); + if (unit && unit->GetEntry() == NPC_KRIKTHIR) + { + return true; + } + } + return false; +} + +// bool AnubarakImpaleTrigger::IsActive() +// { +// Unit* boss = AI_VALUE2(Unit*, "find target", "anub'arak"); +// if (!boss) { return false; } +// GuidVector triggers = AI_VALUE(GuidVector, "possible triggers"); +// for (auto i = triggers.begin(); i != triggers.end(); i++) +// { +// Unit* unit = botAI->GetUnit(*i); + +// if (unit) +// { +// bot->Yell("TRIGGER="+unit->GetName(), LANG_UNIVERSAL); +// } +// } +// return false; +// } + +bool AnubarakPoundTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "anub'arak"); + if (!boss) { return false; } + + return boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_POUND); +} diff --git a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubTriggers.h b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubTriggers.h new file mode 100644 index 00000000..5d12b596 --- /dev/null +++ b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubTriggers.h @@ -0,0 +1,61 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONANTRIGGERS_H +#define _PLAYERBOT_WOTLKDUNGEONANTRIGGERS_H + +#include "Trigger.h" +#include "PlayerbotAIConfig.h" +#include "GenericTriggers.h" +#include "DungeonStrategyUtils.h" + +enum AzjolNerubIDs +{ + // Krik'thir the Gatewatcher + NPC_KRIKTHIR = 28684, + NPC_WATCHER_SILTHIK = 28731, + NPC_WATCHER_GASHRA = 28730, + NPC_WATCHER_NARJIL = 28729, + NPC_WATCHER_SKIRMISHER = 28734, + NPC_WATCHER_SHADOWCASTER = 28733, + NPC_WATCHER_WARRIOR = 28732, + DEBUFF_WEB_WRAP = 52086, + NPC_WEB_WRAP = 28619, + + // Anub'arak + // Not sure how to track this - first one is cast as a buff on himself, + // which triggers periodic casts of the spikes spell. + SPELL_IMPALE_PERIODIC = 53456, + SPELL_IMPALE_SPIKES = 53457, + SPELL_POUND_N = 53472, + SPELL_POUND_H = 59433, +}; + +#define SPELL_POUND DUNGEON_MODE(bot, SPELL_POUND_N, SPELL_POUND_H) + +class KrikthirWebWrapTrigger : public Trigger +{ +public: + KrikthirWebWrapTrigger(PlayerbotAI* ai) : Trigger(ai, "krik'thir web wrap") {} + bool IsActive() override; +}; + +class KrikthirWatchersTrigger : public Trigger +{ +public: + KrikthirWatchersTrigger(PlayerbotAI* ai) : Trigger(ai, "krik'thir watchers") {} + bool IsActive() override; +}; + +// class AnubarakImpaleTrigger : public Trigger +// { +// public: +// AnubarakImpaleTrigger(PlayerbotAI* ai) : Trigger(ai, "anub'arak impale") {} +// bool IsActive() override; +// }; + +class AnubarakPoundTrigger : public Trigger +{ +public: + AnubarakPoundTrigger(PlayerbotAI* ai) : Trigger(ai, "anub'arak pound") {} + bool IsActive() override; +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/azjolnerub/TODO b/src/strategy/dungeons/wotlk/azjolnerub/TODO deleted file mode 100644 index e69de29b..00000000 From 66cd3489edec7f787df5417f2fb8482b5ba5a83d Mon Sep 17 00:00:00 2001 From: Bobblybook Date: Mon, 7 Oct 2024 10:08:56 +1100 Subject: [PATCH 26/40] Remove debug --- src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubActions.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubActions.cpp b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubActions.cpp index 161dd0d5..4cf1c83e 100644 --- a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubActions.cpp +++ b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubActions.cpp @@ -106,6 +106,5 @@ bool AnubarakDodgePoundAction::Execute(Event event) // Can be adjusted - this value tends to mirror how a human would play, // and visibly ensures you won't get hit while not creating excessive movements. float distanceExtra = 2.0f; - bot->Yell("MOVING", LANG_UNIVERSAL); return Move(bot->GetAngle(boss), distance + distanceExtra); } From 5838cc60ca7a22b59cb0a6924fa70c125ab5a753 Mon Sep 17 00:00:00 2001 From: Bobblybook Date: Mon, 7 Oct 2024 10:09:36 +1100 Subject: [PATCH 27/40] Remove debug #2 --- src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubActions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubActions.cpp b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubActions.cpp index 4cf1c83e..2bfe2723 100644 --- a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubActions.cpp +++ b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubActions.cpp @@ -25,7 +25,7 @@ bool AttackWebWrapAction::Execute(Event event) { return false; } - bot->Yell("ATTACKING WRAP", LANG_UNIVERSAL); + return Attack(webWrap); } From e8c8401b4ab75c6980df8c989b6ad85391d22b42 Mon Sep 17 00:00:00 2001 From: bash Date: Mon, 7 Oct 2024 20:57:19 +0000 Subject: [PATCH 28/40] Ported cmangos of allowAcitity and prority, removed autoscaled based on latency --- conf/playerbots.conf.dist | 4 - src/PlayerbotAI.cpp | 263 +++++++++++++++++++++++++------------ src/PlayerbotAI.h | 28 +++- src/PlayerbotAIConfig.cpp | 1 - src/PlayerbotAIConfig.h | 1 - src/RandomPlayerbotMgr.cpp | 3 + 6 files changed, 209 insertions(+), 91 deletions(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 6da1c7a3..a977beb9 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -1462,10 +1462,6 @@ AiPlayerbot.RandombotsWalkingRPG.InDoors = 0 # The default is 10. With 10% of all bots going active or inactive each minute. AiPlayerbot.BotActiveAlone = 100 -# Specify 1 for enabled, 0 for disabled. -# The default is 1. Automatically adjusts 'BotActiveAlone' percentage based on server latency. -AiPlayerbot.botActiveAloneAutoScale = 1 - # Premade spell to avoid (undetected spells) # spellid-radius, ... AiPlayerbot.PremadeAvoidAoe = 62234-4 diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 68512241..b597f646 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -3926,7 +3926,10 @@ Player* PlayerbotAI::GetGroupMaster() uint32 PlayerbotAI::GetFixedBotNumer(BotTypeNumber typeNumber, uint32 maxNum, float cyclePerMin) { - uint32 randseed = rand32(); // Seed random number + //deterministic seed + uint8 seedNumber = uint8(typeNumber); + std::mt19937 rng(seedNumber); + uint32 randseed = rng(); // Seed random number uint32 randnum = bot->GetGUID().GetCounter() + randseed; // Semi-random but fixed number for each bot. if (cyclePerMin > 0) @@ -3936,8 +3939,7 @@ uint32 PlayerbotAI::GetFixedBotNumer(BotTypeNumber typeNumber, uint32 maxNum, fl randnum += cycle; // Make the random number cylce. } - randnum = - (randnum % (maxNum + 1)); // Loops the randomnumber at maxNum. Bassically removes all the numbers above 99. + randnum = (randnum % (maxNum + 1)); // Loops the randomnumber at maxNum. Bassically removes all the numbers above 99. return randnum; // Now we have a number unique for each bot between 0 and maxNum that increases by cyclePerMin. } @@ -4075,18 +4077,15 @@ inline bool HasRealPlayers(Map* map) return false; } -bool PlayerbotAI::AllowActive(ActivityType activityType) +ActivePiorityType PlayerbotAI::GetPriorityType(ActivityType activityType) { - // General exceptions - if (activityType == PACKET_ACTIVITY) - return true; + // First priority - priorities disabled or has player master. Always active. + if (HasRealPlayerMaster()) + return ActivePiorityType::HAS_REAL_PLAYER_MASTER; - if (GetMaster()) // Has player master. Always active. - { - PlayerbotAI* masterBotAI = GET_PLAYERBOT_AI(GetMaster()); - if (!masterBotAI || masterBotAI->IsRealPlayer()) - return true; - } + // Self bot in a group with a bot master. + if (IsRealPlayer()) + return ActivePiorityType::IS_REAL_PLAYER; Group* group = bot->GetGroup(); if (group) @@ -4102,19 +4101,40 @@ bool PlayerbotAI::AllowActive(ActivityType activityType) PlayerbotAI* memberBotAI = GET_PLAYERBOT_AI(member); if (!memberBotAI || memberBotAI->HasRealPlayerMaster()) - return true; + return ActivePiorityType::IN_GROUP_WITH_REAL_PLAYER; if (group->IsLeader(member->GetGUID())) + { if (!memberBotAI->AllowActivity(PARTY_ACTIVITY)) - return false; + return ActivePiorityType::IN_GROUP_WITH_REAL_PLAYER; + } } } - if (!WorldPosition(bot).isOverworld()) // bg, raid, dungeon - return true; + if (bot->IsBeingTeleported()) // Allow activity while teleportation. + return ActivePiorityType::IN_INSTANCE; - if (bot->InBattlegroundQueue()) // In bg queue. Speed up bg queue/join. - return true; + if (!WorldPosition(bot).isOverworld()) + return ActivePiorityType::IN_INSTANCE; + + if (HasPlayerNearby()) + return ActivePiorityType::VISIBLE_FOR_PLAYER; + + if (activityType != OUT_OF_PARTY_ACTIVITY && activityType != PACKET_ACTIVITY) + { + // Is in combat.Defend yourself. + if (bot->IsInCombat()) + return ActivePiorityType::IN_COMBAT; + } + + if (HasPlayerNearby(300.f)) + return ActivePiorityType::NEARBY_PLAYER; + + //if (sPlayerbotAIConfig->IsFreeAltBot(bot) || HasStrategy("travel once", BotState::BOT_STATE_NON_COMBAT)) + // return ActivePiorityType::IS_ALWAYS_ACTIVE; + + if (bot->InBattlegroundQueue()) + return ActivePiorityType::IN_BG_QUEUE; bool isLFG = false; if (group) @@ -4124,62 +4144,166 @@ bool PlayerbotAI::AllowActive(ActivityType activityType) isLFG = true; } } - if (sLFGMgr->GetState(bot->GetGUID()) != lfg::LFG_STATE_NONE) { isLFG = true; } - if (isLFG) - return true; + return ActivePiorityType::IN_LFG; - if (activityType != OUT_OF_PARTY_ACTIVITY && activityType != PACKET_ACTIVITY) // Is in combat. Defend yourself. - if (bot->IsInCombat()) - return true; - - if (HasPlayerNearby(300.f)) // Player is near. Always active. - return true; + // If has real players - slow down continents without player + // This means we first disable bots in a different continent/area. + if (sRandomPlayerbotMgr->GetPlayers().empty()) + return ActivePiorityType::IN_EMPTY_SERVER; // friends always active - - // HasFriend sometimes cause crash, disable - // for (auto& player : sRandomPlayerbotMgr->GetPlayers()) - // { - // if (!player || !player->IsInWorld()) - // continue; + // for (auto& player : sRandomPlayerbotMgr->GetPlayers()) + // { + // if (!player || !player->IsInWorld()) + // continue; // if (player->GetSocial()->HasFriend(bot->GetGUID())) - // return true; + // return ActivePiorityType::PLAYER_FRIEND; // } - if (activityType == OUT_OF_PARTY_ACTIVITY || - activityType == GRIND_ACTIVITY) // Many bots nearby. Do not do heavy area checks. - if (HasManyPlayersNearby()) - return false; + // real guild always active if member+ + if (IsInRealGuild()) + return ActivePiorityType::PLAYER_GUILD; - // Bots don't need to move using PathGenerator. - if (activityType == DETAILED_MOVE_ACTIVITY) - return false; + if (bot->IsBeingTeleported() || !bot->IsInWorld() || !HasRealPlayers(bot->GetMap())) + return ActivePiorityType::IN_INACTIVE_MAP; - // All exceptions are now done. - // Below is code to have a specified % of bots active at all times. - // The default is 10%. With 0.1% of all bots going active or inactive each minute. - if (sPlayerbotAIConfig->botActiveAlone <= 0) - return false; - - uint32 mod = sPlayerbotAIConfig->botActiveAlone > 100 ? 100 : sPlayerbotAIConfig->botActiveAlone; - if (sPlayerbotAIConfig->botActiveAloneAutoScale) + // IN_ACTIVE_AREA + if (activityType == OUT_OF_PARTY_ACTIVITY || activityType == GRIND_ACTIVITY) { - mod = AutoScaleActivity(mod); + // Many bots nearby. Do not do heavy area checks. + if (HasManyPlayersNearby()) + return ActivePiorityType::IN_ACTIVE_AREA; } - uint32 ActivityNumber = - GetFixedBotNumer(BotTypeNumber::ACTIVITY_TYPE_NUMBER, 100, - sPlayerbotAIConfig->botActiveAlone * static_cast(mod) / 100 * 0.01f); + return ActivePiorityType::IN_ACTIVE_AREA; +} - return ActivityNumber <= - (sPlayerbotAIConfig->botActiveAlone * mod) / - 100; // The given percentage of bots should be active and rotate 1% of those active bots each minute. +// Returns the lower and upper bracket for bots to be active. +// Ie. 10,20 means all bots in this bracket will be inactive below 10% activityMod, all bots in this bracket will be +// active above 20% activityMod and scale between those values. +std::pair PlayerbotAI::GetPriorityBracket(ActivePiorityType type) +{ + switch (type) + { + case ActivePiorityType::HAS_REAL_PLAYER_MASTER: + case ActivePiorityType::IS_REAL_PLAYER: + case ActivePiorityType::IN_GROUP_WITH_REAL_PLAYER: + case ActivePiorityType::IN_INSTANCE: + case ActivePiorityType::VISIBLE_FOR_PLAYER: + return {0, 0}; + case ActivePiorityType::IS_ALWAYS_ACTIVE: + case ActivePiorityType::IN_COMBAT: + return {0, 10}; + case ActivePiorityType::IN_BG_QUEUE: + return {0, 20}; + case ActivePiorityType::IN_LFG: + return {0, 30}; + case ActivePiorityType::NEARBY_PLAYER: + return {0, 40}; + case ActivePiorityType::PLAYER_FRIEND: + case ActivePiorityType::PLAYER_GUILD: + return {0, 50}; + case ActivePiorityType::IN_ACTIVE_AREA: + case ActivePiorityType::IN_EMPTY_SERVER: + return {50, 100}; + case ActivePiorityType::IN_ACTIVE_MAP: + return {70, 100}; + case ActivePiorityType::IN_INACTIVE_MAP: + return {80, 100}; + default: + return {90, 100}; + } + + return {90, 100}; +} + +bool PlayerbotAI::AllowActive(ActivityType activityType) +{ + //General exceptions + if (activityType == PACKET_ACTIVITY) + return true; + + ActivePiorityType type = GetPriorityType(activityType); + if (activityType == DETAILED_MOVE_ACTIVITY) + { + switch (type) + { + case ActivePiorityType::HAS_REAL_PLAYER_MASTER: + case ActivePiorityType::IS_REAL_PLAYER: + case ActivePiorityType::IN_GROUP_WITH_REAL_PLAYER: + case ActivePiorityType::IN_INSTANCE: + case ActivePiorityType::VISIBLE_FOR_PLAYER: + case ActivePiorityType::IN_COMBAT: + case ActivePiorityType::NEARBY_PLAYER: + return true; + break; + case ActivePiorityType::IS_ALWAYS_ACTIVE: + case ActivePiorityType::IN_BG_QUEUE: + case ActivePiorityType::IN_LFG: + case ActivePiorityType::PLAYER_FRIEND: + case ActivePiorityType::PLAYER_GUILD: + case ActivePiorityType::IN_ACTIVE_AREA: + case ActivePiorityType::IN_EMPTY_SERVER: + case ActivePiorityType::IN_ACTIVE_MAP: + case ActivePiorityType::IN_INACTIVE_MAP: + default: + break; + } + } + else if (activityType == REACT_ACTIVITY) + { + switch (type) + { + case ActivePiorityType::HAS_REAL_PLAYER_MASTER: + case ActivePiorityType::IS_REAL_PLAYER: + case ActivePiorityType::IN_GROUP_WITH_REAL_PLAYER: + case ActivePiorityType::IN_INSTANCE: + case ActivePiorityType::VISIBLE_FOR_PLAYER: + case ActivePiorityType::IS_ALWAYS_ACTIVE: + case ActivePiorityType::IN_COMBAT: + return true; + break; + case ActivePiorityType::NEARBY_PLAYER: + case ActivePiorityType::IN_BG_QUEUE: + case ActivePiorityType::IN_LFG: + case ActivePiorityType::PLAYER_FRIEND: + case ActivePiorityType::PLAYER_GUILD: + case ActivePiorityType::IN_ACTIVE_AREA: + case ActivePiorityType::IN_EMPTY_SERVER: + case ActivePiorityType::IN_ACTIVE_MAP: + case ActivePiorityType::IN_INACTIVE_MAP: + default: + return false; + break; + } + } + + // GetPriorityBracket acitivity + std::pair priorityBracket = GetPriorityBracket(type); + if (!priorityBracket.second) + return true; // No scaling + + // Activity between 0 and 100. + float activityPercentage = sRandomPlayerbotMgr->getActivityPercentage(); + if (priorityBracket.first >= activityPercentage) + return false; + if (priorityBracket.second <= activityPercentage && priorityBracket.second < 100) + return true; + + float activePerc = (activityPercentage - priorityBracket.first) / (priorityBracket.second - priorityBracket.first); + activePerc *= (priorityBracket.second == 100) ? sPlayerbotAIConfig->botActiveAlone : 100; + + // The last number if the amount it cycles per min. Currently set to 1% of the active bots. + uint32 ActivityNumber = GetFixedBotNumer(BotTypeNumber::ACTIVITY_TYPE_NUMBER, 100, activePerc * 0.01f); + + // The given percentage of bots should be active and rotate 1% of those active bots each minute. + return ActivityNumber <= (activePerc); } bool PlayerbotAI::AllowActivity(ActivityType activityType, bool checkNow) @@ -4196,31 +4320,6 @@ bool PlayerbotAI::AllowActivity(ActivityType activityType, bool checkNow) return allowed; } -uint32 PlayerbotAI::AutoScaleActivity(uint32 mod) -{ - uint32 maxDiff = sWorldUpdateTime.GetAverageUpdateTime(); - - if (maxDiff > 500) return 0; - if (maxDiff > 250) - { - if (Map* map = bot->GetMap()) - { - if (map->GetEntry()->IsWorldMap() && - (!HasRealPlayers(map) || - !map->IsGridLoaded(bot->GetPositionX(), bot->GetPositionY()))) - return 0; - } - - return (mod * 1) / 10; - } - if (maxDiff > 200) return (mod * 3) / 10; - if (maxDiff > 150) return (mod * 5) / 10; - if (maxDiff > 100) return (mod * 6) / 10; - if (maxDiff > 80) return (mod * 9) / 10; - - return mod; -} - bool PlayerbotAI::IsOpposing(Player* player) { return IsOpposing(player->getRace(), bot->getRace()); } bool PlayerbotAI::IsOpposing(uint8 race1, uint8 race2) diff --git a/src/PlayerbotAI.h b/src/PlayerbotAI.h index 6e82338b..3b83a5eb 100644 --- a/src/PlayerbotAI.h +++ b/src/PlayerbotAI.h @@ -241,6 +241,27 @@ enum class GuilderType : uint8 VERY_LARGE = 250 }; +enum class ActivePiorityType : uint8 +{ + IS_REAL_PLAYER = 0, + HAS_REAL_PLAYER_MASTER = 1, + IN_GROUP_WITH_REAL_PLAYER = 2, + IN_INSTANCE = 3, + VISIBLE_FOR_PLAYER = 4, + IS_ALWAYS_ACTIVE = 5, + IN_COMBAT = 6, + IN_BG_QUEUE = 7, + IN_LFG = 8, + NEARBY_PLAYER = 9, + PLAYER_FRIEND = 10, + PLAYER_GUILD = 11, + IN_ACTIVE_AREA = 12, + IN_ACTIVE_MAP = 13, + IN_INACTIVE_MAP = 14, + IN_EMPTY_SERVER = 15, + MAX_TYPE +}; + enum ActivityType { GRIND_ACTIVITY = 1, @@ -250,8 +271,8 @@ enum ActivityType PACKET_ACTIVITY = 5, DETAILED_MOVE_ACTIVITY = 6, PARTY_ACTIVITY = 7, - ALL_ACTIVITY = 8, - + REACT_ACTIVITY = 8, + ALL_ACTIVITY = 9, MAX_ACTIVITY_TYPE }; @@ -525,9 +546,10 @@ public: bool HasPlayerNearby(WorldPosition* pos, float range = sPlayerbotAIConfig->reactDistance); bool HasPlayerNearby(float range = sPlayerbotAIConfig->reactDistance); bool HasManyPlayersNearby(uint32 trigerrValue = 20, float range = sPlayerbotAIConfig->sightDistance); + ActivePiorityType GetPriorityType(ActivityType activityType); + std::pair GetPriorityBracket(ActivePiorityType type); bool AllowActive(ActivityType activityType); bool AllowActivity(ActivityType activityType = ALL_ACTIVITY, bool checkNow = false); - uint32 AutoScaleActivity(uint32 mod); // Check if player is safe to use. bool IsSafe(Player* player); diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index 10c680ae..1d60091b 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -465,7 +465,6 @@ bool PlayerbotAIConfig::Initialize() playerbotsXPrate = sConfigMgr->GetOption("AiPlayerbot.KillXPRate", 1); disableDeathKnightLogin = sConfigMgr->GetOption("AiPlayerbot.DisableDeathKnightLogin", 0); botActiveAlone = sConfigMgr->GetOption("AiPlayerbot.BotActiveAlone", 100); - botActiveAloneAutoScale = sConfigMgr->GetOption("AiPlayerbot.botActiveAloneAutoScale", true); enablePrototypePerformanceDiff = sConfigMgr->GetOption("AiPlayerbot.EnablePrototypePerformanceDiff", false); diffWithPlayer = sConfigMgr->GetOption("AiPlayerbot.DiffWithPlayer", 100); diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index f8e3fd82..6caf1179 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -263,7 +263,6 @@ public: uint32 playerbotsXPrate; bool disableDeathKnightLogin; uint32 botActiveAlone; - bool botActiveAloneAutoScale; uint32 enablePrototypePerformanceDiff; uint32 diffWithPlayer; diff --git a/src/RandomPlayerbotMgr.cpp b/src/RandomPlayerbotMgr.cpp index 7d26a096..5d3b5edf 100644 --- a/src/RandomPlayerbotMgr.cpp +++ b/src/RandomPlayerbotMgr.cpp @@ -1108,6 +1108,9 @@ bool RandomPlayerbotMgr::ProcessBot(uint32 bot) bool RandomPlayerbotMgr::ProcessBot(Player* player) { + if (!player || !player->IsInWorld() || player->IsBeingTeleported() || player->GetSession()->isLogingOut()) + return false; + uint32 bot = player->GetGUID().GetCounter(); if (player->InBattleground()) From 16ea1777f4003fc1d21170b7e8e0b5c76bb19555 Mon Sep 17 00:00:00 2001 From: Bobblybook Date: Tue, 8 Oct 2024 22:35:16 +1100 Subject: [PATCH 29/40] Old Kingdom implementation Ahn'kahet: The Old Kingdom bot AI --- src/strategy/AiObjectContext.cpp | 2 + .../dungeons/DungeonStrategyContext.h | 9 +-- .../wotlk/WotlkDungeonActionContext.h | 2 +- .../wotlk/WotlkDungeonTriggerContext.h | 2 +- .../oldkingdom/OldKingdomActionContext.h | 22 +++++ .../wotlk/oldkingdom/OldKingdomActions.cpp | 81 +++++++++++++++++++ .../wotlk/oldkingdom/OldKingdomActions.h | 31 +++++++ .../oldkingdom/OldKingdomMultipliers.cpp | 66 +++++++++++++++ .../wotlk/oldkingdom/OldKingdomMultipliers.h | 33 ++++++++ .../wotlk/oldkingdom/OldKingdomStrategy.cpp | 36 +++++++++ .../wotlk/oldkingdom/OldKingdomStrategy.h | 18 +++++ .../oldkingdom/OldKingdomTriggerContext.h | 23 ++++++ .../wotlk/oldkingdom/OldKingdomTriggers.cpp | 43 ++++++++++ .../wotlk/oldkingdom/OldKingdomTriggers.h | 45 +++++++++++ src/strategy/dungeons/wotlk/oldkingdom/TODO | 0 15 files changed, 405 insertions(+), 8 deletions(-) create mode 100644 src/strategy/dungeons/wotlk/oldkingdom/OldKingdomActionContext.h create mode 100644 src/strategy/dungeons/wotlk/oldkingdom/OldKingdomActions.cpp create mode 100644 src/strategy/dungeons/wotlk/oldkingdom/OldKingdomActions.h create mode 100644 src/strategy/dungeons/wotlk/oldkingdom/OldKingdomMultipliers.cpp create mode 100644 src/strategy/dungeons/wotlk/oldkingdom/OldKingdomMultipliers.h create mode 100644 src/strategy/dungeons/wotlk/oldkingdom/OldKingdomStrategy.cpp create mode 100644 src/strategy/dungeons/wotlk/oldkingdom/OldKingdomStrategy.h create mode 100644 src/strategy/dungeons/wotlk/oldkingdom/OldKingdomTriggerContext.h create mode 100644 src/strategy/dungeons/wotlk/oldkingdom/OldKingdomTriggers.cpp create mode 100644 src/strategy/dungeons/wotlk/oldkingdom/OldKingdomTriggers.h delete mode 100644 src/strategy/dungeons/wotlk/oldkingdom/TODO diff --git a/src/strategy/AiObjectContext.cpp b/src/strategy/AiObjectContext.cpp index 819ea98a..cf6b43ea 100644 --- a/src/strategy/AiObjectContext.cpp +++ b/src/strategy/AiObjectContext.cpp @@ -53,6 +53,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI) actionContexts.Add(new WotlkDungeonUKActionContext()); actionContexts.Add(new WotlkDungeonNexActionContext()); actionContexts.Add(new WotlkDungeonANActionContext()); + actionContexts.Add(new WotlkDungeonOKActionContext()); triggerContexts.Add(new TriggerContext()); triggerContexts.Add(new ChatTriggerContext()); @@ -66,6 +67,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI) triggerContexts.Add(new WotlkDungeonUKTriggerContext()); triggerContexts.Add(new WotlkDungeonNexTriggerContext()); triggerContexts.Add(new WotlkDungeonANTriggerContext()); + triggerContexts.Add(new WotlkDungeonOKTriggerContext()); valueContexts.Add(new ValueContext()); diff --git a/src/strategy/dungeons/DungeonStrategyContext.h b/src/strategy/dungeons/DungeonStrategyContext.h index 8764d351..141953af 100644 --- a/src/strategy/dungeons/DungeonStrategyContext.h +++ b/src/strategy/dungeons/DungeonStrategyContext.h @@ -5,13 +5,10 @@ #include "wotlk/utgardekeep/UtgardeKeepStrategy.h" #include "wotlk/nexus/NexusStrategy.h" #include "wotlk/azjolnerub/AzjolNerubStrategy.h" +#include "wotlk/oldkingdom/OldKingdomStrategy.h" /* Full list/TODO: - - -Ahn'kahet: The Old Kingdom - OK -Elder Nadox, Prince Taldaram, Jedoga Shadowseeker, Herald Volazj, Amanitar (Heroic Only) Drak'Tharon Keep - DTK Trollgore, Novos the Summoner, King Dred, The Prophet Tharon'ja The Violet Hold - VH @@ -77,8 +74,8 @@ class DungeonStrategyContext : public NamedObjectContext static Strategy* wotlk_uk(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } static Strategy* wotlk_nex(PlayerbotAI* botAI) { return new WotlkDungeonNexStrategy(botAI); } static Strategy* wotlk_an(PlayerbotAI* botAI) { return new WotlkDungeonANStrategy(botAI); } - - static Strategy* wotlk_ok(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } + static Strategy* wotlk_ok(PlayerbotAI* botAI) { return new WotlkDungeonOKStrategy(botAI); } + static Strategy* wotlk_dtk(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } static Strategy* wotlk_vh(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } static Strategy* wotlk_gd(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } diff --git a/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h b/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h index 40529513..1a2af3ce 100644 --- a/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h +++ b/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h @@ -4,7 +4,7 @@ #include "utgardekeep/UtgardeKeepActionContext.h" #include "nexus/NexusActionContext.h" #include "azjolnerub/AzjolNerubActionContext.h" -// #include "oldkingdom/OldKingdomActionContext.h" +#include "oldkingdom/OldKingdomActionContext.h" // #include "draktharonkeep/DraktharonKeepActionContext.h" // #include "violethold/VioletHoldActionContext.h" // #include "gundrak/GundrakActionContext.h" diff --git a/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h b/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h index b53d0fa5..c43672cd 100644 --- a/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h +++ b/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h @@ -4,7 +4,7 @@ #include "utgardekeep/UtgardeKeepTriggerContext.h" #include "nexus/NexusTriggerContext.h" #include "azjolnerub/AzjolNerubTriggerContext.h" -// #include "oldkingdom/OldKingdomTriggerContext.h" +#include "oldkingdom/OldKingdomTriggerContext.h" // #include "draktharonkeep/DraktharonKeepTriggerContext.h" // #include "violethold/VioletHoldTriggerContext.h" // #include "gundrak/GundrakTriggerContext.h" diff --git a/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomActionContext.h b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomActionContext.h new file mode 100644 index 00000000..19426c57 --- /dev/null +++ b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomActionContext.h @@ -0,0 +1,22 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONOKACTIONCONTEXT_H +#define _PLAYERBOT_WOTLKDUNGEONOKACTIONCONTEXT_H + +#include "Action.h" +#include "NamedObjectContext.h" +#include "OldKingdomActions.h" + +class WotlkDungeonOKActionContext : public NamedObjectContext +{ + public: + WotlkDungeonOKActionContext() { + creators["attack nadox guardian"] = &WotlkDungeonOKActionContext::attack_nadox_guardian; + creators["attack jedoga volunteer"] = &WotlkDungeonOKActionContext::attack_jedoga_volunteer; + creators["avoid shadow crash"] = &WotlkDungeonOKActionContext::avoid_shadow_crash; + } + private: + static Action* attack_nadox_guardian(PlayerbotAI* ai) { return new AttackNadoxGuardianAction(ai); } + static Action* attack_jedoga_volunteer(PlayerbotAI* ai) { return new AttackJedogaVolunteerAction(ai); } + static Action* avoid_shadow_crash(PlayerbotAI* ai) { return new AvoidShadowCrashAction(ai); } +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomActions.cpp b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomActions.cpp new file mode 100644 index 00000000..c5b27cca --- /dev/null +++ b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomActions.cpp @@ -0,0 +1,81 @@ +#include "Playerbots.h" +#include "OldKingdomActions.h" +#include "OldKingdomStrategy.h" + + +bool AttackNadoxGuardianAction::Execute(Event event) +{ + Unit* target = AI_VALUE2(Unit*, "find target", "ahn'kahar guardian"); + if (!target || AI_VALUE(Unit*, "current target") == target) + { + return false; + } + + return Attack(target); +} + +bool AttackJedogaVolunteerAction::Execute(Event event) +{ + Unit* target = nullptr; + // Target is not findable from threat table using AI_VALUE2(), + // therefore need to search manually for the unit name + GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); + + for (auto i = targets.begin(); i != targets.end(); ++i) + { + Unit* unit = botAI->GetUnit(*i); + if (unit && unit->GetEntry() == NPC_TWILIGHT_VOLUNTEER) + { + target = unit; + break; + } + } + + if (!target || AI_VALUE(Unit*, "current target") == target) + { + return false; + } + return Attack(target); +} + +bool AvoidShadowCrashAction::Execute(Event event) +{ + // Could check all enemy units in range as it's possible to pull multiple of these mobs. + // They should really be killed 1 by 1, multipulls are messy so we just handle singles for now + Unit* npc = AI_VALUE2(Unit*, "find target", "forgotten one"); + Unit* victim = nullptr; + float radius = 10.0f; + float targetDist = radius + 2.0f; + if (!npc) { return false; } + + // Actively move if targeted by a shadow crash. + // Spell check not needed, they don't have any other non-instant casts + if (npc->HasUnitState(UNIT_STATE_CASTING)) // && npc->FindCurrentSpellBySpellId(SPELL_SHADOW_CRASH)) + { + // This doesn't seem to avoid casts very well, perhaps because this isn't checked while allies are casting. + // TODO: Revisit if this is an issue in heroics, otherwise ignore shadow crashes for the most part. + victim = botAI->GetUnit(npc->GetTarget()); + if (victim && bot->GetExactDist2d(victim) < radius) + { + return MoveAway(victim, targetDist - bot->GetExactDist2d(victim)); + } + } + + // Otherwise ranged members passively spread, to avoid AoE overlap + if (botAI->IsMelee(bot)) { return false; } + + GuidVector members = AI_VALUE(GuidVector, "group members"); + for (auto& member : members) + { + if (bot->GetGUID() == member) + { + continue; + } + float currentDist = bot->GetExactDist2d(botAI->GetUnit(member)); + if (currentDist < radius) + { + return MoveAway(botAI->GetUnit(member), targetDist - currentDist); + } + } + return false; +} diff --git a/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomActions.h b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomActions.h new file mode 100644 index 00000000..cba51878 --- /dev/null +++ b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomActions.h @@ -0,0 +1,31 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONOKACTIONS_H +#define _PLAYERBOT_WOTLKDUNGEONOKACTIONS_H + +#include "Action.h" +#include "AttackAction.h" +#include "PlayerbotAI.h" +#include "Playerbots.h" +#include "OldKingdomTriggers.h" + +class AttackNadoxGuardianAction : public AttackAction +{ +public: + AttackNadoxGuardianAction(PlayerbotAI* ai) : AttackAction(ai, "attack nadox guardian") {} + bool Execute(Event event) override; +}; + +class AttackJedogaVolunteerAction : public AttackAction +{ +public: + AttackJedogaVolunteerAction(PlayerbotAI* ai) : AttackAction(ai, "attack jedoga volunteer") {} + bool Execute(Event event) override; +}; + +class AvoidShadowCrashAction : public MovementAction +{ +public: + AvoidShadowCrashAction(PlayerbotAI* ai) : MovementAction(ai, "avoid shadow crash") {} + bool Execute(Event event) override; +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomMultipliers.cpp b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomMultipliers.cpp new file mode 100644 index 00000000..9fc90802 --- /dev/null +++ b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomMultipliers.cpp @@ -0,0 +1,66 @@ +#include "OldKingdomMultipliers.h" +#include "OldKingdomActions.h" +#include "GenericSpellActions.h" +#include "ChooseTargetActions.h" +#include "MovementActions.h" +#include "OldKingdomTriggers.h" +#include "Action.h" + +float ElderNadoxMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "elder nadox"); + Unit* guardian = AI_VALUE2(Unit*, "find target", "ahn'kahar guardian"); + + if (boss && guardian) + { + if (dynamic_cast(action)) + { + return 0.0f; + } + } + return 1.0f; +} + +float JedogaShadowseekerMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "jedoga shadowseeker"); + // Unit* volunteer = AI_VALUE2(Unit*, "find target", "twilight volunteer"); + + Unit* volunteer = nullptr; + // Target is not findable from threat table using AI_VALUE2(), + // therefore need to search manually for the unit name + GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); + + for (auto i = targets.begin(); i != targets.end(); ++i) + { + Unit* unit = botAI->GetUnit(*i); + if (unit && unit->GetEntry() == NPC_TWILIGHT_VOLUNTEER) + { + volunteer = unit; + break; + } + } + + if (boss && volunteer) + { + if (dynamic_cast(action)) + { + return 0.0f; + } + } + return 1.0f; +} + +float ForgottenOneMultiplier::GetValue(Action* action) +{ + Unit* npc = AI_VALUE2(Unit*, "find target", "forgotten one"); + + if (npc && bot->isMoving()) + { + if (dynamic_cast(action)) + { + return 0.0f; + } + } + return 1.0f; +} diff --git a/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomMultipliers.h b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomMultipliers.h new file mode 100644 index 00000000..88ae2fa5 --- /dev/null +++ b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomMultipliers.h @@ -0,0 +1,33 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONOKMULTIPLIERS_H +#define _PLAYERBOT_WOTLKDUNGEONOKMULTIPLIERS_H + +#include "Multiplier.h" + +class ElderNadoxMultiplier : public Multiplier +{ + public: + ElderNadoxMultiplier(PlayerbotAI* ai) : Multiplier(ai, "elder nadox") {} + + public: + virtual float GetValue(Action* action); +}; + +class JedogaShadowseekerMultiplier : public Multiplier +{ + public: + JedogaShadowseekerMultiplier(PlayerbotAI* ai) : Multiplier(ai, "jedoga shadowseeker") {} + + public: + virtual float GetValue(Action* action); +}; + +class ForgottenOneMultiplier : public Multiplier +{ + public: + ForgottenOneMultiplier(PlayerbotAI* ai) : Multiplier(ai, "forgotten one") {} + + public: + virtual float GetValue(Action* action); +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomStrategy.cpp b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomStrategy.cpp new file mode 100644 index 00000000..b353a05d --- /dev/null +++ b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomStrategy.cpp @@ -0,0 +1,36 @@ +#include "OldKingdomStrategy.h" +#include "OldKingdomMultipliers.h" + + +void WotlkDungeonOKStrategy::InitTriggers(std::vector &triggers) +{ + // Elder Nadox + triggers.push_back(new TriggerNode("nadox guardian", + NextAction::array(0, new NextAction("attack nadox guardian", ACTION_RAID + 5), nullptr))); + + // Prince Taldaram + // Flame Orb spawns in melee, doesn't have a clear direction until it starts moving. + // Maybe not worth trying to avoid and just heal through. Only consideration is not to have ranged + // players anywhere near melee when it spawns + + // Jedoga Shadowseeker + triggers.push_back(new TriggerNode("jedoga volunteer", + NextAction::array(0, new NextAction("attack jedoga volunteer", ACTION_RAID + 5), nullptr))); + + // Herald Volazj + // Trash mobs before him have a big telegraphed shadow crash spell, + // this can be avoided and is intended to be dodged + triggers.push_back(new TriggerNode("shadow crash", + NextAction::array(0, new NextAction("avoid shadow crash", ACTION_MOVE + 5), nullptr))); + // Volazj is not implemented properly in AC, insanity phase does nothing. + + // Amanitar (Heroic Only) + // TODO: once I get to heroics +} + +void WotlkDungeonOKStrategy::InitMultipliers(std::vector &multipliers) +{ + multipliers.push_back(new ElderNadoxMultiplier(botAI)); + multipliers.push_back(new JedogaShadowseekerMultiplier(botAI)); + multipliers.push_back(new ForgottenOneMultiplier(botAI)); +} diff --git a/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomStrategy.h b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomStrategy.h new file mode 100644 index 00000000..03c5085e --- /dev/null +++ b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomStrategy.h @@ -0,0 +1,18 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONOKSTRATEGY_H +#define _PLAYERBOT_WOTLKDUNGEONOKSTRATEGY_H + +#include "Multiplier.h" +#include "AiObjectContext.h" +#include "Strategy.h" + + +class WotlkDungeonOKStrategy : public Strategy +{ +public: + WotlkDungeonOKStrategy(PlayerbotAI* ai) : Strategy(ai) {} + virtual std::string const getName() override { return "old kingdom"; } + virtual void InitTriggers(std::vector &triggers) override; + virtual void InitMultipliers(std::vector &multipliers) override; +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomTriggerContext.h b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomTriggerContext.h new file mode 100644 index 00000000..faca4976 --- /dev/null +++ b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomTriggerContext.h @@ -0,0 +1,23 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONOKTRIGGERCONTEXT_H +#define _PLAYERBOT_WOTLKDUNGEONOKTRIGGERCONTEXT_H + +#include "NamedObjectContext.h" +#include "AiObjectContext.h" +#include "OldKingdomTriggers.h" + +class WotlkDungeonOKTriggerContext : public NamedObjectContext +{ + public: + WotlkDungeonOKTriggerContext() + { + creators["nadox guardian"] = &WotlkDungeonOKTriggerContext::nadox_guardian; + creators["jedoga volunteer"] = &WotlkDungeonOKTriggerContext::jedoga_volunteer; + creators["shadow crash"] = &WotlkDungeonOKTriggerContext::shadow_crash; + } + private: + static Trigger* nadox_guardian(PlayerbotAI* ai) { return new NadoxGuardianTrigger(ai); } + static Trigger* jedoga_volunteer(PlayerbotAI* ai) { return new JedogaVolunteerTrigger(ai); } + static Trigger* shadow_crash(PlayerbotAI* ai) { return new ShadowCrashTrigger(ai); } +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomTriggers.cpp b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomTriggers.cpp new file mode 100644 index 00000000..951ab755 --- /dev/null +++ b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomTriggers.cpp @@ -0,0 +1,43 @@ +#include "Playerbots.h" +#include "OldKingdomTriggers.h" +#include "AiObject.h" +#include "AiObjectContext.h" + + +bool NadoxGuardianTrigger::IsActive() +{ + if (botAI->IsHeal(bot)) { return false; } + + Unit* boss = AI_VALUE2(Unit*, "find target", "elder nadox"); + Unit* guardian = AI_VALUE2(Unit*, "find target", "ahn'kahar guardian"); + + return boss && guardian; +} + +bool JedogaVolunteerTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "jedoga shadowseeker"); + // Unit* volunteer = AI_VALUE2(Unit*, "find target", "twilight volunteer"); + Unit* volunteer = nullptr; + // Target is not findable from threat table using AI_VALUE2(), + // therefore need to search manually for the unit name + GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); + + for (auto i = targets.begin(); i != targets.end(); ++i) + { + Unit* unit = botAI->GetUnit(*i); + if (unit && unit->GetEntry() == NPC_TWILIGHT_VOLUNTEER) + { + volunteer = unit; + break; + } + } + + return boss && volunteer; +} + +bool ShadowCrashTrigger::IsActive() +{ + if (botAI->IsMelee(bot)) { return false; } + return !botAI->IsMelee(bot) && AI_VALUE2(Unit*, "find target", "forgotten one"); +} diff --git a/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomTriggers.h b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomTriggers.h new file mode 100644 index 00000000..2aa77986 --- /dev/null +++ b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomTriggers.h @@ -0,0 +1,45 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONOKTRIGGERS_H +#define _PLAYERBOT_WOTLKDUNGEONOKTRIGGERS_H + +#include "Trigger.h" +#include "PlayerbotAIConfig.h" +#include "GenericTriggers.h" +#include "DungeonStrategyUtils.h" + +enum OldKingdomIDs +{ + // Elder Nadox + BUFF_GUARDIAN_AURA = 56153, + + // Jedoga Shadowseeker + NPC_TWILIGHT_VOLUNTEER = 30385, + + // Forgotten One(s) + SPELL_SHADOW_CRASH_N = 60833, + SPELL_SHADOW_CRASH_H = 60848, +}; + +#define SPELL_SHADOW_CRASH DUNGEON_MODE(bot, SPELL_SHADOW_CRASH_N, SPELL_SHADOW_CRASH_H) + +class NadoxGuardianTrigger : public Trigger +{ +public: + NadoxGuardianTrigger(PlayerbotAI* ai) : Trigger(ai, "elder nadox guardian") {} + bool IsActive() override; +}; + +class JedogaVolunteerTrigger : public Trigger +{ +public: + JedogaVolunteerTrigger(PlayerbotAI* ai) : Trigger(ai, "jedoga volunteer") {} + bool IsActive() override; +}; + +class ShadowCrashTrigger : public Trigger +{ +public: + ShadowCrashTrigger(PlayerbotAI* ai) : Trigger(ai, "shadow crash") {} + bool IsActive() override; +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/oldkingdom/TODO b/src/strategy/dungeons/wotlk/oldkingdom/TODO deleted file mode 100644 index e69de29b..00000000 From c7197f0911551d5c097c070bea3ecd2c05a47ed7 Mon Sep 17 00:00:00 2001 From: bash Date: Wed, 9 Oct 2024 22:34:37 +0000 Subject: [PATCH 30/40] [performance] BotActiveAlone SmartScale toggle --- conf/playerbots.conf.dist | 4 +++ src/PlayerbotAI.cpp | 54 ++++++++++++++++++++++++--------------- src/PlayerbotAI.h | 1 + src/PlayerbotAIConfig.cpp | 1 + src/PlayerbotAIConfig.h | 1 + 5 files changed, 41 insertions(+), 20 deletions(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index a977beb9..e0356cb7 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -1462,6 +1462,10 @@ AiPlayerbot.RandombotsWalkingRPG.InDoors = 0 # The default is 10. With 10% of all bots going active or inactive each minute. AiPlayerbot.BotActiveAlone = 100 +# Specify smart scaling is enabled or not. +# The default is 1. When enabled (smart) scales the 'BotActiveAlone' value. +AiPlayerbot.botActiveAloneSmartScale = 1 + # Premade spell to avoid (undetected spells) # spellid-radius, ... AiPlayerbot.PremadeAvoidAoe = 62234-4 diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index b597f646..0df602d8 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -239,6 +239,7 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal) { return; } + // if (!GetMaster() || !GetMaster()->IsInWorld() || !GetMaster()->GetSession() || // GetMaster()->GetSession()->isLogingOut()) { // return; @@ -301,6 +302,7 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal) // bot->GetMotionMaster()->Clear(); // bot->GetMotionMaster()->MoveIdle(); // } + // cheat options if (bot->IsAlive() && ((uint32)GetCheat() > 0 || (uint32)sPlayerbotAIConfig->botCheatMask > 0)) { @@ -4188,7 +4190,7 @@ ActivePiorityType PlayerbotAI::GetPriorityType(ActivityType activityType) // Ie. 10,20 means all bots in this bracket will be inactive below 10% activityMod, all bots in this bracket will be // active above 20% activityMod and scale between those values. std::pair PlayerbotAI::GetPriorityBracket(ActivePiorityType type) -{ +{ switch (type) { case ActivePiorityType::HAS_REAL_PLAYER_MASTER: @@ -4219,13 +4221,13 @@ std::pair PlayerbotAI::GetPriorityBracket(ActivePiorityType type default: return {90, 100}; } - + return {90, 100}; } bool PlayerbotAI::AllowActive(ActivityType activityType) { - //General exceptions + // General exceptions if (activityType == PACKET_ACTIVITY) return true; @@ -4285,19 +4287,17 @@ bool PlayerbotAI::AllowActive(ActivityType activityType) } // GetPriorityBracket acitivity - std::pair priorityBracket = GetPriorityBracket(type); - if (!priorityBracket.second) - return true; // No scaling - - // Activity between 0 and 100. - float activityPercentage = sRandomPlayerbotMgr->getActivityPercentage(); - if (priorityBracket.first >= activityPercentage) - return false; - if (priorityBracket.second <= activityPercentage && priorityBracket.second < 100) - return true; - - float activePerc = (activityPercentage - priorityBracket.first) / (priorityBracket.second - priorityBracket.first); - activePerc *= (priorityBracket.second == 100) ? sPlayerbotAIConfig->botActiveAlone : 100; + float activePerc = 100; + if (sPlayerbotAIConfig->botActiveAloneSmartScale) + { + std::pair priorityBracket = GetPriorityBracket(type); + if (!priorityBracket.second) return true; + float activityPercentage = sRandomPlayerbotMgr->getActivityPercentage(); + if (priorityBracket.first >= activityPercentage) return false; + if (priorityBracket.second <= activityPercentage && priorityBracket.second < 100) return true; + activePerc = (activityPercentage - priorityBracket.first) / (priorityBracket.second - priorityBracket.first); + activePerc *= (priorityBracket.second == 100) ? sPlayerbotAIConfig->botActiveAlone : 100; + } // The last number if the amount it cycles per min. Currently set to 1% of the active bots. uint32 ActivityNumber = GetFixedBotNumer(BotTypeNumber::ACTIVITY_TYPE_NUMBER, 100, activePerc * 0.01f); @@ -5427,15 +5427,29 @@ bool PlayerbotAI::CanMove() if (IsInVehicle() && !IsInVehicle(true)) return false; - if (bot->isFrozen() || bot->IsPolymorphed() || (bot->isDead() && !bot->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)) || - bot->IsBeingTeleported() || bot->isInRoots() || bot->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION) || - bot->HasAuraType(SPELL_AURA_MOD_CONFUSE) || bot->IsCharmed() || bot->HasAuraType(SPELL_AURA_MOD_STUN) || - bot->HasUnitState(UNIT_STATE_IN_FLIGHT) || bot->HasUnitState(UNIT_STATE_LOST_CONTROL)) + if (bot->isFrozen() || + bot->IsPolymorphed() || + (bot->isDead() && !bot->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)) || + bot->IsBeingTeleported() || + bot->isInRoots() || + bot->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION) || + bot->HasAuraType(SPELL_AURA_MOD_CONFUSE) || + bot->IsCharmed() || + bot->HasAuraType(SPELL_AURA_MOD_STUN) || + bot->HasUnitState(UNIT_STATE_IN_FLIGHT) || + bot->HasUnitState(UNIT_STATE_LOST_CONTROL)) + return false; return bot->GetMotionMaster()->GetCurrentMovementGeneratorType() != FLIGHT_MOTION_TYPE; } +bool PlayerbotAI::IsTaxiFlying() +{ + return bot->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && + bot->HasUnitState(UNIT_STATE_IGNORE_PATHFINDING); +} + bool PlayerbotAI::IsInRealGuild() { if (!bot->GetGuildId()) diff --git a/src/PlayerbotAI.h b/src/PlayerbotAI.h index 3b83a5eb..d1a2f292 100644 --- a/src/PlayerbotAI.h +++ b/src/PlayerbotAI.h @@ -576,6 +576,7 @@ public: void ResetJumpDestination() { jumpDestination = Position(); } bool CanMove(); + bool IsTaxiFlying(); bool IsInRealGuild(); static std::vector dispel_whitelist; bool EqualLowercaseName(std::string s1, std::string s2); diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index 1d60091b..d6c8e972 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -465,6 +465,7 @@ bool PlayerbotAIConfig::Initialize() playerbotsXPrate = sConfigMgr->GetOption("AiPlayerbot.KillXPRate", 1); disableDeathKnightLogin = sConfigMgr->GetOption("AiPlayerbot.DisableDeathKnightLogin", 0); botActiveAlone = sConfigMgr->GetOption("AiPlayerbot.BotActiveAlone", 100); + botActiveAloneSmartScale = sConfigMgr->GetOption("AiPlayerbot.botActiveAloneSmartScale", 1); enablePrototypePerformanceDiff = sConfigMgr->GetOption("AiPlayerbot.EnablePrototypePerformanceDiff", false); diffWithPlayer = sConfigMgr->GetOption("AiPlayerbot.DiffWithPlayer", 100); diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index 6caf1179..24609eae 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -263,6 +263,7 @@ public: uint32 playerbotsXPrate; bool disableDeathKnightLogin; uint32 botActiveAlone; + bool botActiveAloneSmartScale; uint32 enablePrototypePerformanceDiff; uint32 diffWithPlayer; From 90464ae3edba024f12a66e52798da193c0b29b59 Mon Sep 17 00:00:00 2001 From: bash Date: Thu, 10 Oct 2024 18:01:54 +0000 Subject: [PATCH 31/40] Added min and max level for smartScale --- conf/playerbots.conf.dist | 4 ++++ src/PlayerbotAI.cpp | 4 +++- src/PlayerbotAIConfig.cpp | 2 ++ src/PlayerbotAIConfig.h | 2 ++ 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index e0356cb7..fe34fde4 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -1464,7 +1464,11 @@ AiPlayerbot.BotActiveAlone = 100 # Specify smart scaling is enabled or not. # The default is 1. When enabled (smart) scales the 'BotActiveAlone' value. +# Only when botLevel is between WhenMinLevel and WhenMaxLevel. AiPlayerbot.botActiveAloneSmartScale = 1 +AiPlayerbot.botActiveAloneSmartScaleWhenMinLevel = 1 +AiPlayerbot.botActiveAloneSmartScaleWhenMaxLevel = 80 + # Premade spell to avoid (undetected spells) # spellid-radius, ... diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 0df602d8..bba3753a 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -4288,7 +4288,9 @@ bool PlayerbotAI::AllowActive(ActivityType activityType) // GetPriorityBracket acitivity float activePerc = 100; - if (sPlayerbotAIConfig->botActiveAloneSmartScale) + if (sPlayerbotAIConfig->botActiveAloneSmartScale && + bot->GetLevel() >= sPlayerbotAIConfig->botActiveAloneSmartScaleWhenMinLevel && + bot->GetLevel() <= sPlayerbotAIConfig->botActiveAloneSmartScaleWhenMaxLevel) { std::pair priorityBracket = GetPriorityBracket(type); if (!priorityBracket.second) return true; diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index d6c8e972..270d3ae5 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -466,6 +466,8 @@ bool PlayerbotAIConfig::Initialize() disableDeathKnightLogin = sConfigMgr->GetOption("AiPlayerbot.DisableDeathKnightLogin", 0); botActiveAlone = sConfigMgr->GetOption("AiPlayerbot.BotActiveAlone", 100); botActiveAloneSmartScale = sConfigMgr->GetOption("AiPlayerbot.botActiveAloneSmartScale", 1); + botActiveAloneSmartScaleWhenMinLevel = sConfigMgr->GetOption("AiPlayerbot.botActiveAloneSmartScaleWhenMinLevel", 1); + botActiveAloneSmartScaleWhenMaxLevel = sConfigMgr->GetOption("AiPlayerbot.botActiveAloneSmartScaleWhenMaxLevel", 80); enablePrototypePerformanceDiff = sConfigMgr->GetOption("AiPlayerbot.EnablePrototypePerformanceDiff", false); diffWithPlayer = sConfigMgr->GetOption("AiPlayerbot.DiffWithPlayer", 100); diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index 24609eae..4a2ec5b9 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -264,6 +264,8 @@ public: bool disableDeathKnightLogin; uint32 botActiveAlone; bool botActiveAloneSmartScale; + uint32 botActiveAloneSmartScaleWhenMinLevel; + uint32 botActiveAloneSmartScaleWhenMaxLevel; uint32 enablePrototypePerformanceDiff; uint32 diffWithPlayer; From 1a47bf429b9067218d09a1d1fdd4792a3f94e8a5 Mon Sep 17 00:00:00 2001 From: Bobblybook Date: Fri, 11 Oct 2024 20:57:58 +1100 Subject: [PATCH 32/40] DTK Implementation DTK Implementation --- src/strategy/AiObjectContext.cpp | 2 + .../dungeons/DungeonStrategyContext.h | 8 +- .../wotlk/WotlkDungeonActionContext.h | 2 +- .../wotlk/WotlkDungeonTriggerContext.h | 2 +- .../wotlk/azjolnerub/AzjolNerubStrategy.h | 2 +- .../DrakTharonKeepActionContext.h | 32 ++++ .../draktharonkeep/DrakTharonKeepActions.cpp | 172 ++++++++++++++++++ .../draktharonkeep/DrakTharonKeepActions.h | 67 +++++++ .../DrakTharonKeepMultipliers.cpp | 62 +++++++ .../DrakTharonKeepMultipliers.h | 24 +++ .../draktharonkeep/DrakTharonKeepStrategy.cpp | 41 +++++ .../draktharonkeep/DrakTharonKeepStrategy.h | 18 ++ .../DrakTharonKeepTriggerContext.h | 28 +++ .../draktharonkeep/DrakTharonKeepTriggers.cpp | 61 +++++++ .../draktharonkeep/DrakTharonKeepTriggers.h | 54 ++++++ .../dungeons/wotlk/draktharonkeep/TODO | 0 .../dungeons/wotlk/nexus/NexusActions.cpp | 2 +- .../utgardekeep/UtgardeKeepMultipliers.cpp | 1 + 18 files changed, 570 insertions(+), 8 deletions(-) create mode 100644 src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepActionContext.h create mode 100644 src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepActions.cpp create mode 100644 src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepActions.h create mode 100644 src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepMultipliers.cpp create mode 100644 src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepMultipliers.h create mode 100644 src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepStrategy.cpp create mode 100644 src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepStrategy.h create mode 100644 src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepTriggerContext.h create mode 100644 src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepTriggers.cpp create mode 100644 src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepTriggers.h delete mode 100644 src/strategy/dungeons/wotlk/draktharonkeep/TODO diff --git a/src/strategy/AiObjectContext.cpp b/src/strategy/AiObjectContext.cpp index cf6b43ea..2c81cefe 100644 --- a/src/strategy/AiObjectContext.cpp +++ b/src/strategy/AiObjectContext.cpp @@ -54,6 +54,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI) actionContexts.Add(new WotlkDungeonNexActionContext()); actionContexts.Add(new WotlkDungeonANActionContext()); actionContexts.Add(new WotlkDungeonOKActionContext()); + actionContexts.Add(new WotlkDungeonDTKActionContext()); triggerContexts.Add(new TriggerContext()); triggerContexts.Add(new ChatTriggerContext()); @@ -68,6 +69,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI) triggerContexts.Add(new WotlkDungeonNexTriggerContext()); triggerContexts.Add(new WotlkDungeonANTriggerContext()); triggerContexts.Add(new WotlkDungeonOKTriggerContext()); + triggerContexts.Add(new WotlkDungeonDTKTriggerContext()); valueContexts.Add(new ValueContext()); diff --git a/src/strategy/dungeons/DungeonStrategyContext.h b/src/strategy/dungeons/DungeonStrategyContext.h index 141953af..faae1372 100644 --- a/src/strategy/dungeons/DungeonStrategyContext.h +++ b/src/strategy/dungeons/DungeonStrategyContext.h @@ -6,11 +6,11 @@ #include "wotlk/nexus/NexusStrategy.h" #include "wotlk/azjolnerub/AzjolNerubStrategy.h" #include "wotlk/oldkingdom/OldKingdomStrategy.h" +#include "wotlk/draktharonkeep/DrakTharonKeepStrategy.h" /* Full list/TODO: -Drak'Tharon Keep - DTK -Trollgore, Novos the Summoner, King Dred, The Prophet Tharon'ja + The Violet Hold - VH Erekem, Moragg, Ichoron, Xevozz, Lavanthor, Zuramat the Obliterator, Cyanigosa Gundrak - GD @@ -75,8 +75,8 @@ class DungeonStrategyContext : public NamedObjectContext static Strategy* wotlk_nex(PlayerbotAI* botAI) { return new WotlkDungeonNexStrategy(botAI); } static Strategy* wotlk_an(PlayerbotAI* botAI) { return new WotlkDungeonANStrategy(botAI); } static Strategy* wotlk_ok(PlayerbotAI* botAI) { return new WotlkDungeonOKStrategy(botAI); } - - static Strategy* wotlk_dtk(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } + static Strategy* wotlk_dtk(PlayerbotAI* botAI) { return new WotlkDungeonDTKStrategy(botAI); } + static Strategy* wotlk_vh(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } static Strategy* wotlk_gd(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } static Strategy* wotlk_hos(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } diff --git a/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h b/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h index 1a2af3ce..eff45c50 100644 --- a/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h +++ b/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h @@ -5,7 +5,7 @@ #include "nexus/NexusActionContext.h" #include "azjolnerub/AzjolNerubActionContext.h" #include "oldkingdom/OldKingdomActionContext.h" -// #include "draktharonkeep/DraktharonKeepActionContext.h" +#include "draktharonkeep/DrakTharonKeepActionContext.h" // #include "violethold/VioletHoldActionContext.h" // #include "gundrak/GundrakActionContext.h" // #include "hallsofstone/HallsOfStoneActionContext.h" diff --git a/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h b/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h index c43672cd..15f98758 100644 --- a/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h +++ b/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h @@ -5,7 +5,7 @@ #include "nexus/NexusTriggerContext.h" #include "azjolnerub/AzjolNerubTriggerContext.h" #include "oldkingdom/OldKingdomTriggerContext.h" -// #include "draktharonkeep/DraktharonKeepTriggerContext.h" +#include "draktharonkeep/DrakTharonKeepTriggerContext.h" // #include "violethold/VioletHoldTriggerContext.h" // #include "gundrak/GundrakTriggerContext.h" // #include "hallsofstone/HallsOfStoneTriggerContext.h" diff --git a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubStrategy.h b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubStrategy.h index 47d5d635..bf5a7917 100644 --- a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubStrategy.h +++ b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubStrategy.h @@ -10,7 +10,7 @@ class WotlkDungeonANStrategy : public Strategy { public: WotlkDungeonANStrategy(PlayerbotAI* ai) : Strategy(ai) {} - virtual std::string const getName() override { return "azjol-nerub"; } + virtual std::string const getName() override { return "azjol'nerub"; } virtual void InitTriggers(std::vector &triggers) override; virtual void InitMultipliers(std::vector &multipliers) override; }; diff --git a/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepActionContext.h b/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepActionContext.h new file mode 100644 index 00000000..1435eedb --- /dev/null +++ b/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepActionContext.h @@ -0,0 +1,32 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONDTKACTIONCONTEXT_H +#define _PLAYERBOT_WOTLKDUNGEONDTKACTIONCONTEXT_H + +#include "Action.h" +#include "NamedObjectContext.h" +#include "DrakTharonKeepActions.h" + +class WotlkDungeonDTKActionContext : public NamedObjectContext +{ + public: + WotlkDungeonDTKActionContext() { + creators["corpse explode spread"] = &WotlkDungeonDTKActionContext::corpse_explode_spread; + creators["avoid arcane field"] = &WotlkDungeonDTKActionContext::avoid_arcane_field; + creators["novos positioning"] = &WotlkDungeonDTKActionContext::novos_positioning; + creators["novos target priority"] = &WotlkDungeonDTKActionContext::novos_target_priority; + creators["slaying strike"] = &WotlkDungeonDTKActionContext::slaying_strike; + creators["tharonja taunt"] = &WotlkDungeonDTKActionContext::taunt; + creators["bone armor"] = &WotlkDungeonDTKActionContext::bone_armor; + creators["touch of life"] = &WotlkDungeonDTKActionContext::touch_of_life; + } + private: + static Action* corpse_explode_spread(PlayerbotAI* ai) { return new CorpseExplodeSpreadAction(ai); } + static Action* avoid_arcane_field(PlayerbotAI* ai) { return new AvoidArcaneFieldAction(ai); } + static Action* novos_positioning(PlayerbotAI* ai) { return new NovosDefaultPositionAction(ai); } + static Action* novos_target_priority(PlayerbotAI* ai) { return new NovosTargetPriorityAction(ai); } + static Action* slaying_strike(PlayerbotAI* ai) { return new CastSlayingStrikeAction(ai); } + static Action* taunt(PlayerbotAI* ai) { return new CastTauntAction(ai); } + static Action* bone_armor(PlayerbotAI* ai) { return new CastBoneArmorAction(ai); } + static Action* touch_of_life(PlayerbotAI* ai) { return new CastTouchOfLifeAction(ai); } +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepActions.cpp b/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepActions.cpp new file mode 100644 index 00000000..3613f467 --- /dev/null +++ b/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepActions.cpp @@ -0,0 +1,172 @@ +#include "Playerbots.h" +#include "DrakTharonKeepActions.h" +#include "DrakTharonKeepStrategy.h" + + +bool CorpseExplodeSpreadAction::Execute(Event event) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "trollgore"); + if (!boss) { return false; } + + float distance = 6.0f; // 5 unit radius, 1 unit added as buffer + GuidVector corpses = AI_VALUE(GuidVector, "nearest corpses"); + for (auto i = corpses.begin(); i != corpses.end(); ++i) + { + Unit* unit = botAI->GetUnit(*i); + if (unit && unit->GetEntry() == NPC_DRAKKARI_INVADER) + { + if (bot->GetExactDist2d(unit) < distance) + { + return MoveAway(unit, distance - bot->GetExactDist2d(unit)); + } + } + } + return false; +} + +bool AvoidArcaneFieldAction::Execute(Event event) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "novos the summoner"); + if (!boss) { return false; } + + float distance = 12.0f; // 11 unit radius, 1 unit added as buffer + if (bot->GetExactDist2d(boss) < distance) + { + return MoveAway(boss, distance - bot->GetExactDist2d(boss)); + } + return false; +} + +bool NovosDefaultPositionAction::isUseful() +{ + // Distance to tether to centre of room + float threshold = 15.0f; + return bot->GetDistance(NOVOS_PARTY_POSITION) > threshold; +} +bool NovosDefaultPositionAction::Execute(Event event) +{ + float clusterDistance = 4.0f; + // Only reposition if we're not killing anything + if (!bot->GetTarget()) + { + return MoveNear(bot->GetMap()->GetId(), + NOVOS_PARTY_POSITION.GetPositionX(), + NOVOS_PARTY_POSITION.GetPositionY(), + NOVOS_PARTY_POSITION.GetPositionZ(), + clusterDistance, MovementPriority::MOVEMENT_NORMAL); + } + return false; +} + +bool NovosTargetPriorityAction::Execute(Event event) +{ + // TODO: This can be improved, some parts are still buggy. + // But it works for now and this fight is very easy + + // Designate a dps char to handle the stairs adds. + // This is probably better as a melee, so just pick the first + // melee dps in the party. If none exist, pick the first ranged. + Player* stairsDps = nullptr; + GuidVector members = AI_VALUE(GuidVector, "group members"); + for (auto& member : members) + { + Player* groupMember = botAI->GetPlayer(member); + if (!groupMember) { continue; } + + if (botAI->IsDps(groupMember)) + { + if (botAI->IsMelee(groupMember)) + { + // Found our first melee dps, grab handle and break + stairsDps = groupMember; + break; + } + else + { + // Ranged dps, only set if none already assigned. + // Don't break, we want to keep searching for a melee instead. + if (!stairsDps) + { + stairsDps = groupMember; + } + } + } + } + + Unit* selectedTargets[2] = {nullptr, nullptr}; + // Target is not findable from threat table using AI_VALUE2(), + // therefore need to search manually for the unit name + GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); + + for (auto i = targets.begin(); i != targets.end(); ++i) + { + Unit* unit = botAI->GetUnit(*i); + if (!unit) { continue; } + uint32 creatureId = unit->GetEntry(); + + // Tank priority: + // Hulking Corpse -> Crystal Handler + if (botAI->IsTank(bot)) + { + if (creatureId == NPC_HULKING_CORPSE) + { + selectedTargets[0] = unit; + } + else if (creatureId == NPC_CRYSTAL_HANDLER) + { + selectedTargets[1] = unit; + } + } + // Dedicated stairs dps is assigned. + // Priority: Risen Shadowcaster -> Fetid Troll Corpse + else if (stairsDps && bot == stairsDps) + { + if (creatureId == NPC_RISEN_SHADOWCASTER) + { + if (!selectedTargets[0] || bot->GetDistance(unit) < bot->GetDistance(selectedTargets[0]) - 5.0f) + { + selectedTargets[0] = unit; + } + + } + else if (creatureId == NPC_FETID_TROLL_CORPSE) + { + if (!selectedTargets[1] || bot->GetDistance(unit) < bot->GetDistance(selectedTargets[1]) - 5.0f) + { + selectedTargets[1] = unit; + } + } + } + // All other dps priority: + // Crystal Handler -> Hulking Corpse + else if (botAI->IsDps(bot)) + { + if (creatureId == NPC_CRYSTAL_HANDLER) + { + selectedTargets[0] = unit; + } + else if (creatureId == NPC_HULKING_CORPSE) + { + selectedTargets[1] = unit; + } + } + } + + for (Unit* primaryTarget : selectedTargets) + { + // Attack the first valid split target in the priority list + if (primaryTarget) + { + if (AI_VALUE(Unit*, "current target") != primaryTarget) + { + // bot->Yell(primaryTarget->GetName(), LANG_UNIVERSAL); + return Attack(primaryTarget); + } + // Don't continue loop here, the target exists so we don't + // want to move down the prio list. We just don't need to send attack + // command again, just return false and exit the loop that way + return false; + } + } + return false; +} diff --git a/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepActions.h b/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepActions.h new file mode 100644 index 00000000..8c59a7c2 --- /dev/null +++ b/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepActions.h @@ -0,0 +1,67 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONDTKACTIONS_H +#define _PLAYERBOT_WOTLKDUNGEONDTKACTIONS_H + +#include "Action.h" +#include "AttackAction.h" +#include "GenericSpellActions.h" +#include "PlayerbotAI.h" +#include "Playerbots.h" +#include "DrakTharonKeepTriggers.h" + +const Position NOVOS_PARTY_POSITION = Position(-378.852f, -760.349f, 28.587f); + +class CorpseExplodeSpreadAction : public MovementAction +{ +public: + CorpseExplodeSpreadAction(PlayerbotAI* ai) : MovementAction(ai, "corpse explode spread") {} + bool Execute(Event event) override; +}; + +class AvoidArcaneFieldAction : public MovementAction +{ +public: + AvoidArcaneFieldAction(PlayerbotAI* ai) : MovementAction(ai, "avoid arcane field") {} + bool Execute(Event event) override; +}; + +class NovosDefaultPositionAction : public MovementAction +{ +public: + NovosDefaultPositionAction(PlayerbotAI* ai) : MovementAction(ai, "novos default position") {} + bool Execute(Event event) override; + bool isUseful() override; +}; + +class NovosTargetPriorityAction : public AttackAction +{ +public: + NovosTargetPriorityAction(PlayerbotAI* ai) : AttackAction(ai, "novos target priority") {} + bool Execute(Event event) override; + // bool isUseful() override; +}; + +class CastSlayingStrikeAction : public CastMeleeSpellAction +{ +public: + CastSlayingStrikeAction(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, "slaying strike") {} +}; + +class CastTauntAction : public CastSpellAction +{ +public: + CastTauntAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "taunt") {} +}; + +class CastBoneArmorAction : public CastSpellAction +{ +public: + CastBoneArmorAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "bone armor") {} +}; + +class CastTouchOfLifeAction : public CastSpellAction +{ +public: + CastTouchOfLifeAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "touch of life") {} +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepMultipliers.cpp b/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepMultipliers.cpp new file mode 100644 index 00000000..bdb9d4d6 --- /dev/null +++ b/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepMultipliers.cpp @@ -0,0 +1,62 @@ +#include "DrakTharonKeepMultipliers.h" +#include "DrakTharonKeepActions.h" +#include "GenericSpellActions.h" +#include "ChooseTargetActions.h" +#include "MovementActions.h" +#include "DrakTharonKeepTriggers.h" +#include "Action.h" + +float NovosMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "novos the summoner"); + if (!boss) { return 1.0f; } + + if (boss->FindCurrentSpellBySpellId(SPELL_ARCANE_FIELD) && bot->GetTarget()) + { + if (dynamic_cast(action) + || dynamic_cast(action)) + { + return 0.0f; + } + } + return 1.0f; +} + +float TharonjaMultiplier::GetValue(Action* action) +{ + if (!bot->HasAura(SPELL_GIFT_OF_THARONJA)) { return 1.0f; } + + // Suppress all skills that are not enabled in skeleton form. + // Still allow non-ability actions such as movement + if (dynamic_cast(action) + && !dynamic_cast(action) + && !dynamic_cast(action) + && !dynamic_cast(action) + && !dynamic_cast(action)) + { + return 0.0f; + } + // Also suppress FleeAction to prevent ranged characters from avoiding melee range + if (dynamic_cast(action)) + { + return 0.0f; + } + + // Tanks should only taunt, no slaying strike + if (botAI->IsTank(bot)) + { + if (dynamic_cast(action)) + { + return 0.0f; + } + } + // Dps & healer should not taunt + else + { + if (dynamic_cast(action)) + { + return 0.0f; + } + } + return 1.0f; +} diff --git a/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepMultipliers.h b/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepMultipliers.h new file mode 100644 index 00000000..c8f3c813 --- /dev/null +++ b/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepMultipliers.h @@ -0,0 +1,24 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONDTKMULTIPLIERS_H +#define _PLAYERBOT_WOTLKDUNGEONDTKMULTIPLIERS_H + +#include "Multiplier.h" + +class NovosMultiplier : public Multiplier +{ + public: + NovosMultiplier(PlayerbotAI* ai) : Multiplier(ai, "novos the summoner") {} + + public: + virtual float GetValue(Action* action); +}; + +class TharonjaMultiplier : public Multiplier +{ + public: + TharonjaMultiplier(PlayerbotAI* ai) : Multiplier(ai, "the prophet tharon'ja") {} + + public: + virtual float GetValue(Action* action); +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepStrategy.cpp b/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepStrategy.cpp new file mode 100644 index 00000000..7685b3d5 --- /dev/null +++ b/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepStrategy.cpp @@ -0,0 +1,41 @@ +#include "DrakTharonKeepStrategy.h" +#include "DrakTharonKeepMultipliers.h" + + +void WotlkDungeonDTKStrategy::InitTriggers(std::vector &triggers) +{ + // Trollgore + triggers.push_back(new TriggerNode("corpse explode", + NextAction::array(0, new NextAction("corpse explode spread", ACTION_MOVE + 5), nullptr))); + + // Novos the Summoner + // TODO: Can be improved - it's a pretty easy fight but complex to program, revisit if needed + triggers.push_back(new TriggerNode("arcane field", + NextAction::array(0, new NextAction("avoid arcane field", ACTION_MOVE + 5), nullptr))); + triggers.push_back(new TriggerNode("arcane field", + NextAction::array(0, new NextAction("novos positioning", ACTION_MOVE + 4), nullptr))); + triggers.push_back(new TriggerNode("arcane field", + NextAction::array(0, new NextAction("novos target priority", ACTION_NORMAL + 1), nullptr))); + + // King Dred + // TODO: Fear ward / tremor totem, or general anti-fear strat development + + //The Prophet Tharon'ja + triggers.push_back(new TriggerNode("gift of tharon'ja", + NextAction::array(0, new NextAction("touch of life", ACTION_NORMAL + 5), nullptr))); + triggers.push_back(new TriggerNode("gift of tharon'ja", + NextAction::array(0, new NextAction("bone armor", ACTION_NORMAL + 4), nullptr))); + // Run ranged chars (who would normally stand at range) into melee, to dps in skeleton form + triggers.push_back(new TriggerNode("tharon'ja out of melee", + NextAction::array(0, new NextAction("reach melee", ACTION_NORMAL + 3), nullptr))); + triggers.push_back(new TriggerNode("gift of tharon'ja", + NextAction::array(0, new NextAction("taunt", ACTION_NORMAL + 2), nullptr))); + triggers.push_back(new TriggerNode("gift of tharon'ja", + NextAction::array(0, new NextAction("slaying strike", ACTION_NORMAL + 2), nullptr))); +} + +void WotlkDungeonDTKStrategy::InitMultipliers(std::vector &multipliers) +{ + multipliers.push_back(new NovosMultiplier(botAI)); + multipliers.push_back(new TharonjaMultiplier(botAI)); +} diff --git a/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepStrategy.h b/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepStrategy.h new file mode 100644 index 00000000..b819ad38 --- /dev/null +++ b/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepStrategy.h @@ -0,0 +1,18 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONDTKSTRATEGY_H +#define _PLAYERBOT_WOTLKDUNGEONDTKSTRATEGY_H + +#include "Multiplier.h" +#include "AiObjectContext.h" +#include "Strategy.h" + + +class WotlkDungeonDTKStrategy : public Strategy +{ +public: + WotlkDungeonDTKStrategy(PlayerbotAI* ai) : Strategy(ai) {} + virtual std::string const getName() override { return "drak'tharon keep"; } + virtual void InitTriggers(std::vector &triggers) override; + virtual void InitMultipliers(std::vector &multipliers) override; +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepTriggerContext.h b/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepTriggerContext.h new file mode 100644 index 00000000..e98f4372 --- /dev/null +++ b/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepTriggerContext.h @@ -0,0 +1,28 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONDTKTRIGGERCONTEXT_H +#define _PLAYERBOT_WOTLKDUNGEONDTKTRIGGERCONTEXT_H + +#include "NamedObjectContext.h" +#include "AiObjectContext.h" +#include "DrakTharonKeepTriggers.h" + +class WotlkDungeonDTKTriggerContext : public NamedObjectContext +{ + public: + WotlkDungeonDTKTriggerContext() + { + creators["corpse explode"] = &WotlkDungeonDTKTriggerContext::corpse_explode; + creators["arcane field"] = &WotlkDungeonDTKTriggerContext::arcane_field; + // creators["crystal handler"] = &WotlkDungeonDTKTriggerContext::crystal_handler; + creators["gift of tharon'ja"] = &WotlkDungeonDTKTriggerContext::gift_of_tharonja; + creators["tharon'ja out of melee"] = &WotlkDungeonDTKTriggerContext::tharonja_out_of_melee; + + } + private: + static Trigger* corpse_explode(PlayerbotAI* ai) { return new CorpseExplodeTrigger(ai); } + static Trigger* arcane_field(PlayerbotAI* ai) { return new ArcaneFieldTrigger(ai); } + // static Trigger* crystal_handler(PlayerbotAI* ai) { return new CrystalHandlerTrigger(ai); } + static Trigger* gift_of_tharonja(PlayerbotAI* ai) { return new GiftOfTharonjaTrigger(ai); } + static Trigger* tharonja_out_of_melee(PlayerbotAI* ai) { return new TwoTriggers(ai, "gift of tharon'ja", "enemy out of melee"); } +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepTriggers.cpp b/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepTriggers.cpp new file mode 100644 index 00000000..0b9e439b --- /dev/null +++ b/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepTriggers.cpp @@ -0,0 +1,61 @@ +#include "Playerbots.h" +#include "DrakTharonKeepTriggers.h" +#include "AiObject.h" +#include "AiObjectContext.h" + + +bool CorpseExplodeTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "trollgore"); + if (!boss) { return false; } + + float distance = 6.0f; // 5 unit radius, 1 unit added as buffer + GuidVector corpses = AI_VALUE(GuidVector, "nearest corpses"); + for (auto i = corpses.begin(); i != corpses.end(); ++i) + { + Unit* unit = botAI->GetUnit(*i); + if (unit && unit->GetEntry() == NPC_DRAKKARI_INVADER) + { + if (bot->GetExactDist2d(unit) < distance) + { + return true; + } + } + } + return false; +} + +bool ArcaneFieldTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "novos the summoner"); + if (boss) + { + return boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_ARCANE_FIELD); + } + return false; +} + +// bool CrystalHandlerTrigger::IsActive() +// { +// Unit* boss = AI_VALUE2(Unit*, "find target", "novos the summoner"); +// if (!boss) { return false; } + +// // Target is not findable from threat table using AI_VALUE2(), +// // therefore need to search manually for the unit name +// GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); + +// for (auto i = targets.begin(); i != targets.end(); ++i) +// { +// Unit* unit = botAI->GetUnit(*i); +// if (unit && unit->GetEntry() == NPC_CRYSTAL_HANDLER) +// { +// return true; +// } +// } +// return false; +// } + +bool GiftOfTharonjaTrigger::IsActive() +{ + return bool(bot->HasAura(SPELL_GIFT_OF_THARONJA)); +} diff --git a/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepTriggers.h b/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepTriggers.h new file mode 100644 index 00000000..627200e8 --- /dev/null +++ b/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepTriggers.h @@ -0,0 +1,54 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONDTKTRIGGERS_H +#define _PLAYERBOT_WOTLKDUNGEONDTKTRIGGERS_H + +#include "Trigger.h" +#include "PlayerbotAIConfig.h" +#include "GenericTriggers.h" +#include "DungeonStrategyUtils.h" + +enum DrakTharonIDs +{ + // Trollgore + NPC_DRAKKARI_INVADER = 27709, + + // Novos the Summoner + NPC_NOVOS = 26631, + SPELL_ARCANE_FIELD = 47346, + NPC_CRYSTAL_HANDLER = 26627, + NPC_HULKING_CORPSE = 27597, + NPC_RISEN_SHADOWCASTER = 27600, + NPC_FETID_TROLL_CORPSE = 27598, + + // The Prophet Tharon'ja + SPELL_GIFT_OF_THARONJA = 52509, +}; + +class CorpseExplodeTrigger : public Trigger +{ +public: + CorpseExplodeTrigger(PlayerbotAI* ai) : Trigger(ai, "corpse explode") {} + bool IsActive() override; +}; + +class ArcaneFieldTrigger : public Trigger +{ +public: + ArcaneFieldTrigger(PlayerbotAI* ai) : Trigger(ai, "arcane field") {} + bool IsActive() override; +}; + +// class CrystalHandlerTrigger : public Trigger +// { +// public: +// CrystalHandlerTrigger(PlayerbotAI* ai) : Trigger(ai, "crystal handler") {} +// bool IsActive() override; +// }; + +class GiftOfTharonjaTrigger : public Trigger +{ +public: + GiftOfTharonjaTrigger(PlayerbotAI* ai) : Trigger(ai, "gift of tharon'ja") {} + bool IsActive() override; +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/draktharonkeep/TODO b/src/strategy/dungeons/wotlk/draktharonkeep/TODO deleted file mode 100644 index e69de29b..00000000 diff --git a/src/strategy/dungeons/wotlk/nexus/NexusActions.cpp b/src/strategy/dungeons/wotlk/nexus/NexusActions.cpp index 8c0b1311..cbeaa6f4 100644 --- a/src/strategy/dungeons/wotlk/nexus/NexusActions.cpp +++ b/src/strategy/dungeons/wotlk/nexus/NexusActions.cpp @@ -182,11 +182,11 @@ bool RearFlankPositionAction::Execute(Event event) // Need to reduce this value very slightly, or the bots get the jitters - // may be due to rounding errors. Need to bring them just inside their attack range. // This boss has a big hitbox so we can reduce by 50% and it's still fine and looks better. + // TODO: Investigate using bot->GetObjectSize() for sizing float distance = bot->GetMeleeRange(boss) * 0.5f; // Alternatively, summing both unit's melee ranges seems to give a fairly natural range. // Use whichever gives the best results.. // float distanceOffset = bot->GetMeleeReach() + boss->GetMeleeReach(); - Position leftFlank = boss->GetPosition(); Position rightFlank = boss->GetPosition(); Position* destination = nullptr; diff --git a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepMultipliers.cpp b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepMultipliers.cpp index d46afd9b..31d310fa 100644 --- a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepMultipliers.cpp +++ b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepMultipliers.cpp @@ -17,6 +17,7 @@ float PrinceKelesethMultiplier::GetValue(Action* action) } return 1.0f; } + float SkarvaldAndDalronnMultiplier::GetValue(Action* action) { // Unit* skarvald = AI_VALUE2(Unit*, "find target", "skarvald the constructor"); From 161a29657b6e92f7aee55aa2e3753f4be5882906 Mon Sep 17 00:00:00 2001 From: Bobblybook Date: Fri, 11 Oct 2024 23:40:06 +1100 Subject: [PATCH 33/40] corrections --- .../dungeons/wotlk/draktharonkeep/DrakTharonKeepActions.cpp | 2 +- .../dungeons/wotlk/draktharonkeep/DrakTharonKeepTriggers.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepActions.cpp b/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepActions.cpp index 3613f467..7d71d26a 100644 --- a/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepActions.cpp +++ b/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepActions.cpp @@ -154,7 +154,7 @@ bool NovosTargetPriorityAction::Execute(Event event) for (Unit* primaryTarget : selectedTargets) { - // Attack the first valid split target in the priority list + // Attack the first valid target in the priority list if (primaryTarget) { if (AI_VALUE(Unit*, "current target") != primaryTarget) diff --git a/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepTriggers.cpp b/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepTriggers.cpp index 0b9e439b..9ed4b191 100644 --- a/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepTriggers.cpp +++ b/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepTriggers.cpp @@ -57,5 +57,5 @@ bool ArcaneFieldTrigger::IsActive() bool GiftOfTharonjaTrigger::IsActive() { - return bool(bot->HasAura(SPELL_GIFT_OF_THARONJA)); + return bot->HasAura(SPELL_GIFT_OF_THARONJA); } From c6f9f43a33d69395765ccd446d2ac570c8b2281d Mon Sep 17 00:00:00 2001 From: bash Date: Fri, 11 Oct 2024 18:48:00 +0000 Subject: [PATCH 34/40] [performance] Tweaked autoscale, too many rndbots were idle which defeats the purpose of having rndbots --- src/PlayerbotAI.cpp | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index bba3753a..c42ad985 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -4187,8 +4187,8 @@ ActivePiorityType PlayerbotAI::GetPriorityType(ActivityType activityType) } // Returns the lower and upper bracket for bots to be active. -// Ie. 10,20 means all bots in this bracket will be inactive below 10% activityMod, all bots in this bracket will be -// active above 20% activityMod and scale between those values. +// Ie. { 10, 20 } means all bots in this bracket will be inactive below 10% activityMod, +// and will be active above 20% activityMod and scale between those values. std::pair PlayerbotAI::GetPriorityBracket(ActivePiorityType type) { switch (type) @@ -4203,26 +4203,24 @@ std::pair PlayerbotAI::GetPriorityBracket(ActivePiorityType type case ActivePiorityType::IN_COMBAT: return {0, 10}; case ActivePiorityType::IN_BG_QUEUE: - return {0, 20}; case ActivePiorityType::IN_LFG: - return {0, 30}; + return {0, 20}; case ActivePiorityType::NEARBY_PLAYER: - return {0, 40}; + return {0, 30}; case ActivePiorityType::PLAYER_FRIEND: + return {0, 40}; + case ActivePiorityType::IN_ACTIVE_AREA: case ActivePiorityType::PLAYER_GUILD: return {0, 50}; - case ActivePiorityType::IN_ACTIVE_AREA: - case ActivePiorityType::IN_EMPTY_SERVER: - return {50, 100}; case ActivePiorityType::IN_ACTIVE_MAP: - return {70, 100}; + return {20, 100}; case ActivePiorityType::IN_INACTIVE_MAP: - return {80, 100}; - default: + return {50, 100}; + case ActivePiorityType::IN_EMPTY_SERVER: return {90, 100}; + default: + return {50, 100}; } - - return {90, 100}; } bool PlayerbotAI::AllowActive(ActivityType activityType) From 158d923c15d1bb062c465261ff091e2f4976c8de Mon Sep 17 00:00:00 2001 From: bash Date: Fri, 11 Oct 2024 21:20:43 +0000 Subject: [PATCH 35/40] Autoscale latency modifier --- conf/playerbots.conf.dist | 12 +++++------- src/PlayerbotAIConfig.cpp | 14 ++++++++------ src/PlayerbotAIConfig.h | 6 ++---- src/RandomPlayerbotMgr.cpp | 11 ++++------- .../actions/BattleGroundJoinAction.cpp | 18 ++++++++---------- 5 files changed, 27 insertions(+), 34 deletions(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index fe34fde4..c7d23eed 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -1263,11 +1263,6 @@ Playerbots.Updates.EnableDatabases = 1 # Command server port, 0 - disabled AiPlayerbot.CommandServerPort = 8888 -# Diff with/without player in server. The server will tune bot activity to reach the desired server tick speed (in ms).# PLAYERBOT SYSTEM SETTINGS # -AiPlayerbot.EnablePrototypePerformanceDiff = 0 -AiPlayerbot.DiffWithPlayer = 100 -AiPlayerbot.DiffEmpty = 200 - # # # @@ -1464,11 +1459,14 @@ AiPlayerbot.BotActiveAlone = 100 # Specify smart scaling is enabled or not. # The default is 1. When enabled (smart) scales the 'BotActiveAlone' value. -# Only when botLevel is between WhenMinLevel and WhenMaxLevel. AiPlayerbot.botActiveAloneSmartScale = 1 +# Only when botLevel is between WhenMinLevel and WhenMaxLevel. AiPlayerbot.botActiveAloneSmartScaleWhenMinLevel = 1 AiPlayerbot.botActiveAloneSmartScaleWhenMaxLevel = 80 - +# The server will tune bot activity to reach the desired server tick speed (in ms) +# bots will only join battleground when there is no lag based on latency diffs below +AiPlayerbot.botActiveAloneSmartScaleDiffWithPlayer = 100 +AiPlayerbot.botActiveAloneSmartScaleDiffEmpty = 200 # Premade spell to avoid (undetected spells) # spellid-radius, ... diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index 270d3ae5..816a345f 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -466,12 +466,14 @@ bool PlayerbotAIConfig::Initialize() disableDeathKnightLogin = sConfigMgr->GetOption("AiPlayerbot.DisableDeathKnightLogin", 0); botActiveAlone = sConfigMgr->GetOption("AiPlayerbot.BotActiveAlone", 100); botActiveAloneSmartScale = sConfigMgr->GetOption("AiPlayerbot.botActiveAloneSmartScale", 1); - botActiveAloneSmartScaleWhenMinLevel = sConfigMgr->GetOption("AiPlayerbot.botActiveAloneSmartScaleWhenMinLevel", 1); - botActiveAloneSmartScaleWhenMaxLevel = sConfigMgr->GetOption("AiPlayerbot.botActiveAloneSmartScaleWhenMaxLevel", 80); - - enablePrototypePerformanceDiff = sConfigMgr->GetOption("AiPlayerbot.EnablePrototypePerformanceDiff", false); - diffWithPlayer = sConfigMgr->GetOption("AiPlayerbot.DiffWithPlayer", 100); - diffEmpty = sConfigMgr->GetOption("AiPlayerbot.DiffEmpty", 200); + botActiveAloneSmartScaleWhenMinLevel = + sConfigMgr->GetOption("AiPlayerbot.botActiveAloneSmartScaleWhenMinLevel", 1); + botActiveAloneSmartScaleWhenMaxLevel = + sConfigMgr->GetOption("AiPlayerbot.botActiveAloneSmartScaleWhenMaxLevel", 80); + botActiveAloneSmartScaleDiffWithPlayer = + sConfigMgr->GetOption("AiPlayerbot.botActiveAloneSmartScaleDiffWithPlayer", 100); + botActiveAloneSmartScaleDiffEmpty = + sConfigMgr->GetOption("AiPlayerbot.botActiveAloneSmartScaleDiffEmpty", 200); randombotsWalkingRPG = sConfigMgr->GetOption("AiPlayerbot.RandombotsWalkingRPG", false); randombotsWalkingRPGInDoors = sConfigMgr->GetOption("AiPlayerbot.RandombotsWalkingRPG.InDoors", false); diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index 4a2ec5b9..5ac3fa4a 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -266,10 +266,8 @@ public: bool botActiveAloneSmartScale; uint32 botActiveAloneSmartScaleWhenMinLevel; uint32 botActiveAloneSmartScaleWhenMaxLevel; - - uint32 enablePrototypePerformanceDiff; - uint32 diffWithPlayer; - uint32 diffEmpty; + uint32 botActiveAloneSmartScaleDiffWithPlayer; + uint32 botActiveAloneSmartScaleDiffEmpty; bool freeMethodLoot; int32 lootRollLevel; diff --git a/src/RandomPlayerbotMgr.cpp b/src/RandomPlayerbotMgr.cpp index 5d3b5edf..9991144a 100644 --- a/src/RandomPlayerbotMgr.cpp +++ b/src/RandomPlayerbotMgr.cpp @@ -292,12 +292,8 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/) if (!sPlayerbotAIConfig->randomBotAutologin || !sPlayerbotAIConfig->enabled) return; - if (sPlayerbotAIConfig->enablePrototypePerformanceDiff) + if (sPlayerbotAIConfig->botActiveAloneSmartScale) { - LOG_INFO("playerbots", "---------------------------------------"); - LOG_INFO("playerbots", - "PROTOTYPE: Playerbot performance enhancements are active. Issues and instability may occur."); - LOG_INFO("playerbots", "---------------------------------------"); ScaleBotActivity(); } @@ -414,8 +410,9 @@ void RandomPlayerbotMgr::ScaleBotActivity() // max/min activity // % increase/decrease wanted diff , avg diff - float activityPercentageMod = pid.calculate( - sRandomPlayerbotMgr->GetPlayers().empty() ? sPlayerbotAIConfig->diffEmpty : sPlayerbotAIConfig->diffWithPlayer, + float activityPercentageMod = pid.calculate(sRandomPlayerbotMgr->GetPlayers().empty() ? + sPlayerbotAIConfig->botActiveAloneSmartScaleDiffEmpty : + sPlayerbotAIConfig->botActiveAloneSmartScaleDiffWithPlayer, sWorldUpdateTime.GetAverageUpdateTime()); activityPercentage = activityPercentageMod + 50; diff --git a/src/strategy/actions/BattleGroundJoinAction.cpp b/src/strategy/actions/BattleGroundJoinAction.cpp index e6842b23..349ae410 100644 --- a/src/strategy/actions/BattleGroundJoinAction.cpp +++ b/src/strategy/actions/BattleGroundJoinAction.cpp @@ -234,16 +234,15 @@ bool BGJoinAction::shouldJoinBg(BattlegroundQueueTypeId queueTypeId, Battlegroun return false; TeamId teamId = bot->GetTeamId(); - bool noLag = sWorldUpdateTime.GetAverageUpdateTime() < (sRandomPlayerbotMgr->GetPlayers().empty() - ? sPlayerbotAIConfig->diffEmpty - : sPlayerbotAIConfig->diffWithPlayer) * - 1.1; + bool noLag = sWorldUpdateTime.GetAverageUpdateTime() < (sRandomPlayerbotMgr->GetPlayers().empty() ? + sPlayerbotAIConfig->botActiveAloneSmartScaleDiffEmpty : + sPlayerbotAIConfig->botActiveAloneSmartScaleDiffWithPlayer) * 1.1; uint32 BracketSize = bg->GetMaxPlayersPerTeam() * 2; uint32 TeamSize = bg->GetMaxPlayersPerTeam(); // If performance diff is enabled, only queue if there is no lag - if (sPlayerbotAIConfig->enablePrototypePerformanceDiff && !noLag) + if (sPlayerbotAIConfig->botActiveAloneSmartScale && !noLag) return false; // If the bot is in a group, only the leader can queue @@ -578,16 +577,15 @@ bool FreeBGJoinAction::shouldJoinBg(BattlegroundQueueTypeId queueTypeId, Battleg return false; TeamId teamId = bot->GetTeamId(); - bool noLag = sWorldUpdateTime.GetAverageUpdateTime() < (sRandomPlayerbotMgr->GetPlayers().empty() - ? sPlayerbotAIConfig->diffEmpty - : sPlayerbotAIConfig->diffWithPlayer) * - 1.1; + bool noLag = sWorldUpdateTime.GetAverageUpdateTime() < (sRandomPlayerbotMgr->GetPlayers().empty() ? + sPlayerbotAIConfig->botActiveAloneSmartScaleDiffEmpty : + sPlayerbotAIConfig->botActiveAloneSmartScaleDiffWithPlayer) * 1.1; uint32 BracketSize = bg->GetMaxPlayersPerTeam() * 2; uint32 TeamSize = bg->GetMaxPlayersPerTeam(); // If performance diff is enabled, only queue if there is no lag - if (sPlayerbotAIConfig->enablePrototypePerformanceDiff && !noLag) + if (sPlayerbotAIConfig->botActiveAloneSmartScale && !noLag) return false; // If the bot is in a group, only the leader can queue From 8c86c6b6d79453f5a4568f3c646b5686df10f476 Mon Sep 17 00:00:00 2001 From: bash Date: Sat, 12 Oct 2024 10:34:45 +0000 Subject: [PATCH 36/40] [performance] SmartScale slighty more aggresive, after testing i found some bots to active --- src/PlayerbotAI.cpp | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index c42ad985..e3fe0c4c 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -4190,7 +4190,7 @@ ActivePiorityType PlayerbotAI::GetPriorityType(ActivityType activityType) // Ie. { 10, 20 } means all bots in this bracket will be inactive below 10% activityMod, // and will be active above 20% activityMod and scale between those values. std::pair PlayerbotAI::GetPriorityBracket(ActivePiorityType type) -{ +{ switch (type) { case ActivePiorityType::HAS_REAL_PLAYER_MASTER: @@ -4203,24 +4203,27 @@ std::pair PlayerbotAI::GetPriorityBracket(ActivePiorityType type case ActivePiorityType::IN_COMBAT: return {0, 10}; case ActivePiorityType::IN_BG_QUEUE: - case ActivePiorityType::IN_LFG: return {0, 20}; - case ActivePiorityType::NEARBY_PLAYER: + case ActivePiorityType::IN_LFG: return {0, 30}; - case ActivePiorityType::PLAYER_FRIEND: + case ActivePiorityType::NEARBY_PLAYER: return {0, 40}; - case ActivePiorityType::IN_ACTIVE_AREA: + case ActivePiorityType::PLAYER_FRIEND: case ActivePiorityType::PLAYER_GUILD: return {0, 50}; + case ActivePiorityType::IN_ACTIVE_AREA: + return {30, 100}; case ActivePiorityType::IN_ACTIVE_MAP: - return {20, 100}; + return {50, 100}; case ActivePiorityType::IN_INACTIVE_MAP: - return {50, 100}; + return {70, 100}; case ActivePiorityType::IN_EMPTY_SERVER: - return {90, 100}; + return {80, 100}; default: - return {50, 100}; + return {90, 100}; } + + return {90, 100}; } bool PlayerbotAI::AllowActive(ActivityType activityType) From b526a07303a0c4cc822a7732558922eb512bf157 Mon Sep 17 00:00:00 2001 From: Bobblybook Date: Sat, 12 Oct 2024 23:51:00 +1100 Subject: [PATCH 37/40] Generalise dragon flanking action --- src/strategy/actions/ActionContext.h | 2 ++ src/strategy/actions/MovementActions.cpp | 40 ++++++++++++++++++++++++ src/strategy/actions/MovementActions.h | 25 +++++++++++++++ 3 files changed, 67 insertions(+) diff --git a/src/strategy/actions/ActionContext.h b/src/strategy/actions/ActionContext.h index e195e2f9..c13f7162 100644 --- a/src/strategy/actions/ActionContext.h +++ b/src/strategy/actions/ActionContext.h @@ -94,6 +94,7 @@ public: creators["avoid aoe"] = &ActionContext::avoid_aoe; creators["combat formation move"] = &ActionContext::combat_formation_move; creators["tank face"] = &ActionContext::tank_face; + creators["rear flank"] = &ActionContext::rear_flank; creators["disperse set"] = &ActionContext::disperse_set; creators["gift of the naaru"] = &ActionContext::gift_of_the_naaru; creators["shoot"] = &ActionContext::shoot; @@ -278,6 +279,7 @@ private: static Action* avoid_aoe(PlayerbotAI* botAI) { return new AvoidAoeAction(botAI); } static Action* combat_formation_move(PlayerbotAI* botAI) { return new CombatFormationMoveAction(botAI); } static Action* tank_face(PlayerbotAI* botAI) { return new TankFaceAction(botAI); } + static Action* rear_flank(PlayerbotAI* botAI) { return new RearFlankAction(botAI); } static Action* disperse_set(PlayerbotAI* botAI) { return new DisperseSetAction(botAI); } static Action* gift_of_the_naaru(PlayerbotAI* botAI) { return new CastGiftOfTheNaaruAction(botAI); } static Action* lifeblood(PlayerbotAI* botAI) { return new CastLifeBloodAction(botAI); } diff --git a/src/strategy/actions/MovementActions.cpp b/src/strategy/actions/MovementActions.cpp index 569d7fcc..791f0990 100644 --- a/src/strategy/actions/MovementActions.cpp +++ b/src/strategy/actions/MovementActions.cpp @@ -2372,6 +2372,46 @@ bool TankFaceAction::Execute(Event event) return MoveTo(bot->GetMapId(), nearest.GetPositionX(), nearest.GetPositionY(), nearest.GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_COMBAT); } +bool RearFlankAction::isUseful() +{ + Unit* target = AI_VALUE(Unit*, "current target"); + if (!target) { return false; } + + // Need to double the front angle check to account for mirrored angle. + bool inFront = target->HasInArc(2.f * minAngle, bot); + // Rear check does not need to double this angle as the logic is inverted + // and we are subtracting from 2pi. + bool inRear = !target->HasInArc((2.f * M_PI) - maxAngle, bot); + + return inFront || inRear; +} + +bool RearFlankAction::Execute(Event event) +{ + Unit* target = AI_VALUE(Unit*, "current target"); + if (!target) { return false; } + + float angle = frand(minAngle, maxAngle); + float baseDistance = bot->GetMeleeRange(target) * 0.5f; + Position leftFlank = target->GetPosition(); + Position rightFlank = target->GetPosition(); + Position* destination = nullptr; + leftFlank.RelocatePolarOffset(angle, baseDistance + distance); + rightFlank.RelocatePolarOffset(-angle, baseDistance + distance); + + if (bot->GetExactDist2d(leftFlank) < bot->GetExactDist2d(rightFlank)) + { + destination = &leftFlank; + } + else + { + destination = &rightFlank; + } + + return MoveTo(bot->GetMapId(), destination->GetPositionX(), destination->GetPositionY(), destination->GetPositionZ(), + false, false, false, true, MovementPriority::MOVEMENT_COMBAT); +} + bool DisperseSetAction::Execute(Event event) { std::string const text = event.getParam(); diff --git a/src/strategy/actions/MovementActions.h b/src/strategy/actions/MovementActions.h index 7b03f034..76929427 100644 --- a/src/strategy/actions/MovementActions.h +++ b/src/strategy/actions/MovementActions.h @@ -18,6 +18,9 @@ class Unit; class WorldObject; class Position; +#define ANGLE_45_DEG (static_cast(M_PI) / 4.f) +#define ANGLE_90_DEG M_PI_2 +#define ANGLE_120_DEG (2.f * static_cast(M_PI) / 3.f) class MovementAction : public Action { @@ -144,6 +147,27 @@ public: bool Execute(Event event) override; }; +class RearFlankAction : public MovementAction +{ +// 90 degree minimum angle prevents any frontal cleaves/breaths and avoids parry-hasting the boss. +// 120 degree maximum angle leaves a 120 degree symmetrical cone at the tail end which is usually enough to avoid tail swipes. +// Some dragons or mobs may have different danger zone angles, override if needed. +public: + RearFlankAction(PlayerbotAI* botAI, float distance = 0.0f, float minAngle = ANGLE_90_DEG, float maxAngle = ANGLE_120_DEG) + : MovementAction(botAI, "rear flank") + { + this->distance = distance; + this->minAngle = minAngle; + this->maxAngle = maxAngle; + } + + bool Execute(Event event) override; + bool isUseful() override; + +protected: + float distance, minAngle, maxAngle; +}; + class DisperseSetAction : public Action { public: @@ -268,4 +292,5 @@ public: bool Execute(Event event) override; }; + #endif From 7b9da575effb0250b35a7d6919fd57dba9c98db4 Mon Sep 17 00:00:00 2001 From: Bobblybook Date: Sat, 12 Oct 2024 23:52:40 +1100 Subject: [PATCH 38/40] Violet Hold implementation Violet Hold implementation Consortium guy is missing, will revisit once I reach heroics so I can code it properly --- src/strategy/AiObjectContext.cpp | 2 + .../dungeons/DungeonStrategyContext.h | 7 +- .../wotlk/WotlkDungeonActionContext.h | 2 +- .../wotlk/WotlkDungeonTriggerContext.h | 2 +- .../azjolnerub/AzjolNerubMultipliers.cpp | 4 +- .../draktharonkeep/DrakTharonKeepActions.cpp | 2 + .../dungeons/wotlk/nexus/NexusActionContext.h | 2 - .../dungeons/wotlk/nexus/NexusActions.cpp | 47 --------- .../dungeons/wotlk/nexus/NexusActions.h | 8 -- .../dungeons/wotlk/nexus/NexusStrategy.cpp | 6 +- .../wotlk/nexus/NexusTriggerContext.h | 4 +- .../dungeons/wotlk/nexus/NexusTriggers.cpp | 6 +- .../dungeons/wotlk/nexus/NexusTriggers.h | 4 +- src/strategy/dungeons/wotlk/violethold/TODO | 0 .../violethold/VioletHoldActionContext.h | 24 +++++ .../wotlk/violethold/VioletHoldActions.cpp | 99 +++++++++++++++++++ .../wotlk/violethold/VioletHoldActions.h | 48 +++++++++ .../violethold/VioletHoldMultipliers.cpp | 57 +++++++++++ .../wotlk/violethold/VioletHoldMultipliers.h | 33 +++++++ .../wotlk/violethold/VioletHoldStrategy.cpp | 41 ++++++++ .../wotlk/violethold/VioletHoldStrategy.h | 18 ++++ .../violethold/VioletHoldTriggerContext.h | 27 +++++ .../wotlk/violethold/VioletHoldTriggers.cpp | 76 ++++++++++++++ .../wotlk/violethold/VioletHoldTriggers.h | 59 +++++++++++ 24 files changed, 505 insertions(+), 73 deletions(-) delete mode 100644 src/strategy/dungeons/wotlk/violethold/TODO create mode 100644 src/strategy/dungeons/wotlk/violethold/VioletHoldActionContext.h create mode 100644 src/strategy/dungeons/wotlk/violethold/VioletHoldActions.cpp create mode 100644 src/strategy/dungeons/wotlk/violethold/VioletHoldActions.h create mode 100644 src/strategy/dungeons/wotlk/violethold/VioletHoldMultipliers.cpp create mode 100644 src/strategy/dungeons/wotlk/violethold/VioletHoldMultipliers.h create mode 100644 src/strategy/dungeons/wotlk/violethold/VioletHoldStrategy.cpp create mode 100644 src/strategy/dungeons/wotlk/violethold/VioletHoldStrategy.h create mode 100644 src/strategy/dungeons/wotlk/violethold/VioletHoldTriggerContext.h create mode 100644 src/strategy/dungeons/wotlk/violethold/VioletHoldTriggers.cpp create mode 100644 src/strategy/dungeons/wotlk/violethold/VioletHoldTriggers.h diff --git a/src/strategy/AiObjectContext.cpp b/src/strategy/AiObjectContext.cpp index 2c81cefe..d8b603ff 100644 --- a/src/strategy/AiObjectContext.cpp +++ b/src/strategy/AiObjectContext.cpp @@ -55,6 +55,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI) actionContexts.Add(new WotlkDungeonANActionContext()); actionContexts.Add(new WotlkDungeonOKActionContext()); actionContexts.Add(new WotlkDungeonDTKActionContext()); + actionContexts.Add(new WotlkDungeonVHActionContext()); triggerContexts.Add(new TriggerContext()); triggerContexts.Add(new ChatTriggerContext()); @@ -70,6 +71,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI) triggerContexts.Add(new WotlkDungeonANTriggerContext()); triggerContexts.Add(new WotlkDungeonOKTriggerContext()); triggerContexts.Add(new WotlkDungeonDTKTriggerContext()); + triggerContexts.Add(new WotlkDungeonVHTriggerContext()); valueContexts.Add(new ValueContext()); diff --git a/src/strategy/dungeons/DungeonStrategyContext.h b/src/strategy/dungeons/DungeonStrategyContext.h index faae1372..8062e09b 100644 --- a/src/strategy/dungeons/DungeonStrategyContext.h +++ b/src/strategy/dungeons/DungeonStrategyContext.h @@ -7,12 +7,11 @@ #include "wotlk/azjolnerub/AzjolNerubStrategy.h" #include "wotlk/oldkingdom/OldKingdomStrategy.h" #include "wotlk/draktharonkeep/DrakTharonKeepStrategy.h" +#include "wotlk/violethold/VioletHoldStrategy.h" /* Full list/TODO: -The Violet Hold - VH -Erekem, Moragg, Ichoron, Xevozz, Lavanthor, Zuramat the Obliterator, Cyanigosa Gundrak - GD Slad'ran, Drakkari Colossus, Moorabi, Gal'darah, Eck the Ferocious (Heroic only) Halls of Stone - HoS @@ -76,8 +75,8 @@ class DungeonStrategyContext : public NamedObjectContext static Strategy* wotlk_an(PlayerbotAI* botAI) { return new WotlkDungeonANStrategy(botAI); } static Strategy* wotlk_ok(PlayerbotAI* botAI) { return new WotlkDungeonOKStrategy(botAI); } static Strategy* wotlk_dtk(PlayerbotAI* botAI) { return new WotlkDungeonDTKStrategy(botAI); } - - static Strategy* wotlk_vh(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } + static Strategy* wotlk_vh(PlayerbotAI* botAI) { return new WotlkDungeonVHStrategy(botAI); } + static Strategy* wotlk_gd(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } static Strategy* wotlk_hos(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } static Strategy* wotlk_hol(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } diff --git a/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h b/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h index eff45c50..060a4e5c 100644 --- a/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h +++ b/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h @@ -6,7 +6,7 @@ #include "azjolnerub/AzjolNerubActionContext.h" #include "oldkingdom/OldKingdomActionContext.h" #include "draktharonkeep/DrakTharonKeepActionContext.h" -// #include "violethold/VioletHoldActionContext.h" +#include "violethold/VioletHoldActionContext.h" // #include "gundrak/GundrakActionContext.h" // #include "hallsofstone/HallsOfStoneActionContext.h" // #include "hallsoflightning/HallsOfLightningActionContext.h" diff --git a/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h b/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h index 15f98758..11097b06 100644 --- a/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h +++ b/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h @@ -6,7 +6,7 @@ #include "azjolnerub/AzjolNerubTriggerContext.h" #include "oldkingdom/OldKingdomTriggerContext.h" #include "draktharonkeep/DrakTharonKeepTriggerContext.h" -// #include "violethold/VioletHoldTriggerContext.h" +#include "violethold/VioletHoldTriggerContext.h" // #include "gundrak/GundrakTriggerContext.h" // #include "hallsofstone/HallsOfStoneTriggerContext.h" // #include "hallsoflightning/HallsOfLightningTriggerContext.h" diff --git a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubMultipliers.cpp b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubMultipliers.cpp index e8cece04..6c287591 100644 --- a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubMultipliers.cpp +++ b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubMultipliers.cpp @@ -39,7 +39,9 @@ float KrikthirMultiplier::GetValue(Action* action) { // Do not target swap // TODO: Need to suppress AoE actions but unsure how to identify them - if (dynamic_cast(action)) + // TODO: TEST AOE Avoid + if (dynamic_cast(action) + || dynamic_cast(action)) { return 0.0f; } diff --git a/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepActions.cpp b/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepActions.cpp index 7d71d26a..e8863a12 100644 --- a/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepActions.cpp +++ b/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepActions.cpp @@ -66,6 +66,8 @@ bool NovosTargetPriorityAction::Execute(Event event) // Designate a dps char to handle the stairs adds. // This is probably better as a melee, so just pick the first // melee dps in the party. If none exist, pick the first ranged. + + // TODO: Switch to botAI->Index instead, cleaner Player* stairsDps = nullptr; GuidVector members = AI_VALUE(GuidVector, "group members"); for (auto& member : members) diff --git a/src/strategy/dungeons/wotlk/nexus/NexusActionContext.h b/src/strategy/dungeons/wotlk/nexus/NexusActionContext.h index 61dd91d8..c0242262 100644 --- a/src/strategy/dungeons/wotlk/nexus/NexusActionContext.h +++ b/src/strategy/dungeons/wotlk/nexus/NexusActionContext.h @@ -15,7 +15,6 @@ class WotlkDungeonNexActionContext : public NamedObjectContext creators["chaotic rift target"] = &WotlkDungeonNexActionContext::chaotic_rift_target; creators["dodge spikes"] = &WotlkDungeonNexActionContext::dodge_spikes; creators["intense cold jump"] = &WotlkDungeonNexActionContext::intense_cold_jump; - creators["rear flank position"] = &WotlkDungeonNexActionContext::rear_flank_position; } private: static Action* move_from_whirlwind(PlayerbotAI* ai) { return new MoveFromWhirlwindAction(ai); } @@ -24,7 +23,6 @@ class WotlkDungeonNexActionContext : public NamedObjectContext static Action* chaotic_rift_target(PlayerbotAI* ai) { return new ChaoticRiftTargetAction(ai); } static Action* dodge_spikes(PlayerbotAI* ai) { return new DodgeSpikesAction(ai); } static Action* intense_cold_jump(PlayerbotAI* ai) { return new IntenseColdJumpAction(ai); } - static Action* rear_flank_position(PlayerbotAI* ai) { return new RearFlankPositionAction(ai); } }; #endif diff --git a/src/strategy/dungeons/wotlk/nexus/NexusActions.cpp b/src/strategy/dungeons/wotlk/nexus/NexusActions.cpp index cbeaa6f4..1a918614 100644 --- a/src/strategy/dungeons/wotlk/nexus/NexusActions.cpp +++ b/src/strategy/dungeons/wotlk/nexus/NexusActions.cpp @@ -157,50 +157,3 @@ bool IntenseColdJumpAction::Execute(Event event) return JumpTo(bot->GetMap()->GetId(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ() + 0.01f); // bot->GetMotionMaster()->MoveFall(); } - -bool RearFlankPositionAction::isUseful() -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "keristrasza"); - if (!boss) { return false; } - - // Need to double the front angle check to account for mirrored angle. - // Total 180 degrees (whole front half) - bool inFront = boss->HasInArc(2.f * DRAGON_MELEE_MIN_ANGLE, bot); - // Rear check does not need to double this angle as the logic is inverted - // and we are subtracting from 2pi. - bool inBack = !boss->HasInArc((2.f * M_PI) - DRAGON_MELEE_MAX_ANGLE, bot); - - return inFront || inBack; -} -bool RearFlankPositionAction::Execute(Event event) -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "keristrasza"); - if (!boss) { return false; } - - // float angleToMove = minAngle + rand_norm() * (maxAngle - minAngle); - float angle = frand(DRAGON_MELEE_MIN_ANGLE, DRAGON_MELEE_MAX_ANGLE); - // Need to reduce this value very slightly, or the bots get the jitters - - // may be due to rounding errors. Need to bring them just inside their attack range. - // This boss has a big hitbox so we can reduce by 50% and it's still fine and looks better. - // TODO: Investigate using bot->GetObjectSize() for sizing - float distance = bot->GetMeleeRange(boss) * 0.5f; - // Alternatively, summing both unit's melee ranges seems to give a fairly natural range. - // Use whichever gives the best results.. - // float distanceOffset = bot->GetMeleeReach() + boss->GetMeleeReach(); - Position leftFlank = boss->GetPosition(); - Position rightFlank = boss->GetPosition(); - Position* destination = nullptr; - leftFlank.RelocatePolarOffset(angle, distance); - rightFlank.RelocatePolarOffset(-angle, distance); - - if (bot->GetExactDist2d(leftFlank) < bot->GetExactDist2d(rightFlank)) - { - destination = &leftFlank; - } - else - { - destination = &rightFlank; - } - - return MoveTo(bot->GetMapId(), destination->GetPositionX(), destination->GetPositionY(), destination->GetPositionZ()); -} diff --git a/src/strategy/dungeons/wotlk/nexus/NexusActions.h b/src/strategy/dungeons/wotlk/nexus/NexusActions.h index a7d31677..145efba7 100644 --- a/src/strategy/dungeons/wotlk/nexus/NexusActions.h +++ b/src/strategy/dungeons/wotlk/nexus/NexusActions.h @@ -68,12 +68,4 @@ public: bool Execute(Event event) override; }; -class RearFlankPositionAction : public MovementAction -{ -public: - RearFlankPositionAction(PlayerbotAI* ai) : MovementAction(ai, "rear flank position") {} - bool Execute(Event event) override; - bool isUseful() override; -}; - #endif diff --git a/src/strategy/dungeons/wotlk/nexus/NexusStrategy.cpp b/src/strategy/dungeons/wotlk/nexus/NexusStrategy.cpp index 44140b6e..b86c1aa4 100644 --- a/src/strategy/dungeons/wotlk/nexus/NexusStrategy.cpp +++ b/src/strategy/dungeons/wotlk/nexus/NexusStrategy.cpp @@ -38,9 +38,9 @@ void WotlkDungeonNexStrategy::InitTriggers(std::vector &triggers) // Keristrasza triggers.push_back(new TriggerNode("intense cold", NextAction::array(0, new NextAction("intense cold jump", ACTION_MOVE + 5), nullptr))); - // Flank dragon positioning for non-tank melee - triggers.push_back(new TriggerNode("dragon positioning", - NextAction::array(0, new NextAction("rear flank position", ACTION_MOVE + 4), nullptr))); + // Flank dragon positioning + triggers.push_back(new TriggerNode("keristrasza positioning", + NextAction::array(0, new NextAction("rear flank", ACTION_MOVE + 4), nullptr))); // TODO: Add frost resist aura for paladins? } diff --git a/src/strategy/dungeons/wotlk/nexus/NexusTriggerContext.h b/src/strategy/dungeons/wotlk/nexus/NexusTriggerContext.h index 72815f91..ca696ece 100644 --- a/src/strategy/dungeons/wotlk/nexus/NexusTriggerContext.h +++ b/src/strategy/dungeons/wotlk/nexus/NexusTriggerContext.h @@ -17,7 +17,7 @@ class WotlkDungeonNexTriggerContext : public NamedObjectContext creators["ormorok spikes"] = &WotlkDungeonNexTriggerContext::ormorok_spikes; creators["ormorok stack"] = &WotlkDungeonNexTriggerContext::ormorok_stack; creators["intense cold"] = &WotlkDungeonNexTriggerContext::intense_cold; - creators["dragon positioning"] = &WotlkDungeonNexTriggerContext::dragon_positioning; + creators["keristrasza positioning"] = &WotlkDungeonNexTriggerContext::keristrasza_positioning; } private: static Trigger* faction_commander_whirlwind(PlayerbotAI* ai) { return new FactionCommanderWhirlwindTrigger(ai); } @@ -27,7 +27,7 @@ class WotlkDungeonNexTriggerContext : public NamedObjectContext static Trigger* ormorok_spikes(PlayerbotAI* ai) { return new OrmorokSpikesTrigger(ai); } static Trigger* ormorok_stack(PlayerbotAI* ai) { return new OrmorokStackTrigger(ai); } static Trigger* intense_cold(PlayerbotAI* ai) { return new IntenseColdTrigger(ai); } - static Trigger* dragon_positioning(PlayerbotAI* ai) { return new DragonPositioningTrigger(ai); } + static Trigger* keristrasza_positioning(PlayerbotAI* ai) { return new KeristraszaPositioningTrigger(ai); } }; #endif diff --git a/src/strategy/dungeons/wotlk/nexus/NexusTriggers.cpp b/src/strategy/dungeons/wotlk/nexus/NexusTriggers.cpp index 22f1b29d..a39e497d 100644 --- a/src/strategy/dungeons/wotlk/nexus/NexusTriggers.cpp +++ b/src/strategy/dungeons/wotlk/nexus/NexusTriggers.cpp @@ -98,8 +98,10 @@ bool IntenseColdTrigger::IsActive() return boss && botAI->GetAura("intense cold", bot, false, false, stackThreshold); } -bool DragonPositioningTrigger::IsActive() +bool KeristraszaPositioningTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "keristrasza"); - return boss && botAI->IsMelee(bot) && !botAI->IsTank(bot); + // Include healers here for now, otherwise they stand in things + return boss && !botAI->IsTank(bot) && !botAI->IsRangedDps(bot); + // return boss && botAI->IsMelee(bot) && !botAI->IsTank(bot); } diff --git a/src/strategy/dungeons/wotlk/nexus/NexusTriggers.h b/src/strategy/dungeons/wotlk/nexus/NexusTriggers.h index c9f14038..ac5d88a4 100644 --- a/src/strategy/dungeons/wotlk/nexus/NexusTriggers.h +++ b/src/strategy/dungeons/wotlk/nexus/NexusTriggers.h @@ -79,10 +79,10 @@ public: bool IsActive() override; }; -class DragonPositioningTrigger : public Trigger +class KeristraszaPositioningTrigger : public Trigger { public: - DragonPositioningTrigger(PlayerbotAI* ai) : Trigger(ai, "dragon positioning") {} + KeristraszaPositioningTrigger(PlayerbotAI* ai) : Trigger(ai, "keristrasza positioning") {} bool IsActive() override; }; diff --git a/src/strategy/dungeons/wotlk/violethold/TODO b/src/strategy/dungeons/wotlk/violethold/TODO deleted file mode 100644 index e69de29b..00000000 diff --git a/src/strategy/dungeons/wotlk/violethold/VioletHoldActionContext.h b/src/strategy/dungeons/wotlk/violethold/VioletHoldActionContext.h new file mode 100644 index 00000000..902332f1 --- /dev/null +++ b/src/strategy/dungeons/wotlk/violethold/VioletHoldActionContext.h @@ -0,0 +1,24 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONVHACTIONCONTEXT_H +#define _PLAYERBOT_WOTLKDUNGEONVHACTIONCONTEXT_H + +#include "Action.h" +#include "NamedObjectContext.h" +#include "VioletHoldActions.h" + +class WotlkDungeonVHActionContext : public NamedObjectContext +{ + public: + WotlkDungeonVHActionContext() { + creators["attack erekem"] = &WotlkDungeonVHActionContext::attack_erekem; + creators["attack ichor globule"] = &WotlkDungeonVHActionContext::attack_ichor_globule; + creators["attack void sentry"] = &WotlkDungeonVHActionContext::attack_void_sentry; + creators["stop attack"] = &WotlkDungeonVHActionContext::stop_attack; + } + private: + static Action* attack_erekem(PlayerbotAI* ai) { return new AttackErekemAction(ai); } + static Action* attack_ichor_globule(PlayerbotAI* ai) { return new AttackIchorGlobuleAction(ai); } + static Action* attack_void_sentry(PlayerbotAI* ai) { return new AttackVoidSentryAction(ai); } + static Action* stop_attack(PlayerbotAI* ai) { return new StopAttackAction(ai); } +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/violethold/VioletHoldActions.cpp b/src/strategy/dungeons/wotlk/violethold/VioletHoldActions.cpp new file mode 100644 index 00000000..a108b323 --- /dev/null +++ b/src/strategy/dungeons/wotlk/violethold/VioletHoldActions.cpp @@ -0,0 +1,99 @@ +#include "Playerbots.h" +#include "VioletHoldActions.h" +#include "VioletHoldStrategy.h" + + +bool AttackErekemAction::Execute(Event event) +{ + // Focus boss first, adds after + Unit* boss = AI_VALUE2(Unit*, "find target", "erekem"); + if (AI_VALUE(Unit*, "current target") != boss) + { + return Attack(boss); + } + return false; +} + +bool AttackIchorGlobuleAction::Execute(Event event) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "ichoron"); + if (!boss) { return false; } + + // Tank prioritise boss if it's up + if (botAI->IsTank(bot) && !boss->HasAura(SPELL_DRAINED)) + { + if (AI_VALUE(Unit*, "current target") != boss) + { + return Attack(boss); + } + return false; + } + + // Target is not findable from threat table using AI_VALUE2(), + // therefore need to search manually for the unit name + GuidVector targets = AI_VALUE(GuidVector, "possible targets"); + + for (auto i = targets.begin(); i != targets.end(); ++i) + { + Unit* unit = botAI->GetUnit(*i); + if (unit && unit->GetEntry() == NPC_ICHOR_GLOBULE) + { + Unit* currentTarget = AI_VALUE(Unit*, "current target"); + // Check IDs here, NOT Unit* pointers: + // Don't keep swapping between sentries. + // If we're already attacking one, don't retarget another + if (currentTarget && currentTarget->GetEntry() == NPC_ICHOR_GLOBULE) + { + return false; + } + return Attack(unit); + } + } + // No ichor globules left alive, fall back to targeting boss + if (AI_VALUE(Unit*, "current target") != boss) + { + return Attack(boss); + } + + return false; +} + +bool AttackVoidSentryAction::Execute(Event event) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "zuramat the obliterator"); + if (!boss) { return false; } + + // Target is not findable from threat table using AI_VALUE2(), + // therefore need to search manually for the unit name + // GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); + GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); + + for (auto i = targets.begin(); i != targets.end(); ++i) + { + Unit* unit = botAI->GetUnit(*i); + if (unit && unit->GetEntry() == NPC_VOID_SENTRY) + { + Unit* currentTarget = AI_VALUE(Unit*, "current target"); + // Check IDs here, NOT Unit* pointers: + // Don't keep swapping between sentries. + // If we're already attacking one, don't retarget another + if (currentTarget && currentTarget->GetEntry() == NPC_VOID_SENTRY) + { + return false; + } + return Attack(unit); + } + } + // No void sentries left alive, fall back to targeting boss + if (AI_VALUE(Unit*, "current target") != boss) + { + return Attack(boss); + } + + return false; +} + +bool StopAttackAction::Execute(Event event) +{ + return bot->AttackStop(); +} diff --git a/src/strategy/dungeons/wotlk/violethold/VioletHoldActions.h b/src/strategy/dungeons/wotlk/violethold/VioletHoldActions.h new file mode 100644 index 00000000..2d2dada0 --- /dev/null +++ b/src/strategy/dungeons/wotlk/violethold/VioletHoldActions.h @@ -0,0 +1,48 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONVHACTIONS_H +#define _PLAYERBOT_WOTLKDUNGEONVHACTIONS_H + +#include "Action.h" +#include "AttackAction.h" +#include "GenericSpellActions.h" +#include "PlayerbotAI.h" +#include "Playerbots.h" +#include "VioletHoldTriggers.h" + +// const Position NOVOS_PARTY_POSITION = Position(-378.852f, -760.349f, 28.587f); + +class AttackErekemAction : public AttackAction +{ +public: + AttackErekemAction(PlayerbotAI* ai) : AttackAction(ai, "attack erekem") {} + bool Execute(Event event) override; +}; + +class AttackIchoronElementalsAction : public AttackAction +{ +public: + AttackIchoronElementalsAction(PlayerbotAI* ai) : AttackAction(ai, "attack ichoron elementals") {} + bool Execute(Event event) override; +}; + +class AttackIchorGlobuleAction : public AttackAction +{ +public: + AttackIchorGlobuleAction(PlayerbotAI* ai) : AttackAction(ai, "attack ichor globule") {} + bool Execute(Event event) override; +}; + +class AttackVoidSentryAction : public AttackAction +{ +public: + AttackVoidSentryAction(PlayerbotAI* ai) : AttackAction(ai, "attack void sentry") {} + bool Execute(Event event) override; +}; + +class StopAttackAction : public Action +{ +public: + StopAttackAction(PlayerbotAI* ai) : Action(ai, "stop attack") {} + bool Execute(Event event) override; +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/violethold/VioletHoldMultipliers.cpp b/src/strategy/dungeons/wotlk/violethold/VioletHoldMultipliers.cpp new file mode 100644 index 00000000..ff6cc6de --- /dev/null +++ b/src/strategy/dungeons/wotlk/violethold/VioletHoldMultipliers.cpp @@ -0,0 +1,57 @@ +#include "VioletHoldMultipliers.h" +#include "VioletHoldActions.h" +#include "GenericSpellActions.h" +#include "ChooseTargetActions.h" +#include "MovementActions.h" +#include "VioletHoldTriggers.h" +#include "Action.h" + +float ErekemMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "erekem"); + if (!boss || !botAI->IsDps(bot)) { return 1.0f; } + + if (dynamic_cast(action)) + { + return 0.0f; + } + if (action->getThreatType() == Action::ActionThreatType::Aoe) + { + return 0.0f; + } + return 1.0f; +} + +float IchoronMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "ichoron"); + if (!boss) { return 1.0f; } + + if (dynamic_cast(action) + || dynamic_cast(action) + || dynamic_cast(action)) + { + return 0.0f; + } + return 1.0f; +} + +float ZuramatMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "zuramat the obliterator"); + if (!boss) { return 1.0f; } + + if (bot->HasAura(SPELL_VOID_SHIFTED)) + { + if (dynamic_cast(action) || dynamic_cast(action)) + { + return 0.0f; + } + } + + if (boss->HasAura(SPELL_SHROUD_OF_DARKNESS) && dynamic_cast(action)) + { + return 0.0f; + } + return 1.0f; +} diff --git a/src/strategy/dungeons/wotlk/violethold/VioletHoldMultipliers.h b/src/strategy/dungeons/wotlk/violethold/VioletHoldMultipliers.h new file mode 100644 index 00000000..67952a16 --- /dev/null +++ b/src/strategy/dungeons/wotlk/violethold/VioletHoldMultipliers.h @@ -0,0 +1,33 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONVHMULTIPLIERS_H +#define _PLAYERBOT_WOTLKDUNGEONVHMULTIPLIERS_H + +#include "Multiplier.h" + +class ErekemMultiplier : public Multiplier +{ + public: + ErekemMultiplier(PlayerbotAI* ai) : Multiplier(ai, "erekem") {} + + public: + virtual float GetValue(Action* action); +}; + +class IchoronMultiplier : public Multiplier +{ + public: + IchoronMultiplier(PlayerbotAI* ai) : Multiplier(ai, "ichoron") {} + + public: + virtual float GetValue(Action* action); +}; + +class ZuramatMultiplier : public Multiplier +{ + public: + ZuramatMultiplier(PlayerbotAI* ai) : Multiplier(ai, "zuramat the obliterator") {} + + public: + virtual float GetValue(Action* action); +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/violethold/VioletHoldStrategy.cpp b/src/strategy/dungeons/wotlk/violethold/VioletHoldStrategy.cpp new file mode 100644 index 00000000..508f6f2f --- /dev/null +++ b/src/strategy/dungeons/wotlk/violethold/VioletHoldStrategy.cpp @@ -0,0 +1,41 @@ +#include "VioletHoldStrategy.h" +#include "VioletHoldMultipliers.h" + + +void WotlkDungeonVHStrategy::InitTriggers(std::vector &triggers) +{ + // Erekem + // This boss has many purgable buffs, purging/dispels could be merged into generic strats though + triggers.push_back(new TriggerNode("erekem target", + NextAction::array(0, new NextAction("attack erekem", ACTION_RAID + 1), nullptr))); + + // Moragg + // TODO: This guy has Optic Link which may require stacking, add if needed + + // Ichoron + triggers.push_back(new TriggerNode("ichoron target", + NextAction::array(0, new NextAction("attack ichor globule", ACTION_RAID + 1), nullptr))); + + // Xevozz + // TODO: Revisit in heroics, waypoints back and forth on stairs. Need to test with double beacon spawn + + // Lavanthor + // Tank & spank + + // Zuramat the Obliterator + triggers.push_back(new TriggerNode("shroud of darkness", + NextAction::array(0, new NextAction("stop attack", ACTION_HIGH + 5), nullptr))); + triggers.push_back(new TriggerNode("void shift", + NextAction::array(0, new NextAction("attack void sentry", ACTION_RAID + 1), nullptr))); + + // Cyanigosa + triggers.push_back(new TriggerNode("cyanigosa positioning", + NextAction::array(0, new NextAction("rear flank", ACTION_MOVE + 5), nullptr))); +} + +void WotlkDungeonVHStrategy::InitMultipliers(std::vector &multipliers) +{ + multipliers.push_back(new ErekemMultiplier(botAI)); + multipliers.push_back(new IchoronMultiplier(botAI)); + multipliers.push_back(new ZuramatMultiplier(botAI)); +} diff --git a/src/strategy/dungeons/wotlk/violethold/VioletHoldStrategy.h b/src/strategy/dungeons/wotlk/violethold/VioletHoldStrategy.h new file mode 100644 index 00000000..16708871 --- /dev/null +++ b/src/strategy/dungeons/wotlk/violethold/VioletHoldStrategy.h @@ -0,0 +1,18 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONVHSTRATEGY_H +#define _PLAYERBOT_WOTLKDUNGEONVHSTRATEGY_H + +#include "Multiplier.h" +#include "AiObjectContext.h" +#include "Strategy.h" + + +class WotlkDungeonVHStrategy : public Strategy +{ +public: + WotlkDungeonVHStrategy(PlayerbotAI* ai) : Strategy(ai) {} + virtual std::string const getName() override { return "violet hold"; } + virtual void InitTriggers(std::vector &triggers) override; + virtual void InitMultipliers(std::vector &multipliers) override; +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/violethold/VioletHoldTriggerContext.h b/src/strategy/dungeons/wotlk/violethold/VioletHoldTriggerContext.h new file mode 100644 index 00000000..48a722f4 --- /dev/null +++ b/src/strategy/dungeons/wotlk/violethold/VioletHoldTriggerContext.h @@ -0,0 +1,27 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONVHTRIGGERCONTEXT_H +#define _PLAYERBOT_WOTLKDUNGEONVHTRIGGERCONTEXT_H + +#include "NamedObjectContext.h" +#include "AiObjectContext.h" +#include "VioletHoldTriggers.h" + +class WotlkDungeonVHTriggerContext : public NamedObjectContext +{ + public: + WotlkDungeonVHTriggerContext() + { + creators["erekem target"] = &WotlkDungeonVHTriggerContext::erekem_target; + creators["ichoron target"] = &WotlkDungeonVHTriggerContext::ichoron_target; + creators["void shift"] = &WotlkDungeonVHTriggerContext::void_shift; + creators["shroud of darkness"] = &WotlkDungeonVHTriggerContext::shroud_of_darkness; + creators["cyanigosa positioning"] = &WotlkDungeonVHTriggerContext::cyanigosa_positioning; + } + private: + static Trigger* erekem_target(PlayerbotAI* ai) { return new ErekemTargetTrigger(ai); } + static Trigger* ichoron_target(PlayerbotAI* ai) { return new IchoronTargetTrigger(ai); } + static Trigger* void_shift(PlayerbotAI* ai) { return new VoidShiftTrigger(ai); } + static Trigger* shroud_of_darkness(PlayerbotAI* ai) { return new ShroudOfDarknessTrigger(ai); } + static Trigger* cyanigosa_positioning(PlayerbotAI* ai) { return new CyanigosaPositioningTrigger(ai); } +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/violethold/VioletHoldTriggers.cpp b/src/strategy/dungeons/wotlk/violethold/VioletHoldTriggers.cpp new file mode 100644 index 00000000..1a6197ab --- /dev/null +++ b/src/strategy/dungeons/wotlk/violethold/VioletHoldTriggers.cpp @@ -0,0 +1,76 @@ +#include "Playerbots.h" +#include "VioletHoldTriggers.h" +#include "AiObject.h" +#include "AiObjectContext.h" + + +bool ErekemTargetTrigger::IsActive() +{ + return AI_VALUE2(Unit*, "find target", "erekem") && botAI->IsDps(bot); +} + +bool IchoronTargetTrigger::IsActive() +{ + return AI_VALUE2(Unit*, "find target", "ichoron") && !botAI->IsHeal(bot); +} + +bool VoidShiftTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "zuramat the obliterator"); + return boss && bot->HasAura(SPELL_VOID_SHIFTED) && !botAI->IsHeal(bot); +} + +bool ShroudOfDarknessTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "zuramat the obliterator"); + return boss && boss->HasAura(SPELL_SHROUD_OF_DARKNESS); +} + +bool CyanigosaPositioningTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "cyanigosa"); + // Include healers here for now, otherwise they stand in things + return boss && !botAI->IsTank(bot) && !botAI->IsRangedDps(bot); + // return boss && botAI->IsMelee(bot) && !botAI->IsTank(bot); +} + +// bool CorpseExplodeTrigger::IsActive() +// { +// Unit* boss = AI_VALUE2(Unit*, "find target", "trollgore"); +// if (!boss) { return false; } + +// float distance = 6.0f; // 5 unit radius, 1 unit added as buffer +// GuidVector corpses = AI_VALUE(GuidVector, "nearest corpses"); +// for (auto i = corpses.begin(); i != corpses.end(); ++i) +// { +// Unit* unit = botAI->GetUnit(*i); +// if (unit && unit->GetEntry() == NPC_DRAKKARI_INVADER) +// { +// if (bot->GetExactDist2d(unit) < distance) +// { +// return true; +// } +// } +// } +// return false; +// } + +// bool CrystalHandlerTrigger::IsActive() +// { +// Unit* boss = AI_VALUE2(Unit*, "find target", "novos the summoner"); +// if (!boss) { return false; } + +// // Target is not findable from threat table using AI_VALUE2(), +// // therefore need to search manually for the unit name +// GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); + +// for (auto i = targets.begin(); i != targets.end(); ++i) +// { +// Unit* unit = botAI->GetUnit(*i); +// if (unit && unit->GetEntry() == NPC_CRYSTAL_HANDLER) +// { +// return true; +// } +// } +// return false; +// } diff --git a/src/strategy/dungeons/wotlk/violethold/VioletHoldTriggers.h b/src/strategy/dungeons/wotlk/violethold/VioletHoldTriggers.h new file mode 100644 index 00000000..1e4f4af6 --- /dev/null +++ b/src/strategy/dungeons/wotlk/violethold/VioletHoldTriggers.h @@ -0,0 +1,59 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONVHTRIGGERS_H +#define _PLAYERBOT_WOTLKDUNGEONVHTRIGGERS_H + +#include "Trigger.h" +#include "PlayerbotAIConfig.h" +#include "GenericTriggers.h" +#include "DungeonStrategyUtils.h" + +enum VioletHoldIDs +{ + // Ichoron + SPELL_DRAINED = 59820, + NPC_ICHOR_GLOBULE = 29321, + + // Ichoron + SPELL_VOID_SHIFTED = 54343, + SPELL_SHROUD_OF_DARKNESS_N = 54524, + SPELL_SHROUD_OF_DARKNESS_H = 59745, + NPC_VOID_SENTRY = 29364, +}; + +#define SPELL_SHROUD_OF_DARKNESS DUNGEON_MODE(bot, SPELL_SHROUD_OF_DARKNESS_N, SPELL_SHROUD_OF_DARKNESS_H) + +class ErekemTargetTrigger : public Trigger +{ +public: + ErekemTargetTrigger(PlayerbotAI* ai) : Trigger(ai, "erekem target") {} + bool IsActive() override; +}; + +class IchoronTargetTrigger : public Trigger +{ +public: + IchoronTargetTrigger(PlayerbotAI* ai) : Trigger(ai, "ichoron target") {} + bool IsActive() override; +}; + +class VoidShiftTrigger : public Trigger +{ +public: + VoidShiftTrigger(PlayerbotAI* ai) : Trigger(ai, "void shift") {} + bool IsActive() override; +}; + +class ShroudOfDarknessTrigger : public Trigger +{ +public: + ShroudOfDarknessTrigger(PlayerbotAI* ai) : Trigger(ai, "shroud of darkness") {} + bool IsActive() override; +}; + +class CyanigosaPositioningTrigger : public Trigger +{ +public: + CyanigosaPositioningTrigger(PlayerbotAI* ai) : Trigger(ai, "cyanigosa positioning") {} + bool IsActive() override; +}; + +#endif From 5e4764ee9ce091d7bd0e129d4628b18375ce53e2 Mon Sep 17 00:00:00 2001 From: Bobblybook Date: Sat, 12 Oct 2024 23:57:42 +1100 Subject: [PATCH 39/40] Comment cleanup --- .../wotlk/violethold/VioletHoldStrategy.cpp | 2 +- .../wotlk/violethold/VioletHoldTriggers.cpp | 41 ------------------- 2 files changed, 1 insertion(+), 42 deletions(-) diff --git a/src/strategy/dungeons/wotlk/violethold/VioletHoldStrategy.cpp b/src/strategy/dungeons/wotlk/violethold/VioletHoldStrategy.cpp index 508f6f2f..bcc27506 100644 --- a/src/strategy/dungeons/wotlk/violethold/VioletHoldStrategy.cpp +++ b/src/strategy/dungeons/wotlk/violethold/VioletHoldStrategy.cpp @@ -10,7 +10,7 @@ void WotlkDungeonVHStrategy::InitTriggers(std::vector &triggers) NextAction::array(0, new NextAction("attack erekem", ACTION_RAID + 1), nullptr))); // Moragg - // TODO: This guy has Optic Link which may require stacking, add if needed + // TODO: This guy has Optic Link which may require moving, add if needed // Ichoron triggers.push_back(new TriggerNode("ichoron target", diff --git a/src/strategy/dungeons/wotlk/violethold/VioletHoldTriggers.cpp b/src/strategy/dungeons/wotlk/violethold/VioletHoldTriggers.cpp index 1a6197ab..cf9e9ab1 100644 --- a/src/strategy/dungeons/wotlk/violethold/VioletHoldTriggers.cpp +++ b/src/strategy/dungeons/wotlk/violethold/VioletHoldTriggers.cpp @@ -33,44 +33,3 @@ bool CyanigosaPositioningTrigger::IsActive() return boss && !botAI->IsTank(bot) && !botAI->IsRangedDps(bot); // return boss && botAI->IsMelee(bot) && !botAI->IsTank(bot); } - -// bool CorpseExplodeTrigger::IsActive() -// { -// Unit* boss = AI_VALUE2(Unit*, "find target", "trollgore"); -// if (!boss) { return false; } - -// float distance = 6.0f; // 5 unit radius, 1 unit added as buffer -// GuidVector corpses = AI_VALUE(GuidVector, "nearest corpses"); -// for (auto i = corpses.begin(); i != corpses.end(); ++i) -// { -// Unit* unit = botAI->GetUnit(*i); -// if (unit && unit->GetEntry() == NPC_DRAKKARI_INVADER) -// { -// if (bot->GetExactDist2d(unit) < distance) -// { -// return true; -// } -// } -// } -// return false; -// } - -// bool CrystalHandlerTrigger::IsActive() -// { -// Unit* boss = AI_VALUE2(Unit*, "find target", "novos the summoner"); -// if (!boss) { return false; } - -// // Target is not findable from threat table using AI_VALUE2(), -// // therefore need to search manually for the unit name -// GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); - -// for (auto i = targets.begin(); i != targets.end(); ++i) -// { -// Unit* unit = botAI->GetUnit(*i); -// if (unit && unit->GetEntry() == NPC_CRYSTAL_HANDLER) -// { -// return true; -// } -// } -// return false; -// } From 23237d27e40827660fe8d2fa8c866b1ca1be05ee Mon Sep 17 00:00:00 2001 From: Bobblybook Date: Sat, 12 Oct 2024 23:58:49 +1100 Subject: [PATCH 40/40] Update VioletHoldTriggers.h --- src/strategy/dungeons/wotlk/violethold/VioletHoldTriggers.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/strategy/dungeons/wotlk/violethold/VioletHoldTriggers.h b/src/strategy/dungeons/wotlk/violethold/VioletHoldTriggers.h index 1e4f4af6..b5a306d4 100644 --- a/src/strategy/dungeons/wotlk/violethold/VioletHoldTriggers.h +++ b/src/strategy/dungeons/wotlk/violethold/VioletHoldTriggers.h @@ -12,7 +12,7 @@ enum VioletHoldIDs SPELL_DRAINED = 59820, NPC_ICHOR_GLOBULE = 29321, - // Ichoron + // Zuramat the Obliterator SPELL_VOID_SHIFTED = 54343, SPELL_SHROUD_OF_DARKNESS_N = 54524, SPELL_SHROUD_OF_DARKNESS_H = 59745,