mirror of
https://github.com/mod-playerbots/mod-playerbots
synced 2025-11-29 15:58:20 +08:00
Merge pull request #615 from Bobblybook/master
Halls of Lightning implementation
This commit is contained in:
@@ -58,6 +58,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
|
||||
actionContexts.Add(new WotlkDungeonVHActionContext());
|
||||
actionContexts.Add(new WotlkDungeonGDActionContext());
|
||||
actionContexts.Add(new WotlkDungeonHoSActionContext());
|
||||
actionContexts.Add(new WotlkDungeonHoLActionContext());
|
||||
|
||||
triggerContexts.Add(new TriggerContext());
|
||||
triggerContexts.Add(new ChatTriggerContext());
|
||||
@@ -76,6 +77,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
|
||||
triggerContexts.Add(new WotlkDungeonVHTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonGDTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonHoSTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonHoLTriggerContext());
|
||||
|
||||
valueContexts.Add(new ValueContext());
|
||||
|
||||
|
||||
@@ -10,12 +10,11 @@
|
||||
#include "wotlk/violethold/VioletHoldStrategy.h"
|
||||
#include "wotlk/gundrak/GundrakStrategy.h"
|
||||
#include "wotlk/hallsofstone/HallsOfStoneStrategy.h"
|
||||
#include "wotlk/hallsoflightning/HallsOfLightningStrategy.h"
|
||||
|
||||
/*
|
||||
Full list/TODO:
|
||||
|
||||
Halls of Lightning - HoL
|
||||
General Bjarngrim, Volkhan, Ionar, Loken
|
||||
The Oculus - Occ
|
||||
Drakos the Interrogator, Varos Cloudstrider, Mage-Lord Urom, Ley-Guardian Eregos
|
||||
Utgarde Pinnacle - UP
|
||||
@@ -76,8 +75,8 @@ class DungeonStrategyContext : public NamedObjectContext<Strategy>
|
||||
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_hol(PlayerbotAI* botAI) { return new WotlkDungeonHoLStrategy(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); }
|
||||
static Strategy* wotlk_cos(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); }
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "violethold/VioletHoldActionContext.h"
|
||||
#include "gundrak/GundrakActionContext.h"
|
||||
#include "hallsofstone/HallsOfStoneActionContext.h"
|
||||
// #include "hallsoflightning/HallsOfLightningActionContext.h"
|
||||
#include "hallsoflightning/HallsOfLightningActionContext.h"
|
||||
// #include "oculus/OculusActionContext.h"
|
||||
// #include "utgardepinnacle/UtgardePinnacleActionContext.h"
|
||||
// #include "cullingofstratholme/CullingOfStratholmeActionContext.h"
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "violethold/VioletHoldTriggerContext.h"
|
||||
#include "gundrak/GundrakTriggerContext.h"
|
||||
#include "hallsofstone/HallsOfStoneTriggerContext.h"
|
||||
// #include "hallsoflightning/HallsOfLightningTriggerContext.h"
|
||||
#include "hallsoflightning/HallsOfLightningTriggerContext.h"
|
||||
// #include "oculus/OculusTriggerContext.h"
|
||||
// #include "utgardepinnacle/UtgardePinnacleTriggerContext.h"
|
||||
// #include "cullingofstratholme/CullingOfStratholmeTriggerContext.h"
|
||||
|
||||
@@ -38,19 +38,15 @@ float KrikthirMultiplier::GetValue(Action* action)
|
||||
if (boss && watcher)
|
||||
{
|
||||
// Do not target swap
|
||||
// TODO: Need to suppress AoE actions but unsure how to identify them
|
||||
// TODO: TEST AOE Avoid
|
||||
if (dynamic_cast<DpsAssistAction*>(action)
|
||||
|| dynamic_cast<DpsAoeAction*>(action))
|
||||
if (dynamic_cast<DpsAssistAction*>(action))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (action->getThreatType() == Action::ActionThreatType::Aoe)
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
// Doesn't seem to work
|
||||
// if (action->getThreatType() == Action::ActionThreatType::Aoe)
|
||||
// {
|
||||
// bot->Yell("Suppressed AoE", LANG_UNIVERSAL);
|
||||
// return 0.0f;
|
||||
// }
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONHOLACTIONCONTEXT_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONHOLACTIONCONTEXT_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "HallsOfLightningActions.h"
|
||||
|
||||
class WotlkDungeonHoLActionContext : public NamedObjectContext<Action>
|
||||
{
|
||||
public:
|
||||
WotlkDungeonHoLActionContext() {
|
||||
creators["bjarngrim target"] = &WotlkDungeonHoLActionContext::bjarngrim_target;
|
||||
creators["avoid whirlwind"] = &WotlkDungeonHoLActionContext::avoid_whirlwind;
|
||||
creators["volkhan target"] = &WotlkDungeonHoLActionContext::volkhan_target;
|
||||
creators["static overload spread"] = &WotlkDungeonHoLActionContext::static_overload_spread;
|
||||
creators["ball lightning spread"] = &WotlkDungeonHoLActionContext::ball_lightning_spread;
|
||||
creators["ionar tank position"] = &WotlkDungeonHoLActionContext::ionar_tank_position;
|
||||
creators["disperse position"] = &WotlkDungeonHoLActionContext::disperse_position;
|
||||
creators["loken stack"] = &WotlkDungeonHoLActionContext::loken_stack;
|
||||
creators["avoid lightning nova"] = &WotlkDungeonHoLActionContext::avoid_lightning_nova;
|
||||
}
|
||||
private:
|
||||
static Action* bjarngrim_target(PlayerbotAI* ai) { return new BjarngrimTargetAction(ai); }
|
||||
static Action* avoid_whirlwind(PlayerbotAI* ai) { return new AvoidWhirlwindAction(ai); }
|
||||
static Action* volkhan_target(PlayerbotAI* ai) { return new VolkhanTargetAction(ai); }
|
||||
static Action* static_overload_spread(PlayerbotAI* ai) { return new StaticOverloadSpreadAction(ai); }
|
||||
static Action* ball_lightning_spread(PlayerbotAI* ai) { return new BallLightningSpreadAction(ai); }
|
||||
static Action* ionar_tank_position(PlayerbotAI* ai) { return new IonarTankPositionAction(ai); }
|
||||
static Action* disperse_position(PlayerbotAI* ai) { return new DispersePositionAction(ai); }
|
||||
static Action* loken_stack(PlayerbotAI* ai) { return new LokenStackAction(ai); }
|
||||
static Action* avoid_lightning_nova(PlayerbotAI* ai) { return new AvoidLightningNovaAction(ai); }
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,170 @@
|
||||
#include "Playerbots.h"
|
||||
#include "HallsOfLightningActions.h"
|
||||
#include "HallsOfLightningStrategy.h"
|
||||
|
||||
bool BjarngrimTargetAction::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_STORMFORGED_LIEUTENANT)
|
||||
{
|
||||
target = unit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Unit* currentTarget = AI_VALUE(Unit*, "current target");
|
||||
// There are two, we don't want to ping-pong between them if we're attacking one already
|
||||
if (target && currentTarget && currentTarget->GetEntry() == NPC_STORMFORGED_LIEUTENANT)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (AI_VALUE(Unit*, "current target") == target)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Attack(target);
|
||||
}
|
||||
|
||||
bool AvoidWhirlwindAction::Execute(Event event)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "general bjarngrim");
|
||||
if (!boss) { return false; }
|
||||
|
||||
float distance = bot->GetExactDist2d(boss->GetPosition());
|
||||
float radius = 8.0f;
|
||||
float distanceExtra = 2.0f;
|
||||
|
||||
if (distance < radius + distanceExtra)
|
||||
{
|
||||
return MoveAway(boss, radius + distanceExtra - distance);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VolkhanTargetAction::Execute(Event event)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "volkhan");
|
||||
if (!boss || AI_VALUE(Unit*, "current target") == boss)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Attack(boss);
|
||||
}
|
||||
|
||||
bool StaticOverloadSpreadAction::Execute(Event event)
|
||||
{
|
||||
float radius = 8.0f;
|
||||
float distanceExtra = 2.0f;
|
||||
|
||||
GuidVector members = AI_VALUE(GuidVector, "group members");
|
||||
for (auto& member : members)
|
||||
{
|
||||
if (bot->GetGUID() == member)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Unit* unit = botAI->GetUnit(member);
|
||||
if (unit && unit->HasAura(SPELL_STATIC_OVERLOAD)
|
||||
&& bot->GetExactDist2d(unit) < radius)
|
||||
{
|
||||
return MoveAway(unit, radius + distanceExtra - bot->GetExactDist2d(unit));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BallLightningSpreadAction::Execute(Event event)
|
||||
{
|
||||
float radius = 6.0f;
|
||||
float distanceExtra = 1.0f;
|
||||
|
||||
GuidVector members = AI_VALUE(GuidVector, "group members");
|
||||
for (auto& member : members)
|
||||
{
|
||||
if (bot->GetGUID() == member)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Unit* unit = botAI->GetUnit(member);
|
||||
if (unit && bot->GetExactDist2d(unit) < radius + distanceExtra)
|
||||
{
|
||||
return MoveAway(unit, radius + distanceExtra - bot->GetExactDist2d(unit));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IonarTankPositionAction::isUseful() { return bot->GetExactDist2d(IONAR_TANK_POSITION) > 10.0f; }
|
||||
bool IonarTankPositionAction::Execute(Event event)
|
||||
{
|
||||
return MoveTo(bot->GetMapId(), IONAR_TANK_POSITION.GetPositionX(), IONAR_TANK_POSITION.GetPositionY(), IONAR_TANK_POSITION.GetPositionZ(),
|
||||
false, false, false, true, MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
|
||||
bool DispersePositionAction::isUseful() { return bot->GetExactDist2d(DISPERSE_POSITION) > 8.0f; }
|
||||
bool DispersePositionAction::Execute(Event event)
|
||||
{
|
||||
return MoveTo(bot->GetMapId(), DISPERSE_POSITION.GetPositionX(), DISPERSE_POSITION.GetPositionY(), DISPERSE_POSITION.GetPositionZ(),
|
||||
false, false, false, true, MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
|
||||
bool LokenStackAction::isUseful()
|
||||
{
|
||||
// Minimum hunter range is 5, but values too close to this seem to cause issues..
|
||||
// Hunter bots will try and melee in between ranged attacks, or just melee entirely at 5 as they are in range.
|
||||
// 6.5 or 7.0 solves this.
|
||||
if(bot->getClass() == CLASS_HUNTER)
|
||||
{
|
||||
return AI_VALUE2(float, "distance", "current target") > 6.5f;
|
||||
}
|
||||
// else
|
||||
return AI_VALUE2(float, "distance", "current target") > 2.0f;
|
||||
}
|
||||
bool LokenStackAction::Execute(Event event)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "loken");
|
||||
if (!boss) { return false; }
|
||||
|
||||
if (!boss->HasUnitState(UNIT_STATE_CASTING))
|
||||
{
|
||||
if(bot->getClass() == CLASS_HUNTER)
|
||||
{
|
||||
return Move(bot->GetAngle(boss), fmin(bot->GetExactDist2d(boss) - 6.5f, 10.0f));
|
||||
}
|
||||
// else
|
||||
return Move(bot->GetAngle(boss), fmin(bot->GetExactDist2d(boss), 10.0f));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool AvoidLightningNovaAction::Execute(Event event)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "loken");
|
||||
if (!boss) { return false; }
|
||||
|
||||
float distance = bot->GetExactDist2d(boss->GetPosition());
|
||||
float radius = 20.0f;
|
||||
float distanceExtra = 2.0f;
|
||||
|
||||
if (distance < radius + distanceExtra)
|
||||
{
|
||||
return MoveAway(boss, radius + distanceExtra - distance);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONHOLACTIONS_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONHOLACTIONS_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "AttackAction.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "Playerbots.h"
|
||||
#include "HallsOfLightningTriggers.h"
|
||||
|
||||
const Position IONAR_TANK_POSITION = Position(1078.860f, -261.928f, 61.226f);
|
||||
const Position DISPERSE_POSITION = Position(1161.152f, -261.584f, 53.223f);
|
||||
|
||||
class BjarngrimTargetAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
BjarngrimTargetAction(PlayerbotAI* ai) : AttackAction(ai, "bjarngrim target") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class AvoidWhirlwindAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
AvoidWhirlwindAction(PlayerbotAI* ai) : MovementAction(ai, "avoid whirlwind") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class VolkhanTargetAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
VolkhanTargetAction(PlayerbotAI* ai) : AttackAction(ai, "volkhan target") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class StaticOverloadSpreadAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
StaticOverloadSpreadAction(PlayerbotAI* ai) : MovementAction(ai, "static overload spread") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class BallLightningSpreadAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
BallLightningSpreadAction(PlayerbotAI* ai) : MovementAction(ai, "ball lightning spread") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class IonarTankPositionAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
IonarTankPositionAction(PlayerbotAI* ai) : MovementAction(ai, "ionar tank position") {}
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
class DispersePositionAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
DispersePositionAction(PlayerbotAI* ai) : MovementAction(ai, "disperse position") {}
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
class LokenStackAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
LokenStackAction(PlayerbotAI* ai) : MovementAction(ai, "loken stack") {}
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
class AvoidLightningNovaAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
AvoidLightningNovaAction(PlayerbotAI* ai) : MovementAction(ai, "avoid lightning nova") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,103 @@
|
||||
#include "HallsOfLightningMultipliers.h"
|
||||
#include "HallsOfLightningActions.h"
|
||||
#include "GenericSpellActions.h"
|
||||
#include "ChooseTargetActions.h"
|
||||
#include "MovementActions.h"
|
||||
#include "HallsOfLightningTriggers.h"
|
||||
#include "Action.h"
|
||||
|
||||
float BjarngrimMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "general bjarngrim");
|
||||
if (!boss || botAI->IsHeal(bot)) { return 1.0f; }
|
||||
|
||||
if (boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_WHIRLWIND_BJARNGRIM))
|
||||
{
|
||||
if (dynamic_cast<MovementAction*>(action) && !dynamic_cast<AvoidWhirlwindAction*>(action))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
// Detect boss adds this way as sometimes they don't get added to threat table on dps bots,
|
||||
// and some dps just stand at range and don't engage the boss at all as they can't find the adds
|
||||
// Unit* boss_add = AI_VALUE2(Unit*, "find target", "stormforged lieutenant");
|
||||
Unit* boss_add = nullptr;
|
||||
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_STORMFORGED_LIEUTENANT)
|
||||
{
|
||||
boss_add = unit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!boss_add || botAI->IsTank(bot)) { return 1.0f; }
|
||||
|
||||
if (dynamic_cast<DpsAssistAction*>(action))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (action->getThreatType() == Action::ActionThreatType::Aoe)
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float VolkhanMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "volkhan");
|
||||
if (!boss || botAI->IsTank(bot) || botAI->IsHeal(bot)) { return 1.0f; }
|
||||
|
||||
if (dynamic_cast<DpsAssistAction*>(action))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (action->getThreatType() == Action::ActionThreatType::Aoe)
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float IonarMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "ionar");
|
||||
if (!boss) { return 1.0f; }
|
||||
|
||||
if(!bot->CanSeeOrDetect(boss))
|
||||
{
|
||||
if (dynamic_cast<MovementAction*>(action)
|
||||
&& !dynamic_cast<DispersePositionAction*>(action)
|
||||
&& !dynamic_cast<StaticOverloadSpreadAction*>(action))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float LokenMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "loken");
|
||||
if (!boss) { return 1.0f; }
|
||||
|
||||
if (dynamic_cast<FleeAction*>(action)) { return 0.0f; }
|
||||
|
||||
if (boss->FindCurrentSpellBySpellId(SPELL_LIGHTNING_NOVA)
|
||||
&& dynamic_cast<MovementAction*>(action)
|
||||
&& !dynamic_cast<AvoidLightningNovaAction*>(action))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONHOLMULTIPLIERS_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONHOLMULTIPLIERS_H
|
||||
|
||||
#include "Multiplier.h"
|
||||
|
||||
class BjarngrimMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
BjarngrimMultiplier(PlayerbotAI* ai) : Multiplier(ai, "general bjarngrim") {}
|
||||
|
||||
public:
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class VolkhanMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
VolkhanMultiplier(PlayerbotAI* ai) : Multiplier(ai, "volkhan") {}
|
||||
|
||||
public:
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class IonarMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
IonarMultiplier(PlayerbotAI* ai) : Multiplier(ai, "ionar") {}
|
||||
|
||||
public:
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class LokenMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
LokenMultiplier(PlayerbotAI* ai) : Multiplier(ai, "loken") {}
|
||||
|
||||
public:
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,41 @@
|
||||
#include "HallsOfLightningStrategy.h"
|
||||
#include "HallsOfLightningMultipliers.h"
|
||||
|
||||
|
||||
void WotlkDungeonHoLStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
|
||||
{
|
||||
// General Bjarngrim
|
||||
triggers.push_back(new TriggerNode("stormforged lieutenant",
|
||||
NextAction::array(0, new NextAction("bjarngrim target", ACTION_RAID + 5), nullptr)));
|
||||
triggers.push_back(new TriggerNode("whirlwind",
|
||||
NextAction::array(0, new NextAction("avoid whirlwind", ACTION_RAID + 4), nullptr)));
|
||||
|
||||
// Volkhan
|
||||
triggers.push_back(new TriggerNode("volkhan",
|
||||
NextAction::array(0, new NextAction("volkhan target", ACTION_RAID + 5), nullptr)));
|
||||
|
||||
// Ionar
|
||||
triggers.push_back(new TriggerNode("ionar disperse",
|
||||
NextAction::array(0, new NextAction("disperse position", ACTION_MOVE + 5), nullptr)));
|
||||
triggers.push_back(new TriggerNode("ionar tank aggro",
|
||||
NextAction::array(0, new NextAction("ionar tank position", ACTION_MOVE + 4), nullptr)));
|
||||
triggers.push_back(new TriggerNode("static overload",
|
||||
NextAction::array(0, new NextAction("static overload spread", ACTION_MOVE + 3), nullptr)));
|
||||
// TODO: Targeted player can dodge the ball, but a single player soaking it isn't too bad to heal
|
||||
triggers.push_back(new TriggerNode("ball lightning",
|
||||
NextAction::array(0, new NextAction("ball lightning spread", ACTION_MOVE + 2), nullptr)));
|
||||
|
||||
// Loken
|
||||
triggers.push_back(new TriggerNode("lightning nova",
|
||||
NextAction::array(0, new NextAction("avoid lightning nova", ACTION_MOVE + 5), nullptr)));
|
||||
triggers.push_back(new TriggerNode("loken ranged",
|
||||
NextAction::array(0, new NextAction("loken stack", ACTION_MOVE + 4), nullptr)));
|
||||
}
|
||||
|
||||
void WotlkDungeonHoLStrategy::InitMultipliers(std::vector<Multiplier*> &multipliers)
|
||||
{
|
||||
multipliers.push_back(new BjarngrimMultiplier(botAI));
|
||||
multipliers.push_back(new VolkhanMultiplier(botAI));
|
||||
multipliers.push_back(new IonarMultiplier(botAI));
|
||||
multipliers.push_back(new LokenMultiplier(botAI));
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONHOLSTRATEGY_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONHOLSTRATEGY_H
|
||||
|
||||
#include "Multiplier.h"
|
||||
#include "AiObjectContext.h"
|
||||
#include "Strategy.h"
|
||||
|
||||
|
||||
class WotlkDungeonHoLStrategy : public Strategy
|
||||
{
|
||||
public:
|
||||
WotlkDungeonHoLStrategy(PlayerbotAI* ai) : Strategy(ai) {}
|
||||
virtual std::string const getName() override { return "halls of lightning"; }
|
||||
virtual void InitTriggers(std::vector<TriggerNode*> &triggers) override;
|
||||
virtual void InitMultipliers(std::vector<Multiplier*> &multipliers) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONHOLTRIGGERCONTEXT_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONHOLTRIGGERCONTEXT_H
|
||||
|
||||
#include "NamedObjectContext.h"
|
||||
#include "AiObjectContext.h"
|
||||
#include "HallsOfLightningTriggers.h"
|
||||
|
||||
class WotlkDungeonHoLTriggerContext : public NamedObjectContext<Trigger>
|
||||
{
|
||||
public:
|
||||
WotlkDungeonHoLTriggerContext()
|
||||
{
|
||||
creators["stormforged lieutenant"] = &WotlkDungeonHoLTriggerContext::stormforged_lieutenant;
|
||||
creators["whirlwind"] = &WotlkDungeonHoLTriggerContext::bjarngrim_whirlwind;
|
||||
creators["volkhan"] = &WotlkDungeonHoLTriggerContext::volkhan;
|
||||
creators["static overload"] = &WotlkDungeonHoLTriggerContext::static_overload;
|
||||
creators["ball lightning"] = &WotlkDungeonHoLTriggerContext::ball_lightning;
|
||||
creators["ionar tank aggro"] = &WotlkDungeonHoLTriggerContext::ionar_tank_aggro;
|
||||
creators["ionar disperse"] = &WotlkDungeonHoLTriggerContext::ionar_disperse;
|
||||
creators["loken ranged"] = &WotlkDungeonHoLTriggerContext::loken_ranged;
|
||||
creators["lightning nova"] = &WotlkDungeonHoLTriggerContext::lightning_nova;
|
||||
}
|
||||
private:
|
||||
static Trigger* stormforged_lieutenant(PlayerbotAI* ai) { return new StormforgedLieutenantTrigger(ai); }
|
||||
static Trigger* bjarngrim_whirlwind(PlayerbotAI* ai) { return new BjarngrimWhirlwindTrigger(ai); }
|
||||
static Trigger* volkhan(PlayerbotAI* ai) { return new VolkhanTrigger(ai); }
|
||||
static Trigger* static_overload(PlayerbotAI* ai) { return new IonarStaticOverloadTrigger(ai); }
|
||||
static Trigger* ball_lightning(PlayerbotAI* ai) { return new IonarBallLightningTrigger(ai); }
|
||||
static Trigger* ionar_tank_aggro(PlayerbotAI* ai) { return new IonarTankAggroTrigger(ai); }
|
||||
static Trigger* ionar_disperse(PlayerbotAI* ai) { return new IonarDisperseTrigger(ai); }
|
||||
static Trigger* loken_ranged(PlayerbotAI* ai) { return new LokenRangedTrigger(ai); }
|
||||
static Trigger* lightning_nova(PlayerbotAI* ai) { return new LokenLightningNovaTrigger(ai); }
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,92 @@
|
||||
#include "Playerbots.h"
|
||||
#include "HallsOfLightningTriggers.h"
|
||||
#include "AiObject.h"
|
||||
#include "AiObjectContext.h"
|
||||
|
||||
bool StormforgedLieutenantTrigger::IsActive()
|
||||
{
|
||||
if (botAI->IsTank(bot) || botAI->IsHeal(bot)) { 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_STORMFORGED_LIEUTENANT)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BjarngrimWhirlwindTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "general bjarngrim");
|
||||
if (!boss) { return false; }
|
||||
|
||||
return boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_WHIRLWIND_BJARNGRIM);
|
||||
}
|
||||
|
||||
bool VolkhanTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "volkhan");
|
||||
return boss && !botAI->IsTank(bot) && !botAI->IsHeal(bot);
|
||||
}
|
||||
|
||||
bool IonarStaticOverloadTrigger::IsActive()
|
||||
{
|
||||
GuidVector members = AI_VALUE(GuidVector, "group members");
|
||||
for (auto& member : members)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(member);
|
||||
if (unit && unit->HasAura(SPELL_STATIC_OVERLOAD))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IonarBallLightningTrigger::IsActive()
|
||||
{
|
||||
if (botAI->IsMelee(bot)) { return false; }
|
||||
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "ionar");
|
||||
if (!boss) { return false; }
|
||||
|
||||
return boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_BALL_LIGHTNING);
|
||||
}
|
||||
|
||||
bool IonarTankAggroTrigger::IsActive()
|
||||
{
|
||||
if (!botAI->IsTank(bot)) { return false; }
|
||||
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "ionar");
|
||||
if (!boss) { return false; }
|
||||
|
||||
return AI_VALUE2(bool, "has aggro", "current target");
|
||||
}
|
||||
|
||||
bool IonarDisperseTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "ionar");
|
||||
if (!boss) { return false; }
|
||||
|
||||
return !bot->CanSeeOrDetect(boss) || boss->FindCurrentSpellBySpellId(SPELL_DISPERSE);
|
||||
}
|
||||
|
||||
bool LokenRangedTrigger::IsActive()
|
||||
{
|
||||
return !botAI->IsMelee(bot) && AI_VALUE2(Unit*, "find target", "loken");
|
||||
}
|
||||
|
||||
bool LokenLightningNovaTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "loken");
|
||||
if (!boss) { return false; }
|
||||
|
||||
return boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_LIGHTNING_NOVA);
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONHOLTRIGGERS_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONHOLTRIGGERS_H
|
||||
|
||||
#include "Trigger.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "GenericTriggers.h"
|
||||
#include "DungeonStrategyUtils.h"
|
||||
|
||||
enum HallsOfLightningIDs
|
||||
{
|
||||
// General Bjarngrim
|
||||
NPC_STORMFORGED_LIEUTENANT = 29240,
|
||||
SPELL_WHIRLWIND_BJARNGRIM = 52027,
|
||||
|
||||
// Ionar
|
||||
SPELL_STATIC_OVERLOAD_N = 52658,
|
||||
SPELL_STATIC_OVERLOAD_H = 59795,
|
||||
SPELL_BALL_LIGHTNING_N = 52780,
|
||||
SPELL_BALL_LIGHTNING_H = 59800,
|
||||
SPELL_DISPERSE = 52770,
|
||||
NPC_SPARK_OF_IONAR = 28926,
|
||||
|
||||
// Loken
|
||||
SPELL_LIGHTNING_NOVA_N = 52960,
|
||||
SPELL_LIGHTNING_NOVA_H = 59835,
|
||||
};
|
||||
|
||||
#define SPELL_STATIC_OVERLOAD DUNGEON_MODE(bot, SPELL_STATIC_OVERLOAD_N, SPELL_STATIC_OVERLOAD_H)
|
||||
#define SPELL_BALL_LIGHTNING DUNGEON_MODE(bot, SPELL_BALL_LIGHTNING_N, SPELL_BALL_LIGHTNING_H)
|
||||
#define SPELL_LIGHTNING_NOVA DUNGEON_MODE(bot, SPELL_LIGHTNING_NOVA_N, SPELL_LIGHTNING_NOVA_H)
|
||||
|
||||
class StormforgedLieutenantTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
StormforgedLieutenantTrigger(PlayerbotAI* ai) : Trigger(ai, "stormforged lieutenant") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class BjarngrimWhirlwindTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
BjarngrimWhirlwindTrigger(PlayerbotAI* ai) : Trigger(ai, "bjarngrim whirlwind") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class VolkhanTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
VolkhanTrigger(PlayerbotAI* ai) : Trigger(ai, "volkhan") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IonarStaticOverloadTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IonarStaticOverloadTrigger(PlayerbotAI* ai) : Trigger(ai, "ionar static overload") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IonarBallLightningTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IonarBallLightningTrigger(PlayerbotAI* ai) : Trigger(ai, "ionar ball lightning spread") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IonarTankAggroTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IonarTankAggroTrigger(PlayerbotAI* ai) : Trigger(ai, "ionar tank aggro") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IonarDisperseTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IonarDisperseTrigger(PlayerbotAI* ai) : Trigger(ai, "ionar disperse") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LokenRangedTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LokenRangedTrigger(PlayerbotAI* ai) : Trigger(ai, "loken ranged") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LokenLightningNovaTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LokenLightningNovaTrigger(PlayerbotAI* ai) : Trigger(ai, "lightning nova") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user