mirror of
https://github.com/mod-playerbots/mod-playerbots
synced 2025-11-29 15:58:20 +08:00
Merge pull request #578 from Bobblybook/master
The Nexus implementation, UK code cleanup, dragon flanking
This commit is contained in:
@@ -51,6 +51,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
|
||||
actionContexts.Add(new RaidUlduarActionContext());
|
||||
actionContexts.Add(new RaidIccActionContext());
|
||||
actionContexts.Add(new WotlkDungeonUKActionContext());
|
||||
actionContexts.Add(new WotlkDungeonNexActionContext());
|
||||
|
||||
triggerContexts.Add(new TriggerContext());
|
||||
triggerContexts.Add(new ChatTriggerContext());
|
||||
@@ -62,6 +63,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
|
||||
triggerContexts.Add(new RaidUlduarTriggerContext());
|
||||
triggerContexts.Add(new RaidIccTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonUKTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonNexTriggerContext());
|
||||
|
||||
valueContexts.Add(new ValueContext());
|
||||
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
#ifndef _PLAYERBOT_DUNGEONSTRATEGYCONTEXT_H_
|
||||
#define _PLAYERBOT_DUNGEONSTRATEGYCONTEXT_H_
|
||||
#ifndef _PLAYERBOT_DUNGEONSTRATEGYCONTEXT_H
|
||||
#define _PLAYERBOT_DUNGEONSTRATEGYCONTEXT_H
|
||||
|
||||
#include "Strategy.h"
|
||||
#include "wotlk/utgardekeep/UtgardeKeepStrategy.h"
|
||||
#include "wotlk/nexus/NexusStrategy.h"
|
||||
|
||||
/*
|
||||
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
|
||||
Krik'thir the Gatewatcher, Hadronox, Anub'arak
|
||||
Ahn'kahet: The Old Kingdom - OK
|
||||
@@ -76,7 +75,7 @@ class DungeonStrategyContext : public NamedObjectContext<Strategy>
|
||||
}
|
||||
private:
|
||||
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_ok(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); }
|
||||
static Strategy* wotlk_dtk(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef _PLAYERBOT_DUNGEONUTILS_H_
|
||||
#define _PLAYERBOT_DUNGEONUTILS_H_
|
||||
#ifndef _PLAYERBOT_DUNGEONUTILS_H
|
||||
#define _PLAYERBOT_DUNGEONUTILS_H
|
||||
|
||||
|
||||
template<class T> inline
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONACTIONCONTEXT_H_
|
||||
#define _PLAYERBOT_WOTLKDUNGEONACTIONCONTEXT_H_
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONACTIONCONTEXT_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONACTIONCONTEXT_H
|
||||
|
||||
#include "utgardekeep/UtgardeKeepActionContext.h"
|
||||
// #include "nexus/NexusActionContext.h"
|
||||
#include "nexus/NexusActionContext.h"
|
||||
// #include "azjolnerub/AzjolNerubActionContext.h"
|
||||
// #include "oldkingdom/OldKingdomActionContext.h"
|
||||
// #include "draktharonkeep/DraktharonKeepActionContext.h"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONTRIGGERCONTEXT_H_
|
||||
#define _PLAYERBOT_WOTLKDUNGEONTRIGGERCONTEXT_H_
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONTRIGGERCONTEXT_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONTRIGGERCONTEXT_H
|
||||
|
||||
#include "utgardekeep/UtgardeKeepTriggerContext.h"
|
||||
// #include "nexus/NexusTriggerContext.h"
|
||||
#include "nexus/NexusTriggerContext.h"
|
||||
// #include "azjolnerub/AzjolNerubTriggerContext.h"
|
||||
// #include "oldkingdom/OldKingdomTriggerContext.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 subtracting 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);
|
||||
}
|
||||
89
src/strategy/dungeons/wotlk/nexus/NexusTriggers.h
Normal file
89
src/strategy/dungeons/wotlk/nexus/NexusTriggers.h
Normal file
@@ -0,0 +1,89 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONNEXTRIGGERS_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONNEXTRIGGERS_H
|
||||
|
||||
#include "Trigger.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "GenericTriggers.h"
|
||||
#include "DungeonStrategyUtils.h"
|
||||
|
||||
enum NexusIDs
|
||||
{
|
||||
// Faction Commander
|
||||
NPC_ALLIANCE_COMMANDER = 27949,
|
||||
NPC_HORDE_COMMANDER = 27947,
|
||||
NPC_COMMANDER_STOUTBEARD = 26796,
|
||||
NPC_COMMANDER_KOLURG = 26798,
|
||||
// SPELL_FRIGHTENING_SHOUT = 19134,
|
||||
SPELL_WHIRLWIND = 38618,
|
||||
|
||||
// Grand Magus Telestra
|
||||
NPC_TELESTRA = 26731,
|
||||
NPC_FIRE_MAGUS = 26928,
|
||||
NPC_FROST_MAGUS = 26930,
|
||||
NPC_ARCANE_MAGUS = 26929,
|
||||
|
||||
// Anomalus
|
||||
BUFF_RIFT_SHIELD = 47748,
|
||||
|
||||
// Ormorok the Tree Shaper
|
||||
// NPC_CRYSTAL_SPIKE = 27099,
|
||||
GO_CRYSTAL_SPIKE = 188537,
|
||||
};
|
||||
|
||||
class FactionCommanderWhirlwindTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
FactionCommanderWhirlwindTrigger(PlayerbotAI* ai) : Trigger(ai, "faction commander whirlwind") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class TelestraFirebombTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
TelestraFirebombTrigger(PlayerbotAI* ai) : Trigger(ai, "telestra firebomb spread") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class TelestraSplitPhaseTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
TelestraSplitPhaseTrigger(PlayerbotAI* ai) : Trigger(ai, "telestra split phase") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class ChaoticRiftTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
ChaoticRiftTrigger(PlayerbotAI* ai) : Trigger(ai, "chaotic rift") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class OrmorokSpikesTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
OrmorokSpikesTrigger(PlayerbotAI* ai) : Trigger(ai, "ormorok spikes") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class OrmorokStackTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
OrmorokStackTrigger(PlayerbotAI* ai) : Trigger(ai, "ormorok stack") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IntenseColdTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
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;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "UtgardeKeepActions.h"
|
||||
#include "UtgardeKeepStrategy.h"
|
||||
|
||||
bool AttackFrostTombAction::isUseful() { return !botAI->IsHeal(bot); }
|
||||
bool AttackFrostTombAction::Execute(Event event)
|
||||
{
|
||||
Unit* frostTomb = nullptr;
|
||||
|
||||
@@ -12,6 +12,7 @@ class AttackFrostTombAction : public AttackAction
|
||||
public:
|
||||
AttackFrostTombAction(PlayerbotAI* ai) : AttackAction(ai, "attack frost tomb") {}
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
class AttackDalronnAction : public AttackAction
|
||||
@@ -32,16 +33,16 @@ class IngvarDodgeSmashAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
IngvarDodgeSmashAction(PlayerbotAI* ai) : MovementAction(ai, "ingvar dodge smash") {}
|
||||
bool isUseful() override;
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
class IngvarSmashReturnAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
IngvarSmashReturnAction(PlayerbotAI* ai) : MovementAction(ai, "ingvar smash return") {}
|
||||
bool isUseful() override;
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
float PrinceKelesethMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "prince keleseth");
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "prince keleseth");
|
||||
if (!boss)
|
||||
{
|
||||
return 1.0f;
|
||||
@@ -15,7 +15,7 @@ float PrinceKelesethMultiplier::GetValue(Action* action)
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
return 1.0f;
|
||||
return 1.0f;
|
||||
}
|
||||
float SkarvaldAndDalronnMultiplier::GetValue(Action* action)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef _PLAYERRBOT_WOTLKDUNGEONUKMULTIPLIERS_H_
|
||||
#define _PLAYERRBOT_WOTLKDUNGEONUKMULTIPLIERS_H_
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONUKMULTIPLIERS_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONUKMULTIPLIERS_H
|
||||
|
||||
#include "Multiplier.h"
|
||||
|
||||
|
||||
@@ -4,40 +4,40 @@
|
||||
|
||||
void WotlkDungeonUKStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
|
||||
{
|
||||
// Prince Keleseth
|
||||
triggers.push_back(new TriggerNode("keleseth frost tomb",
|
||||
// Prince Keleseth
|
||||
triggers.push_back(new TriggerNode("keleseth frost tomb",
|
||||
NextAction::array(0, new NextAction("attack frost tomb", ACTION_RAID + 1), nullptr)));
|
||||
|
||||
// Skarvald the Constructor & Dalronn the Controller
|
||||
triggers.push_back(new TriggerNode("dalronn priority",
|
||||
|
||||
// Skarvald the Constructor & Dalronn the Controller
|
||||
triggers.push_back(new TriggerNode("dalronn priority",
|
||||
NextAction::array(0, new NextAction("attack dalronn", ACTION_RAID + 1), nullptr)));
|
||||
|
||||
// Ingvar the Plunderer
|
||||
|
||||
// Ingvar the Plunderer
|
||||
|
||||
// Doesn't work yet, this action doesn't get processed until the existing cast finishes
|
||||
// triggers.push_back(new TriggerNode("ingvar staggering roar",
|
||||
// Doesn't work yet, this action doesn't get processed until the existing cast finishes
|
||||
// triggers.push_back(new TriggerNode("ingvar staggering roar",
|
||||
// NextAction::array(0, new NextAction("ingvar stop casting", ACTION_RAID + 1), nullptr)));
|
||||
|
||||
// No easy way to check LoS here, the pillars do not seem to count as gameobjects.
|
||||
// Not implemented for now, unsure if this is needed as a good group can probably burst through the boss
|
||||
// and just eat the debuff.
|
||||
// triggers.push_back(new TriggerNode("ingvar dreadful roar",
|
||||
// No easy way to check LoS here, the pillars do not seem to count as gameobjects.
|
||||
// Not implemented for now, unsure if this is needed as a good group can probably burst through the boss
|
||||
// and just eat the debuff.
|
||||
// triggers.push_back(new TriggerNode("ingvar dreadful roar",
|
||||
// NextAction::array(0, new NextAction("ingvar hide los", ACTION_RAID + 1), nullptr)));
|
||||
triggers.push_back(new TriggerNode("ingvar smash tank",
|
||||
triggers.push_back(new TriggerNode("ingvar smash tank",
|
||||
NextAction::array(0, new NextAction("ingvar dodge smash", ACTION_MOVE + 5), nullptr)));
|
||||
triggers.push_back(new TriggerNode("ingvar smash tank return",
|
||||
triggers.push_back(new TriggerNode("ingvar smash tank return",
|
||||
NextAction::array(0, new NextAction("ingvar smash return", ACTION_MOVE + 5), nullptr)));
|
||||
// Buggy... if not behind target, ai can get stuck running towards and away from target.
|
||||
// I think for ranged chars, a custom action should be added that doesn't attempt to run into melee.
|
||||
// This is a bandaid for now, needs to be improved.
|
||||
triggers.push_back(new TriggerNode("not behind ingvar",
|
||||
// Buggy... if not behind target, ai can get stuck running towards and away from target.
|
||||
// I think for ranged chars, a custom action should be added that doesn't attempt to run into melee.
|
||||
// This is a bandaid for now, needs to be improved.
|
||||
triggers.push_back(new TriggerNode("not behind ingvar",
|
||||
NextAction::array(0, new NextAction("set behind", ACTION_MOVE + 1), nullptr)));
|
||||
|
||||
}
|
||||
|
||||
void WotlkDungeonUKStrategy::InitMultipliers(std::vector<Multiplier*> &multipliers)
|
||||
{
|
||||
multipliers.push_back(new PrinceKelesethMultiplier(botAI));
|
||||
multipliers.push_back(new SkarvaldAndDalronnMultiplier(botAI));
|
||||
multipliers.push_back(new IngvarThePlundererMultiplier(botAI));
|
||||
multipliers.push_back(new PrinceKelesethMultiplier(botAI));
|
||||
multipliers.push_back(new SkarvaldAndDalronnMultiplier(botAI));
|
||||
multipliers.push_back(new IngvarThePlundererMultiplier(botAI));
|
||||
}
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONUKTRIGGERS_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONUKTRIGGERS_H
|
||||
|
||||
#include "EventMap.h"
|
||||
#include "Trigger.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "GenericTriggers.h"
|
||||
#include "DungeonStrategyUtils.h"
|
||||
|
||||
// Taken from:
|
||||
// src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_ingvar_the_plunderer.cpp
|
||||
enum eSpells
|
||||
enum UtgardeKeepIDs
|
||||
{
|
||||
SPELL_SUMMON_VALKYR = 42912,
|
||||
SPELL_RESURRECTION_BEAM = 42857,
|
||||
@@ -32,7 +29,6 @@ enum eSpells
|
||||
SPELL_DARK_SMASH = 42723,
|
||||
SPELL_SHADOW_AXE = 42749,
|
||||
|
||||
// Added
|
||||
DEBUFF_FROST_TOMB = 48400,
|
||||
};
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ bool FingersOfFrostSingleTrigger::IsActive()
|
||||
{
|
||||
// Fingers of Frost "stack" count is always 1.
|
||||
// The value is instead stored in the charges.
|
||||
Aura* aura = botAI->GetAura(getName(), GetTarget(), false, true, -1);
|
||||
Aura* aura = botAI->GetAura("fingers of frost", bot, false, true, -1);
|
||||
return (aura && aura->GetCharges() == 1);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user