From 1a47bf429b9067218d09a1d1fdd4792a3f94e8a5 Mon Sep 17 00:00:00 2001 From: Bobblybook Date: Fri, 11 Oct 2024 20:57:58 +1100 Subject: [PATCH 1/2] 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 2/2] 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); }