mirror of
https://github.com/mod-playerbots/mod-playerbots
synced 2025-11-29 15:58:20 +08:00
Halls of Stone implementation
This commit is contained in:
@@ -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());
|
||||
|
||||
|
||||
@@ -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); }
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user