Old Kingdom implementation

Ahn'kahet: The Old Kingdom bot AI
This commit is contained in:
Bobblybook
2024-10-08 22:35:16 +11:00
parent 62a9a6040a
commit 16ea1777f4
15 changed files with 405 additions and 8 deletions

View File

@@ -53,6 +53,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
actionContexts.Add(new WotlkDungeonUKActionContext()); actionContexts.Add(new WotlkDungeonUKActionContext());
actionContexts.Add(new WotlkDungeonNexActionContext()); actionContexts.Add(new WotlkDungeonNexActionContext());
actionContexts.Add(new WotlkDungeonANActionContext()); actionContexts.Add(new WotlkDungeonANActionContext());
actionContexts.Add(new WotlkDungeonOKActionContext());
triggerContexts.Add(new TriggerContext()); triggerContexts.Add(new TriggerContext());
triggerContexts.Add(new ChatTriggerContext()); triggerContexts.Add(new ChatTriggerContext());
@@ -66,6 +67,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
triggerContexts.Add(new WotlkDungeonUKTriggerContext()); triggerContexts.Add(new WotlkDungeonUKTriggerContext());
triggerContexts.Add(new WotlkDungeonNexTriggerContext()); triggerContexts.Add(new WotlkDungeonNexTriggerContext());
triggerContexts.Add(new WotlkDungeonANTriggerContext()); triggerContexts.Add(new WotlkDungeonANTriggerContext());
triggerContexts.Add(new WotlkDungeonOKTriggerContext());
valueContexts.Add(new ValueContext()); valueContexts.Add(new ValueContext());

View File

@@ -5,13 +5,10 @@
#include "wotlk/utgardekeep/UtgardeKeepStrategy.h" #include "wotlk/utgardekeep/UtgardeKeepStrategy.h"
#include "wotlk/nexus/NexusStrategy.h" #include "wotlk/nexus/NexusStrategy.h"
#include "wotlk/azjolnerub/AzjolNerubStrategy.h" #include "wotlk/azjolnerub/AzjolNerubStrategy.h"
#include "wotlk/oldkingdom/OldKingdomStrategy.h"
/* /*
Full list/TODO: Full list/TODO:
Ahn'kahet: The Old Kingdom - OK
Elder Nadox, Prince Taldaram, Jedoga Shadowseeker, Herald Volazj, Amanitar (Heroic Only)
Drak'Tharon Keep - DTK Drak'Tharon Keep - DTK
Trollgore, Novos the Summoner, King Dred, The Prophet Tharon'ja Trollgore, Novos the Summoner, King Dred, The Prophet Tharon'ja
The Violet Hold - VH The Violet Hold - VH
@@ -77,8 +74,8 @@ class DungeonStrategyContext : public NamedObjectContext<Strategy>
static Strategy* wotlk_uk(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } static Strategy* wotlk_uk(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); }
static Strategy* wotlk_nex(PlayerbotAI* botAI) { return new WotlkDungeonNexStrategy(botAI); } static Strategy* wotlk_nex(PlayerbotAI* botAI) { return new WotlkDungeonNexStrategy(botAI); }
static Strategy* wotlk_an(PlayerbotAI* botAI) { return new WotlkDungeonANStrategy(botAI); } static Strategy* wotlk_an(PlayerbotAI* botAI) { return new WotlkDungeonANStrategy(botAI); }
static Strategy* wotlk_ok(PlayerbotAI* botAI) { return new WotlkDungeonOKStrategy(botAI); }
static Strategy* wotlk_ok(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); }
static Strategy* wotlk_dtk(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); } static Strategy* wotlk_vh(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); }
static Strategy* wotlk_gd(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } static Strategy* wotlk_gd(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); }

View File

@@ -4,7 +4,7 @@
#include "utgardekeep/UtgardeKeepActionContext.h" #include "utgardekeep/UtgardeKeepActionContext.h"
#include "nexus/NexusActionContext.h" #include "nexus/NexusActionContext.h"
#include "azjolnerub/AzjolNerubActionContext.h" #include "azjolnerub/AzjolNerubActionContext.h"
// #include "oldkingdom/OldKingdomActionContext.h" #include "oldkingdom/OldKingdomActionContext.h"
// #include "draktharonkeep/DraktharonKeepActionContext.h" // #include "draktharonkeep/DraktharonKeepActionContext.h"
// #include "violethold/VioletHoldActionContext.h" // #include "violethold/VioletHoldActionContext.h"
// #include "gundrak/GundrakActionContext.h" // #include "gundrak/GundrakActionContext.h"

View File

@@ -4,7 +4,7 @@
#include "utgardekeep/UtgardeKeepTriggerContext.h" #include "utgardekeep/UtgardeKeepTriggerContext.h"
#include "nexus/NexusTriggerContext.h" #include "nexus/NexusTriggerContext.h"
#include "azjolnerub/AzjolNerubTriggerContext.h" #include "azjolnerub/AzjolNerubTriggerContext.h"
// #include "oldkingdom/OldKingdomTriggerContext.h" #include "oldkingdom/OldKingdomTriggerContext.h"
// #include "draktharonkeep/DraktharonKeepTriggerContext.h" // #include "draktharonkeep/DraktharonKeepTriggerContext.h"
// #include "violethold/VioletHoldTriggerContext.h" // #include "violethold/VioletHoldTriggerContext.h"
// #include "gundrak/GundrakTriggerContext.h" // #include "gundrak/GundrakTriggerContext.h"

View File

@@ -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<Action>
{
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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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<DpsAssistAction*>(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<DpsAssistAction*>(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<MovementAction*>(action))
{
return 0.0f;
}
}
return 1.0f;
}

View File

@@ -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

View File

@@ -0,0 +1,36 @@
#include "OldKingdomStrategy.h"
#include "OldKingdomMultipliers.h"
void WotlkDungeonOKStrategy::InitTriggers(std::vector<TriggerNode*> &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<Multiplier*> &multipliers)
{
multipliers.push_back(new ElderNadoxMultiplier(botAI));
multipliers.push_back(new JedogaShadowseekerMultiplier(botAI));
multipliers.push_back(new ForgottenOneMultiplier(botAI));
}

View File

@@ -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<TriggerNode*> &triggers) override;
virtual void InitMultipliers(std::vector<Multiplier*> &multipliers) override;
};
#endif

View File

@@ -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<Trigger>
{
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

View File

@@ -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");
}

View File

@@ -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