From 70c9bdbd9d389023009decf558ab81d252bffd38 Mon Sep 17 00:00:00 2001 From: Bobblybook Date: Tue, 15 Oct 2024 22:40:47 +1100 Subject: [PATCH] Halls of Stone implementation --- src/strategy/AiObjectContext.cpp | 2 + .../dungeons/DungeonStrategyContext.h | 5 +- .../wotlk/WotlkDungeonActionContext.h | 2 +- .../wotlk/WotlkDungeonTriggerContext.h | 2 +- .../hallsofstone/HallsOfStoneActionContext.h | 20 +++++++ .../hallsofstone/HallsOfStoneActions.cpp | 52 +++++++++++++++++++ .../wotlk/hallsofstone/HallsOfStoneActions.h | 24 +++++++++ .../hallsofstone/HallsOfStoneMultipliers.cpp | 41 +++++++++++++++ .../hallsofstone/HallsOfStoneMultipliers.h | 24 +++++++++ .../hallsofstone/HallsOfStoneStrategy.cpp | 29 +++++++++++ .../wotlk/hallsofstone/HallsOfStoneStrategy.h | 18 +++++++ .../hallsofstone/HallsOfStoneTriggerContext.h | 21 ++++++++ .../hallsofstone/HallsOfStoneTriggers.cpp | 22 ++++++++ .../wotlk/hallsofstone/HallsOfStoneTriggers.h | 36 +++++++++++++ src/strategy/dungeons/wotlk/hallsofstone/TODO | 0 15 files changed, 293 insertions(+), 5 deletions(-) create mode 100644 src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneActionContext.h create mode 100644 src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneActions.cpp create mode 100644 src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneActions.h create mode 100644 src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneMultipliers.cpp create mode 100644 src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneMultipliers.h create mode 100644 src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneStrategy.cpp create mode 100644 src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneStrategy.h create mode 100644 src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneTriggerContext.h create mode 100644 src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneTriggers.cpp create mode 100644 src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneTriggers.h delete mode 100644 src/strategy/dungeons/wotlk/hallsofstone/TODO diff --git a/src/strategy/AiObjectContext.cpp b/src/strategy/AiObjectContext.cpp index ddd9ea9c..c874f577 100644 --- a/src/strategy/AiObjectContext.cpp +++ b/src/strategy/AiObjectContext.cpp @@ -57,6 +57,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI) actionContexts.Add(new WotlkDungeonDTKActionContext()); actionContexts.Add(new WotlkDungeonVHActionContext()); actionContexts.Add(new WotlkDungeonGDActionContext()); + actionContexts.Add(new WotlkDungeonHoSActionContext()); triggerContexts.Add(new TriggerContext()); triggerContexts.Add(new ChatTriggerContext()); @@ -74,6 +75,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI) triggerContexts.Add(new WotlkDungeonDTKTriggerContext()); triggerContexts.Add(new WotlkDungeonVHTriggerContext()); triggerContexts.Add(new WotlkDungeonGDTriggerContext()); + triggerContexts.Add(new WotlkDungeonHoSTriggerContext()); valueContexts.Add(new ValueContext()); diff --git a/src/strategy/dungeons/DungeonStrategyContext.h b/src/strategy/dungeons/DungeonStrategyContext.h index 523a601e..fa0d31c2 100644 --- a/src/strategy/dungeons/DungeonStrategyContext.h +++ b/src/strategy/dungeons/DungeonStrategyContext.h @@ -9,12 +9,11 @@ #include "wotlk/draktharonkeep/DrakTharonKeepStrategy.h" #include "wotlk/violethold/VioletHoldStrategy.h" #include "wotlk/gundrak/GundrakStrategy.h" +#include "wotlk/hallsofstone/HallsOfStoneStrategy.h" /* Full list/TODO: -Halls of Stone - HoS -Maiden of Grief, Krystallus, Tribunal of Ages, Sjonnir The Ironshaper Halls of Lightning - HoL General Bjarngrim, Volkhan, Ionar, Loken The Oculus - Occ @@ -76,8 +75,8 @@ class DungeonStrategyContext : public NamedObjectContext static Strategy* wotlk_dtk(PlayerbotAI* botAI) { return new WotlkDungeonDTKStrategy(botAI); } static Strategy* wotlk_vh(PlayerbotAI* botAI) { return new WotlkDungeonVHStrategy(botAI); } static Strategy* wotlk_gd(PlayerbotAI* botAI) { return new WotlkDungeonGDStrategy(botAI); } + static Strategy* wotlk_hos(PlayerbotAI* botAI) { return new WotlkDungeonHoSStrategy(botAI); } - static Strategy* wotlk_hos(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } static Strategy* wotlk_hol(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } static Strategy* wotlk_occ(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } static Strategy* wotlk_up(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } diff --git a/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h b/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h index 91a87760..73fbecb4 100644 --- a/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h +++ b/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h @@ -8,7 +8,7 @@ #include "draktharonkeep/DrakTharonKeepActionContext.h" #include "violethold/VioletHoldActionContext.h" #include "gundrak/GundrakActionContext.h" -// #include "hallsofstone/HallsOfStoneActionContext.h" +#include "hallsofstone/HallsOfStoneActionContext.h" // #include "hallsoflightning/HallsOfLightningActionContext.h" // #include "oculus/OculusActionContext.h" // #include "utgardepinnacle/UtgardePinnacleActionContext.h" diff --git a/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h b/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h index 31524e69..7e966e2f 100644 --- a/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h +++ b/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h @@ -8,7 +8,7 @@ #include "draktharonkeep/DrakTharonKeepTriggerContext.h" #include "violethold/VioletHoldTriggerContext.h" #include "gundrak/GundrakTriggerContext.h" -// #include "hallsofstone/HallsOfStoneTriggerContext.h" +#include "hallsofstone/HallsOfStoneTriggerContext.h" // #include "hallsoflightning/HallsOfLightningTriggerContext.h" // #include "oculus/OculusTriggerContext.h" // #include "utgardepinnacle/UtgardePinnacleTriggerContext.h" diff --git a/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneActionContext.h b/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneActionContext.h new file mode 100644 index 00000000..40b469b4 --- /dev/null +++ b/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneActionContext.h @@ -0,0 +1,20 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONHOSACTIONCONTEXT_H +#define _PLAYERBOT_WOTLKDUNGEONHOSACTIONCONTEXT_H + +#include "Action.h" +#include "NamedObjectContext.h" +#include "HallsOfStoneActions.h" + +class WotlkDungeonHoSActionContext : public NamedObjectContext +{ + public: + WotlkDungeonHoSActionContext() { + creators["shatter spread"] = &WotlkDungeonHoSActionContext::shatter_spread; + creators["avoid lightning ring"] = &WotlkDungeonHoSActionContext::avoid_lightning_ring; + } + private: + static Action* shatter_spread(PlayerbotAI* ai) { return new ShatterSpreadAction(ai); } + static Action* avoid_lightning_ring(PlayerbotAI* ai) { return new AvoidLightningRingAction(ai); } +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneActions.cpp b/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneActions.cpp new file mode 100644 index 00000000..25f19341 --- /dev/null +++ b/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneActions.cpp @@ -0,0 +1,52 @@ +#include "Playerbots.h" +#include "HallsOfStoneActions.h" +#include "HallsOfStoneStrategy.h" + +bool ShatterSpreadAction::Execute(Event event) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "krystallus"); + float radius = 40.0f; + if (!boss) { return false; } + + Unit* closestMember = nullptr; + GuidVector members = AI_VALUE(GuidVector, "group members"); + for (auto& member : members) + { + if (bot->GetGUID() == member) + { + continue; + } + if (!closestMember || + bot->GetExactDist2d(botAI->GetUnit(member)) < bot->GetExactDist2d(closestMember)) + { + closestMember = botAI->GetUnit(member); + } + } + + if (closestMember && bot->GetExactDist2d(closestMember) < radius) + { + // Move in small increments so course can be corrected, otherwise two bots may + // run in the same direction and not adjust until they reach the destination + // return MoveAway(closestMember, radius - bot->GetExactDist2d(closestMember)); + return MoveAway(closestMember, 5.0f); + } + + return false; +} + +bool AvoidLightningRingAction::Execute(Event event) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "sjonnir the ironshaper"); + if (!boss) { return false; } + + float distance = bot->GetExactDist2d(boss->GetPosition()); + float radius = 10.0f; + float distanceExtra = 2.0f; + + if (distance < radius + distanceExtra) + { + return MoveAway(boss, radius + distanceExtra - distance); + } + + return false; +} diff --git a/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneActions.h b/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneActions.h new file mode 100644 index 00000000..ad7058af --- /dev/null +++ b/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneActions.h @@ -0,0 +1,24 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONHOSACTIONS_H +#define _PLAYERBOT_WOTLKDUNGEONHOSACTIONS_H + +#include "Action.h" +#include "AttackAction.h" +#include "PlayerbotAI.h" +#include "Playerbots.h" +#include "HallsOfStoneTriggers.h" + +class ShatterSpreadAction : public MovementAction +{ +public: + ShatterSpreadAction(PlayerbotAI* ai) : MovementAction(ai, "shatter spread") {} + bool Execute(Event event) override; +}; + +class AvoidLightningRingAction : public MovementAction +{ +public: + AvoidLightningRingAction(PlayerbotAI* ai) : MovementAction(ai, "avoid lightning ring") {} + bool Execute(Event event) override; +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneMultipliers.cpp b/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneMultipliers.cpp new file mode 100644 index 00000000..0b4a51ba --- /dev/null +++ b/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneMultipliers.cpp @@ -0,0 +1,41 @@ +#include "HallsOfStoneMultipliers.h" +#include "HallsOfStoneActions.h" +#include "GenericSpellActions.h" +#include "ChooseTargetActions.h" +#include "MovementActions.h" +#include "HallsOfStoneTriggers.h" +#include "Action.h" + +float KrystallusMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "krystallus"); + if (!boss) { return 1.0f; } + + // Check both of these... the spell is applied first, debuff later. + // Neither is active for the full duration so we need to trigger off both + if (bot->HasAura(SPELL_GROUND_SLAM) || bot->HasAura(DEBUFF_GROUND_SLAM)) + { + if (dynamic_cast(action) && !dynamic_cast(action)) + { + return 0.0f; + } + } + return 1.0f; +} + +float SjonnirMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "sjonnir the ironshaper"); + if (!boss) { return 1.0f; } + + if (boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_LIGHTNING_RING)) + { + // Problematic since there's a lot of movement on this boss, will prevent players from positioning + // well to deal with adds etc. during the channel period. Takes a bit of work to improve this though + if (dynamic_cast(action) && !dynamic_cast(action)) + { + return 0.0f; + } + } + return 1.0f; +} diff --git a/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneMultipliers.h b/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneMultipliers.h new file mode 100644 index 00000000..543d389a --- /dev/null +++ b/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneMultipliers.h @@ -0,0 +1,24 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONHOSMULTIPLIERS_H +#define _PLAYERBOT_WOTLKDUNGEONHOSMULTIPLIERS_H + +#include "Multiplier.h" + +class KrystallusMultiplier : public Multiplier +{ + public: + KrystallusMultiplier(PlayerbotAI* ai) : Multiplier(ai, "krystallus") {} + + public: + virtual float GetValue(Action* action); +}; + +class SjonnirMultiplier : public Multiplier +{ + public: + SjonnirMultiplier(PlayerbotAI* ai) : Multiplier(ai, "sjonnir the ironshaper") {} + + public: + virtual float GetValue(Action* action); +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneStrategy.cpp b/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneStrategy.cpp new file mode 100644 index 00000000..09e77dc3 --- /dev/null +++ b/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneStrategy.cpp @@ -0,0 +1,29 @@ +#include "HallsOfStoneStrategy.h" +#include "HallsOfStoneMultipliers.h" + + +void WotlkDungeonHoSStrategy::InitTriggers(std::vector &triggers) +{ + // Maiden of Grief + // TODO: Jump into damage during shock of sorrow? + + // Krystallus + // TODO: I think bots need to dismiss pets on this, or they nuke players they are standing close to + triggers.push_back(new TriggerNode("ground slam", + NextAction::array(0, new NextAction("shatter spread", ACTION_RAID + 5), nullptr))); + + // Tribunal of Ages + // Seems fine, maybe add focus targeting strat if needed on heroic. + // Main issue is dps will immediately rambo in and sometimes die before tank gets aggro, + // this is mostly an issue with the bot AI as they do it on every fight + + // Sjonnir The Ironshaper + // Possibly tank in place in the middle of the room, assign a dps to adds? + triggers.push_back(new TriggerNode("lightning ring", + NextAction::array(0, new NextAction("avoid lightning ring", ACTION_RAID + 5), nullptr))); +} + +void WotlkDungeonHoSStrategy::InitMultipliers(std::vector &multipliers) +{ + multipliers.push_back(new KrystallusMultiplier(botAI)); +} diff --git a/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneStrategy.h b/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneStrategy.h new file mode 100644 index 00000000..62084838 --- /dev/null +++ b/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneStrategy.h @@ -0,0 +1,18 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONHOSSTRATEGY_H +#define _PLAYERBOT_WOTLKDUNGEONHOSSTRATEGY_H + +#include "Multiplier.h" +#include "AiObjectContext.h" +#include "Strategy.h" + + +class WotlkDungeonHoSStrategy : public Strategy +{ +public: + WotlkDungeonHoSStrategy(PlayerbotAI* ai) : Strategy(ai) {} + virtual std::string const getName() override { return "halls of stone"; } + virtual void InitTriggers(std::vector &triggers) override; + virtual void InitMultipliers(std::vector &multipliers) override; +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneTriggerContext.h b/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneTriggerContext.h new file mode 100644 index 00000000..5548180d --- /dev/null +++ b/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneTriggerContext.h @@ -0,0 +1,21 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONHOSTRIGGERCONTEXT_H +#define _PLAYERBOT_WOTLKDUNGEONHOSTRIGGERCONTEXT_H + +#include "NamedObjectContext.h" +#include "AiObjectContext.h" +#include "HallsOfStoneTriggers.h" + +class WotlkDungeonHoSTriggerContext : public NamedObjectContext +{ + public: + WotlkDungeonHoSTriggerContext() + { + creators["ground slam"] = &WotlkDungeonHoSTriggerContext::ground_slam; + creators["lightning ring"] = &WotlkDungeonHoSTriggerContext::lightning_ring; + } + private: + static Trigger* ground_slam(PlayerbotAI* ai) { return new KrystallusGroundSlamTrigger(ai); } + static Trigger* lightning_ring(PlayerbotAI* ai) { return new SjonnirLightningRingTrigger(ai); } +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneTriggers.cpp b/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneTriggers.cpp new file mode 100644 index 00000000..854757bb --- /dev/null +++ b/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneTriggers.cpp @@ -0,0 +1,22 @@ +#include "Playerbots.h" +#include "HallsOfStoneTriggers.h" +#include "AiObject.h" +#include "AiObjectContext.h" + +bool KrystallusGroundSlamTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "krystallus"); + if (!boss) { return false; } + + // Check both of these... the spell is applied first, debuff later. + // Neither is active for the full duration so we need to trigger off both + return bot->HasAura(SPELL_GROUND_SLAM) || bot->HasAura(DEBUFF_GROUND_SLAM); +} + +bool SjonnirLightningRingTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "sjonnir the ironshaper"); + if (!boss) { return false; } + + return boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_LIGHTNING_RING); +} diff --git a/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneTriggers.h b/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneTriggers.h new file mode 100644 index 00000000..53a15b6f --- /dev/null +++ b/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneTriggers.h @@ -0,0 +1,36 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONHOSTRIGGERS_H +#define _PLAYERBOT_WOTLKDUNGEONHOSTRIGGERS_H + +#include "Trigger.h" +#include "PlayerbotAIConfig.h" +#include "GenericTriggers.h" +#include "DungeonStrategyUtils.h" + +enum HallsOfStoneIDs +{ + // Krystallus + SPELL_GROUND_SLAM = 50827, + DEBUFF_GROUND_SLAM = 50833, + + // Sjonnir The Ironshaper + SPELL_LIGHTNING_RING_N = 50840, + SPELL_LIGHTNING_RING_H = 59848, +}; + +#define SPELL_LIGHTNING_RING DUNGEON_MODE(bot, SPELL_LIGHTNING_RING_N, SPELL_LIGHTNING_RING_H) + +class KrystallusGroundSlamTrigger : public Trigger +{ +public: + KrystallusGroundSlamTrigger(PlayerbotAI* ai) : Trigger(ai, "krystallus ground slam") {} + bool IsActive() override; +}; + +class SjonnirLightningRingTrigger : public Trigger +{ +public: + SjonnirLightningRingTrigger(PlayerbotAI* ai) : Trigger(ai, "sjonnir lightning ring") {} + bool IsActive() override; +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/hallsofstone/TODO b/src/strategy/dungeons/wotlk/hallsofstone/TODO deleted file mode 100644 index e69de29b..00000000