Halls of Stone implementation

This commit is contained in:
Bobblybook
2024-10-15 22:40:47 +11:00
parent 608f9d1a68
commit 70c9bdbd9d
15 changed files with 293 additions and 5 deletions

View File

@@ -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());

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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<MovementAction*>(action) && !dynamic_cast<ShatterSpreadAction*>(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<MovementAction*>(action) && !dynamic_cast<AvoidLightningRingAction*>(action))
{
return 0.0f;
}
}
return 1.0f;
}

View File

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

View File

@@ -0,0 +1,29 @@
#include "HallsOfStoneStrategy.h"
#include "HallsOfStoneMultipliers.h"
void WotlkDungeonHoSStrategy::InitTriggers(std::vector<TriggerNode*> &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<Multiplier*> &multipliers)
{
multipliers.push_back(new KrystallusMultiplier(botAI));
}

View File

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

View File

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

View File

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

View File

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