mirror of
https://github.com/mod-playerbots/mod-playerbots
synced 2025-11-29 15:58:20 +08:00
Nexus implementation & dragon flank code
This commit is contained in:
@@ -51,6 +51,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
|
|||||||
actionContexts.Add(new RaidUlduarActionContext());
|
actionContexts.Add(new RaidUlduarActionContext());
|
||||||
actionContexts.Add(new RaidIccActionContext());
|
actionContexts.Add(new RaidIccActionContext());
|
||||||
actionContexts.Add(new WotlkDungeonUKActionContext());
|
actionContexts.Add(new WotlkDungeonUKActionContext());
|
||||||
|
actionContexts.Add(new WotlkDungeonNexActionContext());
|
||||||
|
|
||||||
triggerContexts.Add(new TriggerContext());
|
triggerContexts.Add(new TriggerContext());
|
||||||
triggerContexts.Add(new ChatTriggerContext());
|
triggerContexts.Add(new ChatTriggerContext());
|
||||||
@@ -62,6 +63,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
|
|||||||
triggerContexts.Add(new RaidUlduarTriggerContext());
|
triggerContexts.Add(new RaidUlduarTriggerContext());
|
||||||
triggerContexts.Add(new RaidIccTriggerContext());
|
triggerContexts.Add(new RaidIccTriggerContext());
|
||||||
triggerContexts.Add(new WotlkDungeonUKTriggerContext());
|
triggerContexts.Add(new WotlkDungeonUKTriggerContext());
|
||||||
|
triggerContexts.Add(new WotlkDungeonNexTriggerContext());
|
||||||
|
|
||||||
valueContexts.Add(new ValueContext());
|
valueContexts.Add(new ValueContext());
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,11 @@
|
|||||||
|
|
||||||
#include "Strategy.h"
|
#include "Strategy.h"
|
||||||
#include "wotlk/utgardekeep/UtgardeKeepStrategy.h"
|
#include "wotlk/utgardekeep/UtgardeKeepStrategy.h"
|
||||||
|
#include "wotlk/nexus/NexusStrategy.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Full list/TODO:
|
Full list/TODO:
|
||||||
|
|
||||||
The Nexus - Nex
|
|
||||||
Grand Magus Telestra, Anomalus, Ormorok the Tree-Shaper, Keristrasza, Commander Stoutbeard (Horde Heroic Only)/Commander Kolurg (Alliance Heroic Only)
|
|
||||||
Azjol-Nerub: Azjol-Nerub - AN
|
Azjol-Nerub: Azjol-Nerub - AN
|
||||||
Krik'thir the Gatewatcher, Hadronox, Anub'arak
|
Krik'thir the Gatewatcher, Hadronox, Anub'arak
|
||||||
Ahn'kahet: The Old Kingdom - OK
|
Ahn'kahet: The Old Kingdom - OK
|
||||||
@@ -76,7 +75,7 @@ class DungeonStrategyContext : public NamedObjectContext<Strategy>
|
|||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
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 WotlkDungeonUKStrategy(botAI); }
|
static Strategy* wotlk_nex(PlayerbotAI* botAI) { return new WotlkDungeonNexStrategy(botAI); }
|
||||||
static Strategy* wotlk_an(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); }
|
static Strategy* wotlk_an(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); }
|
||||||
static Strategy* wotlk_ok(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(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); }
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#define _PLAYERBOT_WOTLKDUNGEONACTIONCONTEXT_H
|
#define _PLAYERBOT_WOTLKDUNGEONACTIONCONTEXT_H
|
||||||
|
|
||||||
#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"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#define _PLAYERBOT_WOTLKDUNGEONTRIGGERCONTEXT_H
|
#define _PLAYERBOT_WOTLKDUNGEONTRIGGERCONTEXT_H
|
||||||
|
|
||||||
#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"
|
||||||
|
|||||||
30
src/strategy/dungeons/wotlk/nexus/NexusActionContext.h
Normal file
30
src/strategy/dungeons/wotlk/nexus/NexusActionContext.h
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#ifndef _PLAYERBOT_WOTLKDUNGEONNEXACTIONCONTEXT_H
|
||||||
|
#define _PLAYERBOT_WOTLKDUNGEONNEXACTIONCONTEXT_H
|
||||||
|
|
||||||
|
#include "Action.h"
|
||||||
|
#include "NamedObjectContext.h"
|
||||||
|
#include "NexusActions.h"
|
||||||
|
|
||||||
|
class WotlkDungeonNexActionContext : public NamedObjectContext<Action>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WotlkDungeonNexActionContext() {
|
||||||
|
creators["move from whirlwind"] = &WotlkDungeonNexActionContext::move_from_whirlwind;
|
||||||
|
creators["firebomb spread"] = &WotlkDungeonNexActionContext::firebomb_spread;
|
||||||
|
creators["telestra split target"] = &WotlkDungeonNexActionContext::telestra_split_target;
|
||||||
|
creators["chaotic rift target"] = &WotlkDungeonNexActionContext::chaotic_rift_target;
|
||||||
|
creators["dodge spikes"] = &WotlkDungeonNexActionContext::dodge_spikes;
|
||||||
|
creators["intense cold jump"] = &WotlkDungeonNexActionContext::intense_cold_jump;
|
||||||
|
creators["rear flank position"] = &WotlkDungeonNexActionContext::rear_flank_position;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
static Action* move_from_whirlwind(PlayerbotAI* ai) { return new MoveFromWhirlwindAction(ai); }
|
||||||
|
static Action* firebomb_spread(PlayerbotAI* ai) { return new FirebombSpreadAction(ai); }
|
||||||
|
static Action* telestra_split_target(PlayerbotAI* ai) { return new TelestraSplitTargetAction(ai); }
|
||||||
|
static Action* chaotic_rift_target(PlayerbotAI* ai) { return new ChaoticRiftTargetAction(ai); }
|
||||||
|
static Action* dodge_spikes(PlayerbotAI* ai) { return new DodgeSpikesAction(ai); }
|
||||||
|
static Action* intense_cold_jump(PlayerbotAI* ai) { return new IntenseColdJumpAction(ai); }
|
||||||
|
static Action* rear_flank_position(PlayerbotAI* ai) { return new RearFlankPositionAction(ai); }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
206
src/strategy/dungeons/wotlk/nexus/NexusActions.cpp
Normal file
206
src/strategy/dungeons/wotlk/nexus/NexusActions.cpp
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
#include "Playerbots.h"
|
||||||
|
#include "NexusActions.h"
|
||||||
|
#include "NexusStrategy.h"
|
||||||
|
|
||||||
|
bool MoveFromWhirlwindAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
Unit* boss = nullptr;
|
||||||
|
uint8 faction = bot->GetTeamId();
|
||||||
|
float targetDist = 10.0f; // Whirlwind has range of 8, add a couple for safety buffer
|
||||||
|
|
||||||
|
switch (bot->GetMap()->GetDifficulty())
|
||||||
|
{
|
||||||
|
case DUNGEON_DIFFICULTY_NORMAL:
|
||||||
|
if (faction == TEAM_ALLIANCE)
|
||||||
|
{
|
||||||
|
boss = AI_VALUE2(Unit*, "find target", "horde commander");
|
||||||
|
}
|
||||||
|
else //if (faction == TEAM_HORDE)
|
||||||
|
{
|
||||||
|
boss = AI_VALUE2(Unit*, "find target", "alliance commander");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DUNGEON_DIFFICULTY_HEROIC:
|
||||||
|
if (faction == TEAM_ALLIANCE)
|
||||||
|
{
|
||||||
|
boss = AI_VALUE2(Unit*, "find target", "commander kolurg");
|
||||||
|
}
|
||||||
|
else //if (faction == TEAM_HORDE)
|
||||||
|
{
|
||||||
|
boss = AI_VALUE2(Unit*, "find target", "commander stoutbeard");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!boss || bot->GetExactDist2d(boss->GetPosition()) > targetDist)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return MoveAway(boss, targetDist - bot->GetExactDist2d(boss->GetPosition()));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FirebombSpreadAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
Unit* boss = AI_VALUE2(Unit*, "find target", "grand magus telestra");
|
||||||
|
float radius = 5.0f;
|
||||||
|
float targetDist = radius + 1.0f;
|
||||||
|
if (!boss) { return false; }
|
||||||
|
|
||||||
|
GuidVector members = AI_VALUE(GuidVector, "group members");
|
||||||
|
for (auto& member : members)
|
||||||
|
{
|
||||||
|
if (bot->GetGUID() == member)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (bot->GetExactDist2d(botAI->GetUnit(member)) < targetDist)
|
||||||
|
{
|
||||||
|
return MoveAway(botAI->GetUnit(member), targetDist);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TelestraSplitTargetAction::isUseful() { return !botAI->IsHeal(bot); }
|
||||||
|
bool TelestraSplitTargetAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
GuidVector attackers = AI_VALUE(GuidVector, "attackers");
|
||||||
|
Unit* splitTargets[3] = {nullptr, nullptr, nullptr};
|
||||||
|
|
||||||
|
for (auto& attacker : attackers)
|
||||||
|
{
|
||||||
|
Unit* npc = botAI->GetUnit(attacker);
|
||||||
|
if (!npc)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
switch (npc->GetEntry())
|
||||||
|
{
|
||||||
|
// Focus arcane clone first
|
||||||
|
case NPC_ARCANE_MAGUS:
|
||||||
|
splitTargets[0] = npc;
|
||||||
|
break;
|
||||||
|
// Then the frost clone
|
||||||
|
case NPC_FROST_MAGUS:
|
||||||
|
splitTargets[1] = npc;
|
||||||
|
break;
|
||||||
|
// Fire clone last
|
||||||
|
case NPC_FIRE_MAGUS:
|
||||||
|
splitTargets[2] = npc;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Unit* target : splitTargets)
|
||||||
|
{
|
||||||
|
// Attack the first valid split target in the priority list
|
||||||
|
if (target)
|
||||||
|
{
|
||||||
|
if (AI_VALUE(Unit*, "current target") != target)
|
||||||
|
{
|
||||||
|
return Attack(target);
|
||||||
|
}
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChaoticRiftTargetAction::isUseful() { return !botAI->IsHeal(bot); }
|
||||||
|
bool ChaoticRiftTargetAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
Unit* chaoticRift = 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->GetName() == "Chaotic Rift")
|
||||||
|
{
|
||||||
|
chaoticRift = unit;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!chaoticRift || AI_VALUE(Unit*, "current target") == chaoticRift)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return Attack(chaoticRift);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DodgeSpikesAction::isUseful()
|
||||||
|
{
|
||||||
|
Unit* boss = AI_VALUE2(Unit*, "find target", "ormorok the tree-shaper");
|
||||||
|
return bot->GetExactDist2d(boss) > 0.5f;
|
||||||
|
}
|
||||||
|
bool DodgeSpikesAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
Unit* boss = AI_VALUE2(Unit*, "find target", "ormorok the tree-shaper");
|
||||||
|
return Move(bot->GetAngle(boss), bot->GetExactDist2d(boss) - 0.3f);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IntenseColdJumpAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
// This needs improving but maybe it should be done in the playerbot core.
|
||||||
|
// Jump doesn't seem to support zero offset (eg. jump on the spot) so need to add a tiny delta.
|
||||||
|
// This does a tiny bunnyhop that takes a couple of ms, it doesn't do a natural jump.
|
||||||
|
// Adding extra Z offset causes floating, and appears to scale the jump speed based on Z difference.
|
||||||
|
// Probably best to revisit once bot movement is improved
|
||||||
|
return JumpTo(bot->GetMap()->GetId(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ() + 0.01f);
|
||||||
|
// bot->GetMotionMaster()->MoveFall();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RearFlankPositionAction::isUseful()
|
||||||
|
{
|
||||||
|
Unit* boss = AI_VALUE2(Unit*, "find target", "keristrasza");
|
||||||
|
if (!boss) { return false; }
|
||||||
|
|
||||||
|
// Need to double the front angle check to account for mirrored angle.
|
||||||
|
// Total 180 degrees (whole front half)
|
||||||
|
bool inFront = boss->HasInArc(2.f * DRAGON_MELEE_MIN_ANGLE, bot);
|
||||||
|
// Rear check does not need to double this angle as the logic is inverted
|
||||||
|
// and we are subtracing from 2pi.
|
||||||
|
bool inBack = !boss->HasInArc((2.f * M_PI) - DRAGON_MELEE_MAX_ANGLE, bot);
|
||||||
|
|
||||||
|
return inFront || inBack;
|
||||||
|
}
|
||||||
|
bool RearFlankPositionAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
Unit* boss = AI_VALUE2(Unit*, "find target", "keristrasza");
|
||||||
|
if (!boss) { return false; }
|
||||||
|
|
||||||
|
// float angleToMove = minAngle + rand_norm() * (maxAngle - minAngle);
|
||||||
|
float angle = frand(DRAGON_MELEE_MIN_ANGLE, DRAGON_MELEE_MAX_ANGLE);
|
||||||
|
// 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.
|
||||||
|
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;
|
||||||
|
leftFlank.RelocatePolarOffset(angle, distance);
|
||||||
|
rightFlank.RelocatePolarOffset(-angle, distance);
|
||||||
|
|
||||||
|
if (bot->GetExactDist2d(leftFlank) < bot->GetExactDist2d(rightFlank))
|
||||||
|
{
|
||||||
|
destination = &leftFlank;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
destination = &rightFlank;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MoveTo(bot->GetMapId(), destination->GetPositionX(), destination->GetPositionY(), destination->GetPositionZ());
|
||||||
|
}
|
||||||
79
src/strategy/dungeons/wotlk/nexus/NexusActions.h
Normal file
79
src/strategy/dungeons/wotlk/nexus/NexusActions.h
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
#ifndef _PLAYERBOT_WOTLKDUNGEONNEXACTIONS_H
|
||||||
|
#define _PLAYERBOT_WOTLKDUNGEONNEXACTIONS_H
|
||||||
|
|
||||||
|
#include "Action.h"
|
||||||
|
#include "AttackAction.h"
|
||||||
|
#include "PlayerbotAI.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
#include "NexusTriggers.h"
|
||||||
|
|
||||||
|
#define ANGLE_45_DEG (static_cast<float>(M_PI) / 4.f)
|
||||||
|
#define ANGLE_90_DEG M_PI_2
|
||||||
|
#define ANGLE_120_DEG (2.f * static_cast<float>(M_PI) / 3.f)
|
||||||
|
|
||||||
|
// Slice of the circle that we want melee dps to attack from.
|
||||||
|
// Measured from boss orientation, on one side.
|
||||||
|
|
||||||
|
// Even though the breath cone is not the full 180 degrees,
|
||||||
|
// avoid melee dps from the front due to parry potential.
|
||||||
|
// Bots should learn good dps etiquette :)
|
||||||
|
#define DRAGON_MELEE_MIN_ANGLE ANGLE_90_DEG
|
||||||
|
// This leaves a danger zone of 60 degrees at the tail end on both sides.
|
||||||
|
// This is a total of 120 degrees tail arc that bots will avoid -
|
||||||
|
// number just happens to be the same in this case, but this is always measured from the front.
|
||||||
|
#define DRAGON_MELEE_MAX_ANGLE ANGLE_120_DEG
|
||||||
|
|
||||||
|
class MoveFromWhirlwindAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MoveFromWhirlwindAction(PlayerbotAI* ai) : MovementAction(ai, "move from whirlwind") {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FirebombSpreadAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FirebombSpreadAction(PlayerbotAI* ai) : MovementAction(ai, "firebomb spread") {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TelestraSplitTargetAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TelestraSplitTargetAction(PlayerbotAI* ai) : AttackAction(ai, "telestra split target") {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
bool isUseful() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ChaoticRiftTargetAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ChaoticRiftTargetAction(PlayerbotAI* ai) : AttackAction(ai, "chaotic rift target") {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
bool isUseful() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DodgeSpikesAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DodgeSpikesAction(PlayerbotAI* ai) : MovementAction(ai, "dodge spikes") {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
bool isUseful() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class IntenseColdJumpAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
IntenseColdJumpAction(PlayerbotAI* ai) : MovementAction(ai, "intense cold jump") {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class RearFlankPositionAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RearFlankPositionAction(PlayerbotAI* ai) : MovementAction(ai, "rear flank position") {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
bool isUseful() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
99
src/strategy/dungeons/wotlk/nexus/NexusMultipliers.cpp
Normal file
99
src/strategy/dungeons/wotlk/nexus/NexusMultipliers.cpp
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
#include "NexusMultipliers.h"
|
||||||
|
#include "NexusActions.h"
|
||||||
|
#include "GenericSpellActions.h"
|
||||||
|
#include "ChooseTargetActions.h"
|
||||||
|
#include "MovementActions.h"
|
||||||
|
#include "NexusTriggers.h"
|
||||||
|
|
||||||
|
float FactionCommanderMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
Unit* boss = nullptr;
|
||||||
|
uint8 faction = bot->GetTeamId();
|
||||||
|
|
||||||
|
switch (bot->GetMap()->GetDifficulty())
|
||||||
|
{
|
||||||
|
case DUNGEON_DIFFICULTY_NORMAL:
|
||||||
|
if (faction == TEAM_ALLIANCE)
|
||||||
|
{
|
||||||
|
boss = AI_VALUE2(Unit*, "find target", "horde commander");
|
||||||
|
}
|
||||||
|
else //if (faction == TEAM_HORDE)
|
||||||
|
{
|
||||||
|
boss = AI_VALUE2(Unit*, "find target", "alliance commander");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DUNGEON_DIFFICULTY_HEROIC:
|
||||||
|
if (faction == TEAM_ALLIANCE)
|
||||||
|
{
|
||||||
|
boss = AI_VALUE2(Unit*, "find target", "commander kolurg");
|
||||||
|
}
|
||||||
|
else //if (faction == TEAM_HORDE)
|
||||||
|
{
|
||||||
|
boss = AI_VALUE2(Unit*, "find target", "commander stoutbeard");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (boss && boss->HasUnitState(UNIT_STATE_CASTING) &&
|
||||||
|
boss->FindCurrentSpellBySpellId(SPELL_WHIRLWIND))
|
||||||
|
{
|
||||||
|
// Prevent movement actions other than flee during a whirlwind, to prevent running back in early.
|
||||||
|
if (dynamic_cast<MovementAction*>(action) && !dynamic_cast<MoveFromWhirlwindAction*>(action))
|
||||||
|
{
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float TelestraMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
Unit* boss = AI_VALUE2(Unit*, "find target", "grand magus telestra");
|
||||||
|
if (boss && boss->GetEntry() != NPC_TELESTRA)
|
||||||
|
{
|
||||||
|
// boss is split into clones, do not auto acquire target
|
||||||
|
if (dynamic_cast<DpsAssistAction*>(action))
|
||||||
|
{
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float AnomalusMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
Unit* boss = AI_VALUE2(Unit*, "find target", "anomalus");
|
||||||
|
if (boss && boss->HasAura(BUFF_RIFT_SHIELD))
|
||||||
|
{
|
||||||
|
if (dynamic_cast<DpsAssistAction*>(action))
|
||||||
|
{
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float OrmorokMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
Unit* boss = AI_VALUE2(Unit*, "find target", "ormorok the tree-shaper");
|
||||||
|
if (!boss)
|
||||||
|
{
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
// These are used for auto ranged repositioning, need to suppress so ranged dps don't ping-pong
|
||||||
|
if (dynamic_cast<FleeAction*>(action))
|
||||||
|
{
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
// This boss is annoying and shuffles around a lot. Don't let tank move once fight has started.
|
||||||
|
// Extra checks are to allow the tank to close distance and engage the boss initially
|
||||||
|
if (dynamic_cast<MovementAction*>(action) && !dynamic_cast<DodgeSpikesAction*>(action)
|
||||||
|
&& botAI->IsTank(bot) && bot->IsWithinMeleeRange(boss)
|
||||||
|
&& AI_VALUE2(bool, "facing", "current target"))
|
||||||
|
{
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
51
src/strategy/dungeons/wotlk/nexus/NexusMultipliers.h
Normal file
51
src/strategy/dungeons/wotlk/nexus/NexusMultipliers.h
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
#ifndef _PLAYERBOT_WOTLKDUNGEONNEXMULTIPLIERS_H
|
||||||
|
#define _PLAYERBOT_WOTLKDUNGEONNEXMULTIPLIERS_H
|
||||||
|
|
||||||
|
#include "Multiplier.h"
|
||||||
|
|
||||||
|
class FactionCommanderMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FactionCommanderMultiplier(PlayerbotAI* ai) : Multiplier(ai, "faction commander") {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class TelestraMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TelestraMultiplier(PlayerbotAI* ai) : Multiplier(ai, "grand magus telestra") {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class AnomalusMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AnomalusMultiplier(PlayerbotAI* ai) : Multiplier(ai, "anomalus") {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class OrmorokMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
OrmorokMultiplier(PlayerbotAI* ai) : Multiplier(ai, "ormorok the tree-shaper") {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class KeristraszaMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KeristraszaMultiplier(PlayerbotAI* ai) : Multiplier(ai, "keristrasza") {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
54
src/strategy/dungeons/wotlk/nexus/NexusStrategy.cpp
Normal file
54
src/strategy/dungeons/wotlk/nexus/NexusStrategy.cpp
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#include "NexusStrategy.h"
|
||||||
|
#include "NexusMultipliers.h"
|
||||||
|
|
||||||
|
|
||||||
|
void WotlkDungeonNexStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
|
||||||
|
{
|
||||||
|
// Horde Commander (Alliance N)/Commander Kolurg (Alliance H)
|
||||||
|
// or
|
||||||
|
// Alliance Commander (Horde N)/Commander Stoutbeard (Horde H)
|
||||||
|
triggers.push_back(new TriggerNode("faction commander whirlwind",
|
||||||
|
NextAction::array(0, new NextAction("move from whirlwind", ACTION_MOVE + 5), nullptr)));
|
||||||
|
// TODO: Handle fear? (tremor totems, fear ward etc.)
|
||||||
|
|
||||||
|
// Grand Magus Telestra
|
||||||
|
triggers.push_back(new TriggerNode("telestra firebomb",
|
||||||
|
NextAction::array(0, new NextAction("firebomb spread", ACTION_MOVE + 5), nullptr)));
|
||||||
|
triggers.push_back(new TriggerNode("telestra split phase",
|
||||||
|
NextAction::array(0, new NextAction("telestra split target", ACTION_RAID + 1), nullptr)));
|
||||||
|
// TODO: Add priority interrupt on the frost split's Blizzard casts
|
||||||
|
|
||||||
|
// Anomalus
|
||||||
|
triggers.push_back(new TriggerNode("chaotic rift",
|
||||||
|
NextAction::array(0, new NextAction("chaotic rift target", ACTION_RAID + 1), nullptr)));
|
||||||
|
|
||||||
|
// Ormorok the Tree-Shaper
|
||||||
|
// Tank trigger to stack inside boss. Can also add return action to prevent boss repositioning
|
||||||
|
// if it becomes too much of a problem. He usually dies before he's up against a wall though
|
||||||
|
triggers.push_back(new TriggerNode("ormorok spikes",
|
||||||
|
NextAction::array(0, new NextAction("dodge spikes", ACTION_MOVE + 5), nullptr)));
|
||||||
|
// Non-tank trigger to stack. Avoiding the spikes at range is.. harder than it seems.
|
||||||
|
// TODO: This turns hunters into melee marshmallows, have not come up with a better solution yet
|
||||||
|
triggers.push_back(new TriggerNode("ormorok stack",
|
||||||
|
NextAction::array(0, new NextAction("dodge spikes", ACTION_MOVE + 5), nullptr)));
|
||||||
|
// TODO: Add handling for spell reflect... best to spam low level/weak spells but don't want
|
||||||
|
// to hardcode spells per class, might be difficult to dynamically generate this.
|
||||||
|
// Will revisit if I find my altbots killing themselves in heroic, just heal through it for now
|
||||||
|
|
||||||
|
// Keristrasza
|
||||||
|
triggers.push_back(new TriggerNode("intense cold",
|
||||||
|
NextAction::array(0, new NextAction("intense cold jump", ACTION_MOVE + 5), nullptr)));
|
||||||
|
// Flank dragon positioning for non-tank melee
|
||||||
|
triggers.push_back(new TriggerNode("dragon positioning",
|
||||||
|
NextAction::array(0, new NextAction("rear flank position", ACTION_MOVE + 4), nullptr)));
|
||||||
|
// TODO: Add frost resist aura for paladins?
|
||||||
|
}
|
||||||
|
|
||||||
|
void WotlkDungeonNexStrategy::InitMultipliers(std::vector<Multiplier*> &multipliers)
|
||||||
|
{
|
||||||
|
multipliers.push_back(new FactionCommanderMultiplier(botAI));
|
||||||
|
multipliers.push_back(new TelestraMultiplier(botAI));
|
||||||
|
multipliers.push_back(new AnomalusMultiplier(botAI));
|
||||||
|
multipliers.push_back(new OrmorokMultiplier(botAI));
|
||||||
|
// multipliers.push_back(new KeristraszaMultiplier(botAI));
|
||||||
|
}
|
||||||
18
src/strategy/dungeons/wotlk/nexus/NexusStrategy.h
Normal file
18
src/strategy/dungeons/wotlk/nexus/NexusStrategy.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#ifndef _PLAYERBOT_WOTLKDUNGEONNEXSTRATEGY_H
|
||||||
|
#define _PLAYERBOT_WOTLKDUNGEONNEXSTRATEGY_H
|
||||||
|
|
||||||
|
#include "Multiplier.h"
|
||||||
|
#include "AiObjectContext.h"
|
||||||
|
#include "Strategy.h"
|
||||||
|
|
||||||
|
|
||||||
|
class WotlkDungeonNexStrategy : public Strategy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WotlkDungeonNexStrategy(PlayerbotAI* ai) : Strategy(ai) {}
|
||||||
|
virtual std::string const getName() override { return "nexus"; }
|
||||||
|
virtual void InitTriggers(std::vector<TriggerNode*> &triggers) override;
|
||||||
|
virtual void InitMultipliers(std::vector<Multiplier*> &multipliers) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
33
src/strategy/dungeons/wotlk/nexus/NexusTriggerContext.h
Normal file
33
src/strategy/dungeons/wotlk/nexus/NexusTriggerContext.h
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#ifndef _PLAYERBOT_WOTLKDUNGEONNEXTRIGGERCONTEXT_H
|
||||||
|
#define _PLAYERBOT_WOTLKDUNGEONNEXTRIGGERCONTEXT_H
|
||||||
|
|
||||||
|
#include "NamedObjectContext.h"
|
||||||
|
#include "AiObjectContext.h"
|
||||||
|
#include "NexusTriggers.h"
|
||||||
|
|
||||||
|
class WotlkDungeonNexTriggerContext : public NamedObjectContext<Trigger>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WotlkDungeonNexTriggerContext()
|
||||||
|
{
|
||||||
|
creators["faction commander whirlwind"] = &WotlkDungeonNexTriggerContext::faction_commander_whirlwind;
|
||||||
|
creators["telestra firebomb"] = &WotlkDungeonNexTriggerContext::telestra_firebomb;
|
||||||
|
creators["telestra split phase"] = &WotlkDungeonNexTriggerContext::telestra_split_phase;
|
||||||
|
creators["chaotic rift"] = &WotlkDungeonNexTriggerContext::chaotic_rift;
|
||||||
|
creators["ormorok spikes"] = &WotlkDungeonNexTriggerContext::ormorok_spikes;
|
||||||
|
creators["ormorok stack"] = &WotlkDungeonNexTriggerContext::ormorok_stack;
|
||||||
|
creators["intense cold"] = &WotlkDungeonNexTriggerContext::intense_cold;
|
||||||
|
creators["dragon positioning"] = &WotlkDungeonNexTriggerContext::dragon_positioning;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
static Trigger* faction_commander_whirlwind(PlayerbotAI* ai) { return new FactionCommanderWhirlwindTrigger(ai); }
|
||||||
|
static Trigger* telestra_firebomb(PlayerbotAI* ai) { return new TelestraFirebombTrigger(ai); }
|
||||||
|
static Trigger* telestra_split_phase(PlayerbotAI* ai) { return new TelestraSplitPhaseTrigger(ai); }
|
||||||
|
static Trigger* chaotic_rift(PlayerbotAI* ai) { return new ChaoticRiftTrigger(ai); }
|
||||||
|
static Trigger* ormorok_spikes(PlayerbotAI* ai) { return new OrmorokSpikesTrigger(ai); }
|
||||||
|
static Trigger* ormorok_stack(PlayerbotAI* ai) { return new OrmorokStackTrigger(ai); }
|
||||||
|
static Trigger* intense_cold(PlayerbotAI* ai) { return new IntenseColdTrigger(ai); }
|
||||||
|
static Trigger* dragon_positioning(PlayerbotAI* ai) { return new DragonPositioningTrigger(ai); }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
105
src/strategy/dungeons/wotlk/nexus/NexusTriggers.cpp
Normal file
105
src/strategy/dungeons/wotlk/nexus/NexusTriggers.cpp
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
#include "Playerbots.h"
|
||||||
|
#include "NexusTriggers.h"
|
||||||
|
#include "AiObject.h"
|
||||||
|
#include "AiObjectContext.h"
|
||||||
|
|
||||||
|
bool FactionCommanderWhirlwindTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* boss = nullptr;
|
||||||
|
uint8 faction = bot->GetTeamId();
|
||||||
|
|
||||||
|
switch (bot->GetMap()->GetDifficulty())
|
||||||
|
{
|
||||||
|
case DUNGEON_DIFFICULTY_NORMAL:
|
||||||
|
if (faction == TEAM_ALLIANCE)
|
||||||
|
{
|
||||||
|
boss = AI_VALUE2(Unit*, "find target", "horde commander");
|
||||||
|
}
|
||||||
|
else //if (faction == TEAM_HORDE)
|
||||||
|
{
|
||||||
|
boss = AI_VALUE2(Unit*, "find target", "alliance commander");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DUNGEON_DIFFICULTY_HEROIC:
|
||||||
|
if (faction == TEAM_ALLIANCE)
|
||||||
|
{
|
||||||
|
boss = AI_VALUE2(Unit*, "find target", "commander kolurg");
|
||||||
|
}
|
||||||
|
else //if (faction == TEAM_HORDE)
|
||||||
|
{
|
||||||
|
boss = AI_VALUE2(Unit*, "find target", "commander stoutbeard");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (boss && boss->HasUnitState(UNIT_STATE_CASTING))
|
||||||
|
{
|
||||||
|
if (boss->FindCurrentSpellBySpellId(SPELL_WHIRLWIND))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TelestraFirebombTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (botAI->IsMelee(bot)) { return false; }
|
||||||
|
|
||||||
|
Unit* boss = AI_VALUE2(Unit*, "find target", "grand magus telestra");
|
||||||
|
// Avoid split phase with the fake Telestra units, only match the true boss id
|
||||||
|
return boss && boss->GetEntry() == NPC_TELESTRA;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TelestraSplitPhaseTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* boss = AI_VALUE2(Unit*, "find target", "grand magus telestra");
|
||||||
|
// Only match split phase with the fake Telestra units
|
||||||
|
return boss && boss->GetEntry() != NPC_TELESTRA;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChaoticRiftTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* boss = AI_VALUE2(Unit*, "find target", "anomalus");
|
||||||
|
return boss && boss->HasAura(BUFF_RIFT_SHIELD);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OrmorokSpikesTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* boss = AI_VALUE2(Unit*, "find target", "ormorok the tree-shaper");
|
||||||
|
if (!boss || !botAI->IsTank(bot)) { return false; }
|
||||||
|
|
||||||
|
GuidVector objects = AI_VALUE(GuidVector, "closest game objects");
|
||||||
|
for (auto i = objects.begin(); i != objects.end(); ++i)
|
||||||
|
{
|
||||||
|
GameObject* go = botAI->GetGameObject(*i);
|
||||||
|
if (go && go->GetEntry() == GO_CRYSTAL_SPIKE)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OrmorokStackTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* boss = AI_VALUE2(Unit*, "find target", "ormorok the tree-shaper");
|
||||||
|
return (boss && !botAI->IsTank(bot));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IntenseColdTrigger::IsActive()
|
||||||
|
{
|
||||||
|
// Adjust as needed - too much interrupting loses dps time,
|
||||||
|
// but too many stacks is deadly. Assuming 3-5 is a good number to clear
|
||||||
|
int stackThreshold = 5;
|
||||||
|
Unit* boss = AI_VALUE2(Unit*, "find target", "keristrasza");
|
||||||
|
return boss && botAI->GetAura("intense cold", bot, false, false, stackThreshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DragonPositioningTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* boss = AI_VALUE2(Unit*, "find target", "keristrasza");
|
||||||
|
return boss && botAI->IsMelee(bot) && !botAI->IsTank(bot);
|
||||||
|
}
|
||||||
@@ -1,93 +1,88 @@
|
|||||||
#ifndef _PLAYERBOT_WOTLKDUNGEONNEXTRIGGERS_H
|
#ifndef _PLAYERBOT_WOTLKDUNGEONNEXTRIGGERS_H
|
||||||
#define _PLAYERBOT_WOTLKDUNGEONNEXTRIGGERS_H
|
#define _PLAYERBOT_WOTLKDUNGEONNEXTRIGGERS_H
|
||||||
|
|
||||||
#include "EventMap.h"
|
|
||||||
#include "Trigger.h"
|
#include "Trigger.h"
|
||||||
#include "PlayerbotAIConfig.h"
|
#include "PlayerbotAIConfig.h"
|
||||||
#include "GenericTriggers.h"
|
#include "GenericTriggers.h"
|
||||||
#include "DungeonStrategyUtils.h"
|
#include "DungeonStrategyUtils.h"
|
||||||
|
|
||||||
// Taken from:
|
enum NexusIDs
|
||||||
// src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_ingvar_the_plunderer.cpp
|
|
||||||
enum Spells
|
|
||||||
{
|
{
|
||||||
SPELL_SUMMON_VALKYR = 42912,
|
// Faction Commander
|
||||||
SPELL_RESURRECTION_BEAM = 42857,
|
NPC_ALLIANCE_COMMANDER = 27949,
|
||||||
SPELL_RESURRECTION_BALL = 42862,
|
NPC_HORDE_COMMANDER = 27947,
|
||||||
SPELL_RESURRECTION_HEAL = 42704,
|
NPC_COMMANDER_STOUTBEARD = 26796,
|
||||||
SPELL_INGVAR_TRANSFORM = 42796,
|
NPC_COMMANDER_KOLURG = 26798,
|
||||||
|
// SPELL_FRIGHTENING_SHOUT = 19134,
|
||||||
|
SPELL_WHIRLWIND = 38618,
|
||||||
|
|
||||||
SPELL_STAGGERING_ROAR_N = 42708,
|
// Grand Magus Telestra
|
||||||
SPELL_STAGGERING_ROAR_H = 59708,
|
NPC_TELESTRA = 26731,
|
||||||
SPELL_CLEAVE = 42724,
|
NPC_FIRE_MAGUS = 26928,
|
||||||
SPELL_SMASH_N = 42669,
|
NPC_FROST_MAGUS = 26930,
|
||||||
SPELL_SMASH_H = 59706,
|
NPC_ARCANE_MAGUS = 26929,
|
||||||
SPELL_ENRAGE_N = 42705,
|
|
||||||
SPELL_ENRAGE_H = 59707,
|
|
||||||
|
|
||||||
SPELL_DREADFUL_ROAR_N = 42729,
|
// Anomalus
|
||||||
SPELL_DREADFUL_ROAR_H = 59734,
|
BUFF_RIFT_SHIELD = 47748,
|
||||||
SPELL_WOE_STRIKE_N = 42730,
|
|
||||||
SPELL_WOE_STRIKE_H = 59735,
|
|
||||||
SPELL_DARK_SMASH = 42723,
|
|
||||||
SPELL_SHADOW_AXE = 42749,
|
|
||||||
|
|
||||||
// Added
|
// Ormorok the Tree Shaper
|
||||||
DEBUFF_FROST_TOMB = 48400,
|
// NPC_CRYSTAL_SPIKE = 27099,
|
||||||
|
GO_CRYSTAL_SPIKE = 188537,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define SPELL_STAGGERING_ROAR DUNGEON_MODE(bot, SPELL_STAGGERING_ROAR_N, SPELL_STAGGERING_ROAR_H)
|
class FactionCommanderWhirlwindTrigger : public Trigger
|
||||||
#define SPELL_DREADFUL_ROAR DUNGEON_MODE(bot, SPELL_DREADFUL_ROAR_N, SPELL_DREADFUL_ROAR_H)
|
|
||||||
#define SPELL_WOE_STRIKE DUNGEON_MODE(bot, SPELL_WOE_STRIKE_N, SPELL_WOE_STRIKE_H)
|
|
||||||
#define SPELL_SMASH DUNGEON_MODE(bot, SPELL_SMASH_N, SPELL_SMASH_H)
|
|
||||||
#define SPELL_ENRAGE DUNGEON_MODE(bot, SPELL_ENRAGE_N, SPELL_ENRAGE_H)
|
|
||||||
|
|
||||||
class KelesethFrostTombTrigger : public Trigger
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
KelesethFrostTombTrigger(PlayerbotAI* ai) : Trigger(ai, "keleseth frost tomb") {}
|
FactionCommanderWhirlwindTrigger(PlayerbotAI* ai) : Trigger(ai, "faction commander whirlwind") {}
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DalronnNontankTrigger : public Trigger
|
class TelestraFirebombTrigger : public Trigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DalronnNontankTrigger(PlayerbotAI* ai) : Trigger(ai, "dalronn non-tank") {}
|
TelestraFirebombTrigger(PlayerbotAI* ai) : Trigger(ai, "telestra firebomb spread") {}
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class IngvarStaggeringRoarTrigger : public Trigger
|
class TelestraSplitPhaseTrigger : public Trigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
IngvarStaggeringRoarTrigger(PlayerbotAI* ai) : Trigger(ai, "ingvar staggering roar") {}
|
TelestraSplitPhaseTrigger(PlayerbotAI* ai) : Trigger(ai, "telestra split phase") {}
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class IngvarDreadfulRoarTrigger : public Trigger
|
class ChaoticRiftTrigger : public Trigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
IngvarDreadfulRoarTrigger(PlayerbotAI* ai) : Trigger(ai, "ingvar dreadful roar") {}
|
ChaoticRiftTrigger(PlayerbotAI* ai) : Trigger(ai, "chaotic rift") {}
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class IngvarSmashTankTrigger : public Trigger
|
class OrmorokSpikesTrigger : public Trigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
IngvarSmashTankTrigger(PlayerbotAI* ai) : Trigger(ai, "ingvar smash tank") {}
|
OrmorokSpikesTrigger(PlayerbotAI* ai) : Trigger(ai, "ormorok spikes") {}
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class IngvarSmashTankReturnTrigger : public Trigger
|
class OrmorokStackTrigger : public Trigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
IngvarSmashTankReturnTrigger(PlayerbotAI* ai) : Trigger(ai, "ingvar smash tank return") {}
|
OrmorokStackTrigger(PlayerbotAI* ai) : Trigger(ai, "ormorok stack") {}
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class NotBehindIngvarTrigger : public Trigger
|
class IntenseColdTrigger : public Trigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
NotBehindIngvarTrigger(PlayerbotAI* ai) : Trigger(ai, "not behind ingvar") {}
|
IntenseColdTrigger(PlayerbotAI* ai) : Trigger(ai, "intense cold") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DragonPositioningTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DragonPositioningTrigger(PlayerbotAI* ai) : Trigger(ai, "dragon positioning") {}
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user