mirror of
https://github.com/mod-playerbots/mod-playerbots.git
synced 2026-02-09 05:31:10 +00:00
Implement Serpentshrine Cavern Strategies (#1888)
Edited: Below description of methods were brought up to date as of the PR coming off of draft. ### General I've starting leveraging, to the extent possible, an out-of-combat method to erase map keys. This is mostly useful for timers that need to start upon the pull because I dislike having to rely on a check for a boss to be at 100% HP (or 99.9% or whatever) because it can be unreliable sometimes. ### Trash Underbog Colossi: Some Colossi leave behind a lake of toxin when they die that quickly kills any player that is standing in it. The pool is a dynamic-object-generated AoE, and bots will not avoid it on their own (I think because the AoE is out of combat, plus the radius is much larger than the default avoidance radius in the config). The method does not require bots to be in combat, and simply gets bots to run out of the toxin. You will probably still get a couple of idiots who drink in the middle of it, but in my experience, the vast majority of the raid gets out, and healers that escape can easily keep up a couple of fools until they've drank to full. Greyheart Tidecallers: Bots will mark and destroy Water Elemental Totems immediately. ### Hydross the Unstable The strategy uses 2 tanks, with the main tank assigned to the frost phase and the 1st assistant tank assigned to the nature phase. - The main tank will tank the frost phase, and the first assistant tank will tank the nature phase. They each have designated spots and will wait at their spots twiddling their thumbs while Hydross is in the other phase. - Hunters will misdirect to the applicable tank upon the pull and after each phase change. - The phase change process begins 1 second after Hydross reaches 100% Marks. The current tank will begin moving to the next phase tank's spot for the next tank to take over as soon as Hydross transitions. - DPS is ordered to stop after Hydross reaches 100% Marks until 5 seconds after he transitions. - Bots will prioritize the elementals adds after every phase change, unless Hydross is under 10% HP, in which case they should ignore the adds and burn the boss. - Ranged bots should spread during the frost phase to mitigate the impact of Water Tombs. ### The Lurker Below - There is a designated spot for the main tank. - Ranged DPS will fan out over a 120-degree arc that is centered directly across from the tank spot (to try to spread to reduce Geyser damage while also keeping them behind Lurker). - When Spout begins, all bots will run around behind Lurker. The intent is to keep a distance with a radius of 20 or 21 yards and within 45 degrees (either side) of directly behind him. Movement is specifically tangential along an arc so bots don't run in front of Lurker. - Spout's duration is tracked by a timer. The mechanics of the spell itself are rather unique and don't involve a continuous cast or aura to track easily so I settled for the timer. - If you have 3 (or more) tanks, each of the first 3 tanks will be assigned to one of the 3 Coilfang Guardians during the submerge phase. ### Leotheras the Blind The fight is designed for a Warlock tank. You can choose the Warlock tank by giving a Warlock the Assistant flag. If you don't do that, your highest HP Warlock will be picked. Do NOT switch the Warlock tank to a co +tank strategy--the designated Warlock is hardcoded to spam Searing Pain on Demon Leo and otherwise will engage in normal DPS strategies. If you don't have a Warlock at all, the strategy has some methods built in to try to make things work as best as possible with a melee tank. - The Spellbinders get marked with skulls and killed in order. - There is no designated spot or designated tank for the human phase. Your tanks will fight for aggro. Ranged bots will attempt to keep some distance, and when Whirlwind starts, everybody will run away from Leotheras. - During the demon phase, your melee tanks should take a backseat to your Warlock tank, who will receive help in the form of Misdirection. Bots will get the hell away from the Warlock tank so the Warlock tank should be taking every Chaos Blast alone. - During the final phase, your regular tanks will tank Leotheras, and the Warlock tank will tank his Shadow. The melee tanks will attempt to separate Leotheras from his Shadow so bots can focus down Leotheras without getting hit with Chaos Blasts. - Bots will wait 5 seconds to DPS after every transition into human phase, 12 seconds to DPS after every transition into demon phase, and 8 seconds to DPS after the transition into the final phase. There is no waiting on DPS after Whirlwinds, even though it would be ideal. It's not a big deal to live without, and for various reasons, it would have been a pain in the ass to deal with. - Bots will save Bloodlust/Heroism until after Spellbinders are down. - To deal with the Inner Demons, I disabled DPS assist for bots who are targeted and force them to focus only on their Inner Demons. This is sufficient in my experience for all DPS bots and Protection Warriors and Paladins to kill their Inner Demons, even at 50% damage. Feral Tank Druids and Healers still need help, so the strategy hardcodes their actions while fighting Inner Demons. For example, Resto Druids are coded to shift out of Tree Form, cast Barkskin on themselves, and just spam Wrath until the Inner Demon is dead. There are no bot strategy changes used for this method. ### Fathom-Lord Karathress You will need 4 tanks. Your main tank will tank Karathress, and an assistant tank will tank each Fathom Guard. If you have fewer than 4 tanks, then the priority order for tank assignment will be Karathress, Caribdis, Sharkkis, and then Tidalvess. - Roughly, the tank spots are (1) for Karathress, near where he starts but closer to the ledge for LoS reasons, (2) for Sharkkis, North from his starting location on the other side of the ramp, (3) for Tidalvess, Northwest from his starting location near the pillar, and (4) for Caribdis, far to the West of her starting position, near the corner. - Note that the tanks will probably clip through the terrain a bit when going to their positions. This is due to me implementing a forced MoveTo to the tank position coordinates. There is something weird about the maps in Karathress's room, and the tanks will take some really screwed up paths without making them go directly to the exact coordinates. So this looks stupid but is necessary. - One healer will be assigned to heal the Caribdis tank. Because AC Playerbots does not yet have a focus heal strategy, this just means that such healer has a designated location near the Caribdis tank's location. This healer can be selected with the Assistant flag. - Hunters will misdirect the Fathom Guards onto their applicable tanks. If you don't have three Hunters, the priority is Caribdis, Tidalvess, then Sharkkis. - DPS will wait 12 seconds to begin attacking. After that, they will prioritize targets as follows: - (1): Melee will always prioritize Spitfire Totems as soon as they spawn. This will continue through the duration of the fight. - (2): All bots will kill Tidalvess first. - (3): Melee bots will move to Sharkkis, and ranged bots will move to Caribdis. I understand this is not the standard kill order for players, which would have the entire raid kill Sharkkis next. The reasons I have done this differently are because melee DPS is much stronger with 3.3.5 talents vs. in retail TBC, and because bots get really thrown off by Cyclones and therefore they struggle to kill Caribdis quickly. You do not want Karathress below 75% HP before all Fathom-Guards are dead or he gets a huge damage buff. - (4) If Caribdis dies first, ranged bots will help with Sharkkis. - (5) Everybody kills Sharkkis's pet. - (6) Everybody kills Karathress. ### Morogrim Tidewalker - The main tank will pull the boss to the Northeast pillar, with the tank's back against the pillar. - A hunter will misdirect the boss onto the main tank upon the pull. - When the boss gets to 26% HP, the main tank will begin moving the boss to the Northeast corner of the room in preparation for Phase 2 (which begins at 25%). The tank will move in two steps to get around the pillar. - When the boss gets to 25% HP, ranged will follow the main tank to the corner and stack up right behind the boss. They will also move in two steps. - There is no method for melee since they will just naturally follow the boss anyway. ### Lady Vashj **Phase 1**: - The main tank will tank Vashj in the center of the arena. - If a Shaman is in the main tank's group, that Shaman will attempt to keep a Grounding Totem down in range of the main tank to absorb Shock Blast. This should continue in Phase 3. - Ranged bots will spread out in a semicircle around the center of the arena. - If any bot other than the main tank gets Static Charge, it will run away from other bots. If the main tank gets Static Charge, other bots will run away from the main tank. This method should continue in Phase 3. - If any bot is Entangled and has Static Charge, the bot will attempt to use Cloak of Shadows if it is a Rogue, and Paladins will attempt to use Hand of Freedom. This method should continue in Phase 3 (with some modifications). - Bots will not use Bloodlust or Heroism (saved for Phase 3). Bots will not use any other major cooldowns, either, such as Metamorphosis (saved for Phase 2 and 3). **Phase 2**: There are two central mechanics to this phase, both of which were challenging to get bots to execute properly. First is the system of prioritizing adds. The large playing field and multiple types of adds coming from random directions make this phase not doable with realistic DPS under the standard Playerbots target selection system. Therefore, I took inspiration from liyunfan's Naxx strategy for Phase 1 of Kel'Thuzad to disable dps assist and create a custom target selection system. First, a cheat with respect to the Coilfang Striders: - Tanks will permanently have the Fear Ward aura applied to them if you have raid cheats enabled. This allows them to tank the Coilfang Striders. The standard strategy was to have an Elemental Shaman kite the Strider around the perimeter of the arena, with ranged players (including healers) spamming DoTs on the Strider. If you can make bots do this, then great, but it's far beyond my capabilities. Therefore, with the cheat, the first assistant tank is responsible for tanking Striders and keeping them away from Core passers (described below) and Vashj. Evidently it was (and is, in TBC Classic) possible to tank (and melee DPS) Striders by wearing a Dire Maul Ogre Suit, which would give you enough reach to stay out of the Strider's fear. I actually tried that, and it does not work, either because AC's radiuses are not the same or just because bots do not maintain the same level of precise positioning. But anyway, the point is that technically the Striders are tankable by real players, so maybe that will make you feel better about using this cheat (it's fine enough rationalizing for me). I found this fight to be unmanageable without this cheat (i.e., using a method that would only have bots try to run away from Striders) because each Strider was guaranteed to wipe out a couple of bots, and you really cannot afford to lose anyone. YMMV though. - If cheats are enabled for Striders, Hunters will attempt to Misdirect the Striders to the first assist tank. - If cheats are not enabled, bots will attempt to use slows/roots to stop the Striders. I have some logic for them to use Netherweave Nets, but I suspect it does not actually work so I may remove it instead of trying to get it to function properly. Target priority is as follows: - Hunters and Mages: Enchanted Elementals, Coilfang Striders, Coilfang Elites. - Other Ranged Bots: Elites, Striders, Elementals. - Melee DPS: Elementals, Elites. - Tanks: Elites, Elementals (except if cheats are enabled, the first assistant tank will instead prioritize Striders and then Elementals) - Everybody else (basically means healers): Elementals, Elites, Striders - If there is more than one of the same target, bots will prioritize the one that is closer to Vashj. - In all cases, the valid attack ranged is limited so that bots should not leave the central platform. - If somehow a bot ends up too far from the center of the room and is not actively attacking anything, there is logic to make them run back. Handling Tainted Elementals and the Tainted Core: I will make another post about this later. It is easily the most complicated strategy I've ever worked on (far beyond anything on Kael'thas even) so will necessitate a long explanation. The tl;dr is that there is a chain of two-to-four bots that receive/pass the Tainted Core before using it on a Shield Generator, and if you are playing by yourself, you probably need to turn raid cheats on, in which case there will also be a bot that teleports to, kills, and loots the Tainted Elementals (i.e., the bots will then handle the entire sequence of shutting down Shield Generators). **Phase 3**: - The main tank will pick up Vashj immediately and try to keep her away from Enchanted Elementals. - DPS will burn down residual adds from Phase 2 in the order of (1) elementals, (2) strider for ranged only (if you have more than one up, you're dead), and (3) elites (hopefully you have only one up, but two with one almost dead is possible). - Hunters will kill Toxic Sporebats. This works quite well, but they (and anybody else if ordered to target Sporebats) have a tendency to levitate up into the pipes at the top of the room when killing the Sporebats. To counteract this, a method forcibly teleports bots to the ground if they get more than 2 yards above the ground. - The Phase 1 Cloak of Shadows/Hand of Freedom method is now expanded to include bots Entangled in the Sporebat poison pools (with Hand of Freedom usage prioritized on the main tank). - There is a specific method to avoid the Sporebat poison pools. The Vashj tank will move backwards when avoiding poison. --------- Co-authored-by: kadeshar <kadeshar@gmail.com>
This commit is contained in:
@@ -558,7 +558,7 @@ AiPlayerbot.AutoGearScoreLimit = 0
|
||||
# "mana" (bots have infinite mana)
|
||||
# "power" (bots have infinite energy, rage, and runic power)
|
||||
# "taxi" (bots may use all flight paths, though they will not actually learn them)
|
||||
# "raid" (bots use cheats implemented into raid strategies (currently only for Ulduar))
|
||||
# "raid" (bots use cheats implemented into raid strategies (currently only for SSC and Ulduar))
|
||||
# To use multiple cheats, separate them by commas below (e.g., to enable all, use "gold,health,mana,power,raid,taxi")
|
||||
# Default: food, taxi, and raid are enabled
|
||||
AiPlayerbot.BotCheats = "food,taxi,raid"
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "RaidKarazhanStrategy.h"
|
||||
#include "RaidMagtheridonStrategy.h"
|
||||
#include "RaidGruulsLairStrategy.h"
|
||||
#include "RaidSSCStrategy.h"
|
||||
#include "RaidOsStrategy.h"
|
||||
#include "RaidEoEStrategy.h"
|
||||
#include "RaidVoAStrategy.h"
|
||||
@@ -26,6 +27,7 @@ public:
|
||||
creators["karazhan"] = &RaidStrategyContext::karazhan;
|
||||
creators["magtheridon"] = &RaidStrategyContext::magtheridon;
|
||||
creators["gruulslair"] = &RaidStrategyContext::gruulslair;
|
||||
creators["ssc"] = &RaidStrategyContext::ssc;
|
||||
creators["wotlk-os"] = &RaidStrategyContext::wotlk_os;
|
||||
creators["wotlk-eoe"] = &RaidStrategyContext::wotlk_eoe;
|
||||
creators["voa"] = &RaidStrategyContext::voa;
|
||||
@@ -41,6 +43,7 @@ private:
|
||||
static Strategy* karazhan(PlayerbotAI* botAI) { return new RaidKarazhanStrategy(botAI); }
|
||||
static Strategy* magtheridon(PlayerbotAI* botAI) { return new RaidMagtheridonStrategy(botAI); }
|
||||
static Strategy* gruulslair(PlayerbotAI* botAI) { return new RaidGruulsLairStrategy(botAI); }
|
||||
static Strategy* ssc(PlayerbotAI* botAI) { return new RaidSSCStrategy(botAI); }
|
||||
static Strategy* wotlk_os(PlayerbotAI* botAI) { return new RaidOsStrategy(botAI); }
|
||||
static Strategy* wotlk_eoe(PlayerbotAI* botAI) { return new RaidEoEStrategy(botAI); }
|
||||
static Strategy* voa(PlayerbotAI* botAI) { return new RaidVoAStrategy(botAI); }
|
||||
|
||||
3128
src/Ai/Raid/SerpentshrineCavern/Action/RaidSSCActions.cpp
Normal file
3128
src/Ai/Raid/SerpentshrineCavern/Action/RaidSSCActions.cpp
Normal file
File diff suppressed because it is too large
Load Diff
457
src/Ai/Raid/SerpentshrineCavern/Action/RaidSSCActions.h
Normal file
457
src/Ai/Raid/SerpentshrineCavern/Action/RaidSSCActions.h
Normal file
@@ -0,0 +1,457 @@
|
||||
#ifndef _PLAYERBOT_RAIDSSCACTIONS_H
|
||||
#define _PLAYERBOT_RAIDSSCACTIONS_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "AttackAction.h"
|
||||
#include "MovementActions.h"
|
||||
|
||||
// General
|
||||
|
||||
class SerpentShrineCavernEraseTimersAndTrackersAction : public Action
|
||||
{
|
||||
public:
|
||||
SerpentShrineCavernEraseTimersAndTrackersAction(
|
||||
PlayerbotAI* botAI, std::string const name = "serpent shrine cavern erase timers and trackers") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
// Trash
|
||||
|
||||
class UnderbogColossusEscapeToxicPoolAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
UnderbogColossusEscapeToxicPoolAction(
|
||||
PlayerbotAI* botAI, std::string const name = "underbog colossus escape toxic pool") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class GreyheartTidecallerMarkWaterElementalTotemAction : public Action
|
||||
{
|
||||
public:
|
||||
GreyheartTidecallerMarkWaterElementalTotemAction(
|
||||
PlayerbotAI* botAI, std::string const name = "greyheart tidecaller mark water elemental totem") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
// Hydross the Unstable <Duke of Currents>
|
||||
|
||||
class HydrossTheUnstablePositionFrostTankAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
HydrossTheUnstablePositionFrostTankAction(
|
||||
PlayerbotAI* botAI, std::string const name = "hydross the unstable position frost tank") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class HydrossTheUnstablePositionNatureTankAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
HydrossTheUnstablePositionNatureTankAction(
|
||||
PlayerbotAI* botAI, std::string const name = "hydross the unstable position nature tank") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class HydrossTheUnstablePrioritizeElementalAddsAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
HydrossTheUnstablePrioritizeElementalAddsAction(
|
||||
PlayerbotAI* botAI, std::string const name = "hydross the unstable prioritize elemental adds") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class HydrossTheUnstableFrostPhaseSpreadOutAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
HydrossTheUnstableFrostPhaseSpreadOutAction(
|
||||
PlayerbotAI* botAI, std::string const name = "hydross the unstable frost phase spread out") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class HydrossTheUnstableMisdirectBossToTankAction : public Action
|
||||
{
|
||||
public:
|
||||
HydrossTheUnstableMisdirectBossToTankAction(
|
||||
PlayerbotAI* botAI, std::string const name = "hydross the unstable misdirect boss to tank") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
private:
|
||||
bool TryMisdirectToFrostTank(Unit* hydross, Group* group);
|
||||
bool TryMisdirectToNatureTank(Unit* hydross, Group* group);
|
||||
};
|
||||
|
||||
class HydrossTheUnstableStopDpsUponPhaseChangeAction : public Action
|
||||
{
|
||||
public:
|
||||
HydrossTheUnstableStopDpsUponPhaseChangeAction(
|
||||
PlayerbotAI* botAI, std::string const name = "hydross the unstable stop dps upon phase change") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class HydrossTheUnstableManageTimersAction : public Action
|
||||
{
|
||||
public:
|
||||
HydrossTheUnstableManageTimersAction(
|
||||
PlayerbotAI* botAI, std::string const name = "hydross the unstable manage timers") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
// The Lurker Below
|
||||
|
||||
class TheLurkerBelowRunAroundBehindBossAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
TheLurkerBelowRunAroundBehindBossAction(
|
||||
PlayerbotAI* botAI, std::string const name = "the lurker below run around behind boss") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class TheLurkerBelowPositionMainTankAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
TheLurkerBelowPositionMainTankAction(
|
||||
PlayerbotAI* botAI, std::string const name = "the lurker below position main tank") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class TheLurkerBelowSpreadRangedInArcAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
TheLurkerBelowSpreadRangedInArcAction(
|
||||
PlayerbotAI* botAI, std::string const name = "the lurker below spread ranged in arc") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class TheLurkerBelowTanksPickUpAddsAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
TheLurkerBelowTanksPickUpAddsAction(
|
||||
PlayerbotAI* botAI, std::string const name = "the lurker below tanks pick up adds") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class TheLurkerBelowManageSpoutTimerAction : public Action
|
||||
{
|
||||
public:
|
||||
TheLurkerBelowManageSpoutTimerAction(
|
||||
PlayerbotAI* botAI, std::string const name = "the lurker below manage spout timer") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
// Leotheras the Blind
|
||||
|
||||
class LeotherasTheBlindTargetSpellbindersAction : public Action
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindTargetSpellbindersAction(
|
||||
PlayerbotAI* botAI, std::string const name = "leotheras the blind target spellbinders") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LeotherasTheBlindPositionRangedAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindPositionRangedAction(
|
||||
PlayerbotAI* botAI, std::string const name = "leotheras the blind position ranged") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LeotherasTheBlindDemonFormTankAttackBossAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindDemonFormTankAttackBossAction(
|
||||
PlayerbotAI* botAI, std::string const name = "leotheras the blind demon form tank attack boss") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LeotherasTheBlindMeleeTanksDontAttackDemonFormAction : public Action
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindMeleeTanksDontAttackDemonFormAction(
|
||||
PlayerbotAI* botAI, std::string const name = "leotheras the blind melee tanks don't attack demon form") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LeotherasTheBlindRunAwayFromWhirlwindAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindRunAwayFromWhirlwindAction(
|
||||
PlayerbotAI* botAI, std::string const name = "leotheras the blind run away from whirlwind") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LeotherasTheBlindMeleeDpsRunAwayFromBossAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindMeleeDpsRunAwayFromBossAction(
|
||||
PlayerbotAI* botAI, std::string const name = "leotheras the blind melee dps run away from boss") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LeotherasTheBlindDestroyInnerDemonAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindDestroyInnerDemonAction(
|
||||
PlayerbotAI* botAI, std::string const name = "leotheras the blind destroy inner demon") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
private:
|
||||
bool HandleFeralTankStrategy(Unit* innerDemon);
|
||||
bool HandleHealerStrategy(Unit* innerDemon);
|
||||
};
|
||||
|
||||
class LeotherasTheBlindFinalPhaseAssignDpsPriorityAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindFinalPhaseAssignDpsPriorityAction(
|
||||
PlayerbotAI* botAI, std::string const name = "leotheras the blind final phase assign dps priority") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LeotherasTheBlindMisdirectBossToDemonFormTankAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindMisdirectBossToDemonFormTankAction(
|
||||
PlayerbotAI* botAI, std::string const name = "leotheras the blind misdirect boss to demon form tank") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LeotherasTheBlindManageDpsWaitTimersAction : public Action
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindManageDpsWaitTimersAction(
|
||||
PlayerbotAI* botAI, std::string const name = "leotheras the blind manage dps wait timers") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
// Fathom-Lord Karathress
|
||||
|
||||
class FathomLordKarathressMainTankPositionBossAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressMainTankPositionBossAction(
|
||||
PlayerbotAI* botAI, std::string const name = "fathom-lord karathress main tank position boss") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class FathomLordKarathressFirstAssistTankPositionCaribdisAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressFirstAssistTankPositionCaribdisAction(
|
||||
PlayerbotAI* botAI, std::string const name = "fathom-lord karathress first assist tank position caribdis") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class FathomLordKarathressSecondAssistTankPositionSharkkisAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressSecondAssistTankPositionSharkkisAction(
|
||||
PlayerbotAI* botAI, std::string const name = "fathom-lord karathress second assist tank position sharkkis") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class FathomLordKarathressThirdAssistTankPositionTidalvessAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressThirdAssistTankPositionTidalvessAction(
|
||||
PlayerbotAI* botAI, std::string const name = "fathom-lord karathress third assist tank position tidalvess") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class FathomLordKarathressPositionCaribdisTankHealerAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressPositionCaribdisTankHealerAction(
|
||||
PlayerbotAI* botAI, std::string const name = "fathom-lord karathress position caribdis tank healer") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class FathomLordKarathressMisdirectBossesToTanksAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressMisdirectBossesToTanksAction(
|
||||
PlayerbotAI* botAI, std::string const name = "fathom-lord karathress misdirect bosses to tanks") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class FathomLordKarathressAssignDpsPriorityAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressAssignDpsPriorityAction(
|
||||
PlayerbotAI* botAI, std::string const name = "fathom-lord karathress assign dps priority") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class FathomLordKarathressManageDpsTimerAction : public Action
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressManageDpsTimerAction(
|
||||
PlayerbotAI* botAI, std::string const name = "fathom-lord karathress manage dps timer") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
// Morogrim Tidewalker
|
||||
|
||||
class MorogrimTidewalkerMisdirectBossToMainTankAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
MorogrimTidewalkerMisdirectBossToMainTankAction(
|
||||
PlayerbotAI* botAI, std::string const name = "morogrim tidewalker misdirect boss to main tank") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class MorogrimTidewalkerMoveBossToTankPositionAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
MorogrimTidewalkerMoveBossToTankPositionAction(
|
||||
PlayerbotAI* botAI, std::string const name = "morogrim tidewalker move boss to tank position") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
private:
|
||||
bool MoveToPhase1TankPosition(Unit* tidewalker);
|
||||
bool MoveToPhase2TankPosition(Unit* tidewalker);
|
||||
};
|
||||
|
||||
class MorogrimTidewalkerPhase2RepositionRangedAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
MorogrimTidewalkerPhase2RepositionRangedAction(
|
||||
PlayerbotAI* botAI, std::string const name = "morogrim tidewalker phase 2 reposition ranged") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
// Lady Vashj <Coilfang Matron>
|
||||
|
||||
class LadyVashjMainTankPositionBossAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
LadyVashjMainTankPositionBossAction(
|
||||
PlayerbotAI* botAI, std::string const name = "lady vashj main tank position boss") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LadyVashjPhase1SpreadRangedInArcAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
LadyVashjPhase1SpreadRangedInArcAction(
|
||||
PlayerbotAI* botAI, std::string const name = "lady vashj phase 1 spread ranged in arc") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LadyVashjSetGroundingTotemInMainTankGroupAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
LadyVashjSetGroundingTotemInMainTankGroupAction(
|
||||
PlayerbotAI* botAI, std::string const name = "lady vashj set grounding totem in main tank group") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LadyVashjStaticChargeMoveAwayFromGroupAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
LadyVashjStaticChargeMoveAwayFromGroupAction(
|
||||
PlayerbotAI* botAI, std::string const name = "lady vashj static charge move away from group") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LadyVashjMisdirectBossToMainTankAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
LadyVashjMisdirectBossToMainTankAction(
|
||||
PlayerbotAI* botAI, std::string const name = "lady vashj misdirect boss to main tank") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LadyVashjAssignPhase2AndPhase3DpsPriorityAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
LadyVashjAssignPhase2AndPhase3DpsPriorityAction(
|
||||
PlayerbotAI* botAI, std::string const name = "lady vashj assign phase 2 and phase 3 dps priority") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LadyVashjMisdirectStriderToFirstAssistTankAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
LadyVashjMisdirectStriderToFirstAssistTankAction(
|
||||
PlayerbotAI* botAI, std::string const name = "lady vashj misdirect strider to first assist tank") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LadyVashjTankAttackAndMoveAwayStriderAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
LadyVashjTankAttackAndMoveAwayStriderAction(
|
||||
PlayerbotAI* botAI, std::string const name = "lady vashj tank attack and move away strider") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LadyVashjTeleportToTaintedElementalAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
LadyVashjTeleportToTaintedElementalAction(
|
||||
PlayerbotAI* botAI, std::string const name = "lady vashj teleport to tainted elemental") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LadyVashjLootTaintedCoreAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
LadyVashjLootTaintedCoreAction(
|
||||
PlayerbotAI* botAI, std::string const name = "lady vashj loot tainted core") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LadyVashjPassTheTaintedCoreAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
LadyVashjPassTheTaintedCoreAction(
|
||||
PlayerbotAI* botAI, std::string const name = "lady vashj pass the tainted core") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
private:
|
||||
bool LineUpFirstCorePasser(Player* designatedLooter, Unit* closestTrigger);
|
||||
bool LineUpSecondCorePasser(Player* firstCorePasser, Unit* closestTrigger);
|
||||
bool LineUpThirdCorePasser(Player* designatedLooter, Player* firstCorePasser, Player* secondCorePasser, Unit* closestTrigger);
|
||||
bool LineUpFourthCorePasser(Player* firstCorePasser, Player* secondCorePasser, Player* thirdCorePasser, Unit* closestTrigger);
|
||||
bool IsFirstCorePasserInIntendedPosition(Player* designatedLooter, Player* firstCorePasser, Unit* closestTrigger);
|
||||
bool IsSecondCorePasserInIntendedPosition(Player* firstCorePasser, Player* secondCorePasser, Unit* closestTrigger);
|
||||
bool IsThirdCorePasserInIntendedPosition(Player* secondCorePasser, Player* thirdCorePasser, Unit* closestTrigger);
|
||||
bool IsFourthCorePasserInIntendedPosition(Player* thirdCorePasser, Player* fourthCorePasser, Unit* closestTrigger);
|
||||
void ScheduleTransferCoreAfterImbue(PlayerbotAI* botAI, Player* giver, Player* receiver);
|
||||
bool UseCoreOnNearestGenerator(const uint32 instanceId);
|
||||
};
|
||||
|
||||
class LadyVashjDestroyTaintedCoreAction : public Action
|
||||
{
|
||||
public:
|
||||
LadyVashjDestroyTaintedCoreAction(PlayerbotAI* botAI, std::string const name = "lady vashj destroy tainted core") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LadyVashjEraseCorePassingTrackersAction : public Action
|
||||
{
|
||||
public:
|
||||
LadyVashjEraseCorePassingTrackersAction(PlayerbotAI* botAI, std::string const name = "lady vashj erase core passing trackers") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LadyVashjAvoidToxicSporesAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
LadyVashjAvoidToxicSporesAction(PlayerbotAI* botAI, std::string const name = "lady vashj avoid toxic spores") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
static std::vector<Unit*> GetAllSporeDropTriggers(PlayerbotAI* botAI, Player* bot);
|
||||
|
||||
private:
|
||||
Position FindSafestNearbyPosition(const std::vector<Unit*>& spores, const Position& position, float maxRadius, float hazardRadius);
|
||||
bool IsPathSafeFromSpores(const Position& start, const Position& end, const std::vector<Unit*>& spores, float hazardRadius);
|
||||
};
|
||||
|
||||
class LadyVashjUseFreeActionAbilitiesAction : public Action
|
||||
{
|
||||
public:
|
||||
LadyVashjUseFreeActionAbilitiesAction(PlayerbotAI* botAI, std::string const name = "lady vashj use free action abilities") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,799 @@
|
||||
#include "RaidSSCMultipliers.h"
|
||||
#include "RaidSSCActions.h"
|
||||
#include "RaidSSCHelpers.h"
|
||||
#include "ChooseTargetActions.h"
|
||||
#include "DestroyItemAction.h"
|
||||
#include "DKActions.h"
|
||||
#include "DruidActions.h"
|
||||
#include "DruidBearActions.h"
|
||||
#include "DruidCatActions.h"
|
||||
#include "DruidShapeshiftActions.h"
|
||||
#include "FollowActions.h"
|
||||
#include "GenericSpellActions.h"
|
||||
#include "HunterActions.h"
|
||||
#include "LootAction.h"
|
||||
#include "MageActions.h"
|
||||
#include "PaladinActions.h"
|
||||
#include "Playerbots.h"
|
||||
#include "ReachTargetActions.h"
|
||||
#include "RogueActions.h"
|
||||
#include "ShamanActions.h"
|
||||
#include "WarlockActions.h"
|
||||
#include "WarriorActions.h"
|
||||
#include "WipeAction.h"
|
||||
|
||||
using namespace SerpentShrineCavernHelpers;
|
||||
|
||||
// Trash
|
||||
|
||||
float UnderbogColossusEscapeToxicPoolMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (bot->HasAura(SPELL_TOXIC_POOL))
|
||||
{
|
||||
if (dynamic_cast<MovementAction*>(action) &&
|
||||
!dynamic_cast<UnderbogColossusEscapeToxicPoolAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Hydross the Unstable <Duke of Currents>
|
||||
|
||||
float HydrossTheUnstableDisableTankActionsMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!botAI->IsMainTank(bot) && !botAI->IsAssistTankOfIndex(bot, 0, true))
|
||||
return 1.0f;
|
||||
|
||||
Unit* hydross = AI_VALUE2(Unit*, "find target", "hydross the unstable");
|
||||
if (!hydross)
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<TankAssistAction*>(action) ||
|
||||
dynamic_cast<CombatFormationMoveAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (dynamic_cast<CastReachTargetSpellAction*>(action) ||
|
||||
dynamic_cast<ReachTargetAction*>(action) ||
|
||||
(dynamic_cast<AttackAction*>(action) &&
|
||||
!dynamic_cast<HydrossTheUnstablePositionFrostTankAction*>(action) &&
|
||||
!dynamic_cast<HydrossTheUnstablePositionNatureTankAction*>(action)))
|
||||
{
|
||||
if ((botAI->IsMainTank(bot) && hydross->HasAura(SPELL_CORRUPTION)) ||
|
||||
(botAI->IsAssistTankOfIndex(bot, 0, true) && !hydross->HasAura(SPELL_CORRUPTION)))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float HydrossTheUnstableWaitForDpsMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* hydross = AI_VALUE2(Unit*, "find target", "hydross the unstable");
|
||||
if (!hydross)
|
||||
return 1.0f;
|
||||
|
||||
Unit* waterElemental = AI_VALUE2(Unit*, "find target", "pure spawn of hydross");
|
||||
Unit* natureElemental = AI_VALUE2(Unit*, "find target", "tainted spawn of hydross");
|
||||
if (botAI->IsAssistTank(bot) && !botAI->IsAssistTankOfIndex(bot, 0, true) &&
|
||||
(waterElemental || natureElemental))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<HydrossTheUnstableMisdirectBossToTankAction*>(action))
|
||||
return 1.0f;
|
||||
|
||||
const uint32 instanceId = hydross->GetMap()->GetInstanceId();
|
||||
const time_t now = std::time(nullptr);
|
||||
constexpr uint8 phaseChangeWaitSeconds = 1;
|
||||
constexpr uint8 dpsWaitSeconds = 5;
|
||||
|
||||
if (!hydross->HasAura(SPELL_CORRUPTION) && !botAI->IsMainTank(bot))
|
||||
{
|
||||
auto itDps = hydrossFrostDpsWaitTimer.find(instanceId);
|
||||
auto itPhase = hydrossChangeToFrostPhaseTimer.find(instanceId);
|
||||
|
||||
bool justChanged = (itDps == hydrossFrostDpsWaitTimer.end() ||
|
||||
(now - itDps->second) < dpsWaitSeconds);
|
||||
bool aboutToChange = (itPhase != hydrossChangeToFrostPhaseTimer.end() &&
|
||||
(now - itPhase->second) > phaseChangeWaitSeconds);
|
||||
|
||||
if (justChanged || aboutToChange)
|
||||
{
|
||||
if (dynamic_cast<AttackAction*>(action) ||
|
||||
(dynamic_cast<CastSpellAction*>(action) &&
|
||||
!dynamic_cast<CastHealingSpellAction*>(action)))
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
if (hydross->HasAura(SPELL_CORRUPTION) && !botAI->IsAssistTankOfIndex(bot, 0, true))
|
||||
{
|
||||
auto itDps = hydrossNatureDpsWaitTimer.find(instanceId);
|
||||
auto itPhase = hydrossChangeToNaturePhaseTimer.find(instanceId);
|
||||
|
||||
bool justChanged = (itDps == hydrossNatureDpsWaitTimer.end() ||
|
||||
(now - itDps->second) < dpsWaitSeconds);
|
||||
bool aboutToChange = (itPhase != hydrossChangeToNaturePhaseTimer.end() &&
|
||||
(now - itPhase->second) > phaseChangeWaitSeconds);
|
||||
|
||||
if (justChanged || aboutToChange)
|
||||
{
|
||||
if (dynamic_cast<AttackAction*>(action) ||
|
||||
(dynamic_cast<CastSpellAction*>(action) &&
|
||||
!dynamic_cast<CastHealingSpellAction*>(action)))
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float HydrossTheUnstableControlMisdirectionMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (bot->getClass() != CLASS_HUNTER)
|
||||
return 1.0f;
|
||||
|
||||
if (AI_VALUE2(Unit*, "find target", "hydross the unstable"))
|
||||
{
|
||||
if (dynamic_cast<CastMisdirectionOnMainTankAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// The Lurker Below
|
||||
|
||||
float TheLurkerBelowStayAwayFromSpoutMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* lurker = AI_VALUE2(Unit*, "find target", "the lurker below");
|
||||
if (!lurker)
|
||||
return 1.0f;
|
||||
|
||||
const time_t now = std::time(nullptr);
|
||||
|
||||
auto it = lurkerSpoutTimer.find(lurker->GetMap()->GetInstanceId());
|
||||
if (it != lurkerSpoutTimer.end() && it->second > now)
|
||||
{
|
||||
if (dynamic_cast<CastReachTargetSpellAction*>(action) ||
|
||||
dynamic_cast<CastKillingSpreeAction*>(action) ||
|
||||
dynamic_cast<CastBlinkBackAction*>(action) ||
|
||||
dynamic_cast<CastDisengageAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (dynamic_cast<MovementAction*>(action) &&
|
||||
!dynamic_cast<AttackAction*>(action) &&
|
||||
!dynamic_cast<TheLurkerBelowRunAroundBehindBossAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float TheLurkerBelowMaintainRangedSpreadMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!botAI->IsRanged(bot))
|
||||
return 1.0f;
|
||||
|
||||
if (AI_VALUE2(Unit*, "find target", "the lurker below"))
|
||||
{
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||
dynamic_cast<FleeAction*>(action) ||
|
||||
dynamic_cast<CastDisengageAction*>(action) ||
|
||||
dynamic_cast<CastBlinkBackAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Disable tank assist during Submerge only if there are 3 or more tanks in the raid
|
||||
float TheLurkerBelowDisableTankAssistMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!botAI->IsTank(bot))
|
||||
return 1.0f;
|
||||
|
||||
if (bot->GetVictim() == nullptr)
|
||||
return 1.0f;
|
||||
|
||||
Unit* lurker = AI_VALUE2(Unit*, "find target", "the lurker below");
|
||||
if (!lurker || lurker->getStandState() != UNIT_STAND_STATE_SUBMERGED)
|
||||
return 1.0f;
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return 1.0f;
|
||||
|
||||
uint8 tankCount = 0;
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive())
|
||||
continue;
|
||||
|
||||
if (botAI->IsTank(member))
|
||||
++tankCount;
|
||||
}
|
||||
|
||||
if (tankCount >= 3)
|
||||
{
|
||||
if (dynamic_cast<TankAssistAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Leotheras the Blind
|
||||
|
||||
float LeotherasTheBlindAvoidWhirlwindMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (botAI->IsTank(bot))
|
||||
return 1.0f;
|
||||
|
||||
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||
return 1.0f;
|
||||
|
||||
Unit* leotherasHuman = GetLeotherasHuman(botAI);
|
||||
if (!leotherasHuman)
|
||||
return 1.0f;
|
||||
|
||||
if (!leotherasHuman->HasAura(SPELL_LEOTHERAS_BANISHED) &&
|
||||
(leotherasHuman->HasAura(SPELL_WHIRLWIND) ||
|
||||
leotherasHuman->HasAura(SPELL_WHIRLWIND_CHANNEL)))
|
||||
{
|
||||
if (dynamic_cast<CastReachTargetSpellAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (dynamic_cast<MovementAction*>(action) &&
|
||||
!dynamic_cast<AttackAction*>(action) &&
|
||||
!dynamic_cast<LeotherasTheBlindRunAwayFromWhirlwindAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float LeotherasTheBlindDisableTankActionsMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!botAI->IsTank(bot) || bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||
return 1.0f;
|
||||
|
||||
if (!AI_VALUE2(Unit*, "find target", "leotheras the blind"))
|
||||
return 1.0f;
|
||||
|
||||
if (GetPhase2LeotherasDemon(botAI) && dynamic_cast<AttackAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (!GetPhase3LeotherasDemon(botAI) && dynamic_cast<CastBerserkAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float LeotherasTheBlindFocusOnInnerDemonMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||
{
|
||||
if (dynamic_cast<TankAssistAction*>(action) ||
|
||||
dynamic_cast<DpsAssistAction*>(action) ||
|
||||
dynamic_cast<CastHealingSpellAction*>(action) ||
|
||||
dynamic_cast<CastCureSpellAction*>(action) ||
|
||||
dynamic_cast<CurePartyMemberAction*>(action) ||
|
||||
dynamic_cast<CastBuffSpellAction*>(action) ||
|
||||
dynamic_cast<ResurrectPartyMemberAction*>(action) ||
|
||||
dynamic_cast<PartyMemberActionNameSupport*>(action) ||
|
||||
dynamic_cast<CastBearFormAction*>(action) ||
|
||||
dynamic_cast<CastDireBearFormAction*>(action) ||
|
||||
dynamic_cast<CastTreeFormAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float LeotherasTheBlindMeleeDpsAvoidChaosBlastMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (botAI->IsRanged(bot) || botAI->IsTank(bot))
|
||||
return 1.0f;
|
||||
|
||||
if (!GetPhase2LeotherasDemon(botAI))
|
||||
return 1.0f;
|
||||
|
||||
Aura* chaosBlast = bot->GetAura(SPELL_CHAOS_BLAST);
|
||||
if (chaosBlast && chaosBlast->GetStackAmount() >= 5)
|
||||
{
|
||||
if (dynamic_cast<AttackAction*>(action) ||
|
||||
dynamic_cast<ReachTargetAction*>(action) ||
|
||||
dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||
dynamic_cast<CastReachTargetSpellAction*>(action) ||
|
||||
dynamic_cast<CastKillingSpreeAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float LeotherasTheBlindWaitForDpsMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* leotheras = AI_VALUE2(Unit*, "find target", "leotheras the blind");
|
||||
if (!leotheras)
|
||||
return 1.0f;
|
||||
|
||||
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<LeotherasTheBlindMisdirectBossToDemonFormTankAction*>(action))
|
||||
return 1.0f;
|
||||
|
||||
const uint32 instanceId = leotheras->GetMap()->GetInstanceId();
|
||||
const time_t now = std::time(nullptr);
|
||||
|
||||
constexpr uint8 dpsWaitSecondsPhase1 = 5;
|
||||
Unit* leotherasHuman = GetLeotherasHuman(botAI);
|
||||
Unit* leotherasPhase3Demon = GetPhase3LeotherasDemon(botAI);
|
||||
if (leotherasHuman && !leotherasHuman->HasAura(SPELL_LEOTHERAS_BANISHED) &&
|
||||
!leotherasPhase3Demon)
|
||||
{
|
||||
if (botAI->IsTank(bot))
|
||||
return 1.0f;
|
||||
|
||||
auto it = leotherasHumanFormDpsWaitTimer.find(instanceId);
|
||||
if (it == leotherasHumanFormDpsWaitTimer.end() ||
|
||||
(now - it->second) < dpsWaitSecondsPhase1)
|
||||
{
|
||||
if (dynamic_cast<AttackAction*>(action) ||
|
||||
(dynamic_cast<CastSpellAction*>(action) &&
|
||||
!dynamic_cast<CastHealingSpellAction*>(action)))
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr uint8 dpsWaitSecondsPhase2 = 12;
|
||||
Unit* leotherasPhase2Demon = GetPhase2LeotherasDemon(botAI);
|
||||
Player* demonFormTank = GetLeotherasDemonFormTank(bot);
|
||||
if (leotherasPhase2Demon)
|
||||
{
|
||||
if (demonFormTank && demonFormTank == bot)
|
||||
return 1.0f;
|
||||
|
||||
if (!demonFormTank && botAI->IsTank(bot))
|
||||
return 1.0f;
|
||||
|
||||
auto it = leotherasDemonFormDpsWaitTimer.find(instanceId);
|
||||
if (it == leotherasDemonFormDpsWaitTimer.end() ||
|
||||
(now - it->second) < dpsWaitSecondsPhase2)
|
||||
{
|
||||
if (dynamic_cast<AttackAction*>(action) ||
|
||||
(dynamic_cast<CastSpellAction*>(action) &&
|
||||
!dynamic_cast<CastHealingSpellAction*>(action)))
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr uint8 dpsWaitSecondsPhase3 = 8;
|
||||
if (leotherasPhase3Demon)
|
||||
{
|
||||
if ((demonFormTank && demonFormTank == bot) || botAI->IsTank(bot))
|
||||
return 1.0f;
|
||||
|
||||
auto it = leotherasFinalPhaseDpsWaitTimer.find(instanceId);
|
||||
if (it == leotherasFinalPhaseDpsWaitTimer.end() ||
|
||||
(now - it->second) < dpsWaitSecondsPhase3)
|
||||
{
|
||||
if (dynamic_cast<AttackAction*>(action) ||
|
||||
(dynamic_cast<CastSpellAction*>(action) &&
|
||||
!dynamic_cast<CastHealingSpellAction*>(action)))
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Don't use Bloodlust/Heroism during the Channeler phase
|
||||
float LeotherasTheBlindDelayBloodlustAndHeroismMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (bot->getClass() != CLASS_SHAMAN)
|
||||
return 1.0f;
|
||||
|
||||
Unit* leotheras = AI_VALUE2(Unit*, "find target", "leotheras the blind");
|
||||
if (leotheras && leotheras->HasAura(SPELL_LEOTHERAS_BANISHED))
|
||||
{
|
||||
if (dynamic_cast<CastHeroismAction*>(action) ||
|
||||
dynamic_cast<CastBloodlustAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Fathom-Lord Karathress
|
||||
|
||||
float FathomLordKarathressDisableTankActionsMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!botAI->IsTank(bot))
|
||||
return 1.0f;
|
||||
|
||||
if (!AI_VALUE2(Unit*, "find target", "fathom-lord karathress"))
|
||||
return 1.0f;
|
||||
|
||||
if (bot->GetVictim() != nullptr && dynamic_cast<TankAssistAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||
dynamic_cast<AvoidAoeAction*>(action) ||
|
||||
dynamic_cast<CastTauntAction*>(action) ||
|
||||
dynamic_cast<CastChallengingShoutAction*>(action) ||
|
||||
dynamic_cast<CastThunderClapAction*>(action) ||
|
||||
dynamic_cast<CastShockwaveAction*>(action) ||
|
||||
dynamic_cast<CastCleaveAction*>(action) ||
|
||||
dynamic_cast<CastGrowlAction*>(action) ||
|
||||
dynamic_cast<CastSwipeAction*>(action) ||
|
||||
dynamic_cast<CastHandOfReckoningAction*>(action) ||
|
||||
dynamic_cast<CastAvengersShieldAction*>(action) ||
|
||||
dynamic_cast<CastConsecrationAction*>(action) ||
|
||||
dynamic_cast<CastDarkCommandAction*>(action) ||
|
||||
dynamic_cast<CastDeathAndDecayAction*>(action) ||
|
||||
dynamic_cast<CastPestilenceAction*>(action) ||
|
||||
dynamic_cast<CastBloodBoilAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float FathomLordKarathressDisableAoeMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!botAI->IsDps(bot))
|
||||
return 1.0f;
|
||||
|
||||
if (AI_VALUE2(Unit*, "find target", "fathom-lord karathress"))
|
||||
{
|
||||
if (auto castSpellAction = dynamic_cast<CastSpellAction*>(action))
|
||||
{
|
||||
if (castSpellAction->getThreatType() == Action::ActionThreatType::Aoe)
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float FathomLordKarathressControlMisdirectionMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (bot->getClass() != CLASS_HUNTER)
|
||||
return 1.0f;
|
||||
|
||||
if (AI_VALUE2(Unit*, "find target", "fathom-lord karathress"))
|
||||
{
|
||||
if (dynamic_cast<CastMisdirectionOnMainTankAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float FathomLordKarathressWaitForDpsMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (botAI->IsTank(bot))
|
||||
return 1.0f;
|
||||
|
||||
Unit* karathress = AI_VALUE2(Unit*, "find target", "fathom-lord karathress");
|
||||
if (!karathress)
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<FathomLordKarathressMisdirectBossesToTanksAction*>(action))
|
||||
return 1.0f;
|
||||
|
||||
const time_t now = std::time(nullptr);
|
||||
constexpr uint8 dpsWaitSeconds = 12;
|
||||
|
||||
auto it = karathressDpsWaitTimer.find(karathress->GetMap()->GetInstanceId());
|
||||
if (it == karathressDpsWaitTimer.end() || (now - it->second) < dpsWaitSeconds)
|
||||
{
|
||||
if (dynamic_cast<AttackAction*>(action) ||
|
||||
(dynamic_cast<CastSpellAction*>(action) &&
|
||||
!dynamic_cast<CastHealingSpellAction*>(action)))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float FathomLordKarathressCaribdisTankHealerMaintainPositionMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!botAI->IsAssistHealOfIndex(bot, 0, true))
|
||||
return 1.0f;
|
||||
|
||||
if (AI_VALUE2(Unit*, "find target", "fathom-guard caribdis"))
|
||||
{
|
||||
if (dynamic_cast<FleeAction*>(action) ||
|
||||
dynamic_cast<FollowAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Morogrim Tidewalker
|
||||
|
||||
// Use Bloodlust/Heroism after the first Murloc spawn
|
||||
float MorogrimTidewalkerDelayBloodlustAndHeroismMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (bot->getClass() != CLASS_SHAMAN)
|
||||
return 1.0f;
|
||||
|
||||
if (!AI_VALUE2(Unit*, "find target", "morogrim tidewalker"))
|
||||
return 1.0f;
|
||||
|
||||
if (!AI_VALUE2(Unit*, "find target", "tidewalker lurker"))
|
||||
{
|
||||
if (dynamic_cast<CastHeroismAction*>(action) ||
|
||||
dynamic_cast<CastBloodlustAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float MorogrimTidewalkerDisableTankActionsMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!botAI->IsMainTank(bot))
|
||||
return 1.0f;
|
||||
|
||||
if (AI_VALUE2(Unit*, "find target", "morogrim tidewalker"))
|
||||
{
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float MorogrimTidewalkerMaintainPhase2StackingMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!botAI->IsRanged(bot))
|
||||
return 1.0f;
|
||||
|
||||
Unit* tidewalker = AI_VALUE2(Unit*, "find target", "morogrim tidewalker");
|
||||
if (!tidewalker)
|
||||
return 1.0f;
|
||||
|
||||
if (tidewalker->GetHealthPct() < 25.0f)
|
||||
{
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||
dynamic_cast<FleeAction*>(action) ||
|
||||
dynamic_cast<CastDisengageAction*>(action) ||
|
||||
dynamic_cast<CastBlinkBackAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Lady Vashj <Coilfang Matron>
|
||||
|
||||
// Wait until phase 3 to use Bloodlust/Heroism
|
||||
// Don't use other major cooldowns in Phase 1, either
|
||||
float LadyVashjDelayCooldownsMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!AI_VALUE2(Unit*, "find target", "lady vashj"))
|
||||
return 1.0f;
|
||||
|
||||
if (bot->getClass() == CLASS_SHAMAN)
|
||||
{
|
||||
if (IsLadyVashjInPhase3(botAI))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<CastBloodlustAction*>(action) ||
|
||||
dynamic_cast<CastHeroismAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (botAI->IsDps(bot) && IsLadyVashjInPhase1(botAI))
|
||||
{
|
||||
if (dynamic_cast<CastMetamorphosisAction*>(action) ||
|
||||
dynamic_cast<CastAdrenalineRushAction*>(action) ||
|
||||
dynamic_cast<CastBladeFlurryAction*>(action) ||
|
||||
dynamic_cast<CastIcyVeinsAction*>(action) ||
|
||||
dynamic_cast<CastColdSnapAction*>(action) ||
|
||||
dynamic_cast<CastArcanePowerAction*>(action) ||
|
||||
dynamic_cast<CastPresenceOfMindAction*>(action) ||
|
||||
dynamic_cast<CastCombustionAction*>(action) ||
|
||||
dynamic_cast<CastRapidFireAction*>(action) ||
|
||||
dynamic_cast<CastReadinessAction*>(action) ||
|
||||
dynamic_cast<CastAvengingWrathAction*>(action) ||
|
||||
dynamic_cast<CastElementalMasteryAction*>(action) ||
|
||||
dynamic_cast<CastFeralSpiritAction*>(action) ||
|
||||
dynamic_cast<CastFireElementalTotemAction*>(action) ||
|
||||
dynamic_cast<CastFireElementalTotemMeleeAction*>(action) ||
|
||||
dynamic_cast<CastForceOfNatureAction*>(action) ||
|
||||
dynamic_cast<CastArmyOfTheDeadAction*>(action) ||
|
||||
dynamic_cast<CastSummonGargoyleAction*>(action) ||
|
||||
dynamic_cast<CastBerserkingAction*>(action) ||
|
||||
dynamic_cast<CastBloodFuryAction*>(action) ||
|
||||
dynamic_cast<UseTrinketAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float LadyVashjMaintainPhase1RangedSpreadMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!botAI->IsRanged(bot))
|
||||
return 1.0f;
|
||||
|
||||
if (AI_VALUE2(Unit*, "find target", "lady vashj") &&
|
||||
IsLadyVashjInPhase1(botAI))
|
||||
{
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||
dynamic_cast<FleeAction*>(action) ||
|
||||
dynamic_cast<CastDisengageAction*>(action) ||
|
||||
dynamic_cast<CastBlinkBackAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float LadyVashjStaticChargeStayAwayFromGroupMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (botAI->IsMainTank(bot) || !bot->HasAura(SPELL_STATIC_CHARGE))
|
||||
return 1.0f;
|
||||
|
||||
if (!AI_VALUE2(Unit*, "find target", "lady vashj"))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||
dynamic_cast<ReachTargetAction*>(action) ||
|
||||
dynamic_cast<FollowAction*>(action) ||
|
||||
dynamic_cast<CastKillingSpreeAction*>(action) ||
|
||||
dynamic_cast<CastReachTargetSpellAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Bots should not loot the core with normal looting logic
|
||||
float LadyVashjDoNotLootTheTaintedCoreMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (AI_VALUE2(Unit*, "find target", "lady vashj"))
|
||||
{
|
||||
if (dynamic_cast<LootAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float LadyVashjCorePassersPrioritizePositioningMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!AI_VALUE2(Unit*, "find target", "lady vashj") ||
|
||||
!IsLadyVashjInPhase2(botAI))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<WipeAction*>(action) ||
|
||||
dynamic_cast<DestroyItemAction*>(action) ||
|
||||
dynamic_cast<LadyVashjDestroyTaintedCoreAction*>(action))
|
||||
return 1.0f;
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return 1.0f;
|
||||
|
||||
Player* designatedLooter = GetDesignatedCoreLooter(group, botAI);
|
||||
Player* firstCorePasser = GetFirstTaintedCorePasser(group, botAI);
|
||||
Player* secondCorePasser = GetSecondTaintedCorePasser(group, botAI);
|
||||
Player* thirdCorePasser = GetThirdTaintedCorePasser(group, botAI);
|
||||
Player* fourthCorePasser = GetFourthTaintedCorePasser(group, botAI);
|
||||
|
||||
auto hasCore = [](Player* player)
|
||||
{
|
||||
return player && player->HasItemCount(ITEM_TAINTED_CORE, 1, false);
|
||||
};
|
||||
|
||||
if (hasCore(bot))
|
||||
{
|
||||
if (!dynamic_cast<LadyVashjPassTheTaintedCoreAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (bot == designatedLooter)
|
||||
{
|
||||
if (!hasCore(bot))
|
||||
return 1.0f;
|
||||
}
|
||||
else if (bot == firstCorePasser)
|
||||
{
|
||||
if (hasCore(secondCorePasser) || hasCore(thirdCorePasser) ||
|
||||
hasCore(fourthCorePasser))
|
||||
return 1.0f;
|
||||
}
|
||||
else if (bot == secondCorePasser)
|
||||
{
|
||||
if (hasCore(thirdCorePasser) || hasCore(fourthCorePasser))
|
||||
return 1.0f;
|
||||
}
|
||||
else if (bot == thirdCorePasser)
|
||||
{
|
||||
if (hasCore(fourthCorePasser))
|
||||
return 1.0f;
|
||||
}
|
||||
else if (bot != fourthCorePasser)
|
||||
return 1.0f;
|
||||
|
||||
if (AI_VALUE2(Unit*, "find target", "tainted elemental") &&
|
||||
(bot == firstCorePasser || bot == secondCorePasser))
|
||||
{
|
||||
if (dynamic_cast<MovementAction*>(action) &&
|
||||
!dynamic_cast<LadyVashjPassTheTaintedCoreAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (AnyRecentCoreInInventory(group, botAI))
|
||||
{
|
||||
if (dynamic_cast<MovementAction*>(action) &&
|
||||
!dynamic_cast<LadyVashjPassTheTaintedCoreAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// All of phases 2 and 3 require a custom movement and targeting system
|
||||
// So the standard target selection system must be disabled
|
||||
float LadyVashjDisableAutomaticTargetingAndMovementModifier::GetValue(Action *action)
|
||||
{
|
||||
if (!AI_VALUE2(Unit*, "find target", "lady vashj"))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<AvoidAoeAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (IsLadyVashjInPhase2(botAI))
|
||||
{
|
||||
if (dynamic_cast<DpsAssistAction*>(action) ||
|
||||
dynamic_cast<TankAssistAction*>(action) ||
|
||||
dynamic_cast<FollowAction*>(action) ||
|
||||
dynamic_cast<FleeAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (!botAI->IsHeal(bot) && dynamic_cast<CastHealingSpellAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
Unit* enchanted = AI_VALUE2(Unit*, "find target", "enchanted elemental");
|
||||
if (enchanted && bot->GetVictim() == enchanted)
|
||||
{
|
||||
if (dynamic_cast<CastDebuffSpellOnAttackerAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsLadyVashjInPhase3(botAI))
|
||||
{
|
||||
if (dynamic_cast<DpsAssistAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
Unit* enchanted = AI_VALUE2(Unit*, "find target", "enchanted elemental");
|
||||
Unit* strider = AI_VALUE2(Unit*, "find target", "coilfang strider");
|
||||
Unit* elite = AI_VALUE2(Unit*, "find target", "coilfang elite");
|
||||
if (enchanted || strider || elite)
|
||||
{
|
||||
if (dynamic_cast<TankAssistAction*>(action) ||
|
||||
dynamic_cast<FollowAction*>(action) ||
|
||||
dynamic_cast<FleeAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (enchanted && bot->GetVictim() == enchanted)
|
||||
{
|
||||
if (dynamic_cast<CastDebuffSpellOnAttackerAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
else if (dynamic_cast<CombatFormationMoveAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
236
src/Ai/Raid/SerpentshrineCavern/Multiplier/RaidSSCMultipliers.h
Normal file
236
src/Ai/Raid/SerpentshrineCavern/Multiplier/RaidSSCMultipliers.h
Normal file
@@ -0,0 +1,236 @@
|
||||
#ifndef _PLAYERBOT_RAIDSSCMULTIPLIERS_H
|
||||
#define _PLAYERBOT_RAIDSSCMULTIPLIERS_H
|
||||
|
||||
#include "Multiplier.h"
|
||||
|
||||
// Trash
|
||||
|
||||
class UnderbogColossusEscapeToxicPoolMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
UnderbogColossusEscapeToxicPoolMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "underbog colossus escape toxic pool") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
// Hydross the Unstable <Duke of Currents>
|
||||
|
||||
class HydrossTheUnstableDisableTankActionsMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
HydrossTheUnstableDisableTankActionsMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "hydross the unstable disable tank actions") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class HydrossTheUnstableWaitForDpsMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
HydrossTheUnstableWaitForDpsMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "hydross the unstable wait for dps") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class HydrossTheUnstableControlMisdirectionMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
HydrossTheUnstableControlMisdirectionMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "hydross the unstable control misdirection") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
// The Lurker Below
|
||||
|
||||
class TheLurkerBelowStayAwayFromSpoutMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
TheLurkerBelowStayAwayFromSpoutMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "the lurker below stay away from spout") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class TheLurkerBelowMaintainRangedSpreadMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
TheLurkerBelowMaintainRangedSpreadMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "the lurker below maintain ranged spread") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class TheLurkerBelowDisableTankAssistMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
TheLurkerBelowDisableTankAssistMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "the lurker below disable tank assist") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
// Leotheras the Blind
|
||||
|
||||
class LeotherasTheBlindAvoidWhirlwindMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindAvoidWhirlwindMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "leotheras the blind avoid whirlwind") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class LeotherasTheBlindDisableTankActionsMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindDisableTankActionsMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "leotheras the blind disable tank actions") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class LeotherasTheBlindMeleeDpsAvoidChaosBlastMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindMeleeDpsAvoidChaosBlastMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "leotheras the blind melee dps avoid chaos blast") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class LeotherasTheBlindFocusOnInnerDemonMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindFocusOnInnerDemonMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "leotheras the blind focus on inner demon") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class LeotherasTheBlindWaitForDpsMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindWaitForDpsMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "leotheras the blind wait for dps") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class LeotherasTheBlindDelayBloodlustAndHeroismMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindDelayBloodlustAndHeroismMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "leotheras the blind delay bloodlust and heroism") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
// Fathom-Lord Karathress
|
||||
|
||||
class FathomLordKarathressDisableTankActionsMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressDisableTankActionsMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "fathom-lord karathress disable tank actions") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class FathomLordKarathressDisableAoeMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressDisableAoeMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "fathom-lord karathress disable aoe") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class FathomLordKarathressControlMisdirectionMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressControlMisdirectionMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "fathom-lord karathress control misdirection") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class FathomLordKarathressWaitForDpsMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressWaitForDpsMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "fathom-lord karathress wait for dps") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class FathomLordKarathressCaribdisTankHealerMaintainPositionMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressCaribdisTankHealerMaintainPositionMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "fathom-lord karathress caribdis tank healer maintain position") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
// Morogrim Tidewalker
|
||||
|
||||
class MorogrimTidewalkerDelayBloodlustAndHeroismMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
MorogrimTidewalkerDelayBloodlustAndHeroismMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "morogrim tidewalker delay bloodlust and heroism") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class MorogrimTidewalkerDisableTankActionsMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
MorogrimTidewalkerDisableTankActionsMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "morogrim tidewalker disable tank actions") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class MorogrimTidewalkerMaintainPhase2StackingMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
MorogrimTidewalkerMaintainPhase2StackingMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "morogrim tidewalker maintain phase2 stacking") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
// Lady Vashj <Coilfang Matron>
|
||||
|
||||
class LadyVashjDelayCooldownsMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
LadyVashjDelayCooldownsMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "lady vashj delay cooldowns") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class LadyVashjMaintainPhase1RangedSpreadMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
LadyVashjMaintainPhase1RangedSpreadMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "lady vashj maintain phase1 ranged spread") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class LadyVashjStaticChargeStayAwayFromGroupMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
LadyVashjStaticChargeStayAwayFromGroupMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "lady vashj static charge stay away from group") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class LadyVashjDoNotLootTheTaintedCoreMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
LadyVashjDoNotLootTheTaintedCoreMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "lady vashj do not loot the tainted core") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class LadyVashjCorePassersPrioritizePositioningMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
LadyVashjCorePassersPrioritizePositioningMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "lady vashj core passers prioritize positioning") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class LadyVashjDisableAutomaticTargetingAndMovementModifier : public Multiplier
|
||||
{
|
||||
public:
|
||||
LadyVashjDisableAutomaticTargetingAndMovementModifier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "lady vashj disable automatic targeting and movement") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
#endif
|
||||
337
src/Ai/Raid/SerpentshrineCavern/RaidSSCActionContext.h
Normal file
337
src/Ai/Raid/SerpentshrineCavern/RaidSSCActionContext.h
Normal file
@@ -0,0 +1,337 @@
|
||||
#ifndef _PLAYERBOT_RAIDSSCACTIONCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDSSCACTIONCONTEXT_H
|
||||
|
||||
#include "RaidSSCActions.h"
|
||||
#include "NamedObjectContext.h"
|
||||
|
||||
class RaidSSCActionContext : public NamedObjectContext<Action>
|
||||
{
|
||||
public:
|
||||
RaidSSCActionContext()
|
||||
{
|
||||
// General
|
||||
creators["serpent shrine cavern erase timers and trackers"] =
|
||||
&RaidSSCActionContext::serpent_shrine_cavern_erase_timers_and_trackers;
|
||||
|
||||
// Trash
|
||||
creators["underbog colossus escape toxic pool"] =
|
||||
&RaidSSCActionContext::underbog_colossus_escape_toxic_pool;
|
||||
|
||||
creators["greyheart tidecaller mark water elemental totem"] =
|
||||
&RaidSSCActionContext::greyheart_tidecaller_mark_water_elemental_totem;
|
||||
|
||||
// Hydross the Unstable <Duke of Currents>
|
||||
creators["hydross the unstable position frost tank"] =
|
||||
&RaidSSCActionContext::hydross_the_unstable_position_frost_tank;
|
||||
|
||||
creators["hydross the unstable position nature tank"] =
|
||||
&RaidSSCActionContext::hydross_the_unstable_position_nature_tank;
|
||||
|
||||
creators["hydross the unstable prioritize elemental adds"] =
|
||||
&RaidSSCActionContext::hydross_the_unstable_prioritize_elemental_adds;
|
||||
|
||||
creators["hydross the unstable frost phase spread out"] =
|
||||
&RaidSSCActionContext::hydross_the_unstable_frost_phase_spread_out;
|
||||
|
||||
creators["hydross the unstable misdirect boss to tank"] =
|
||||
&RaidSSCActionContext::hydross_the_unstable_misdirect_boss_to_tank;
|
||||
|
||||
creators["hydross the unstable stop dps upon phase change"] =
|
||||
&RaidSSCActionContext::hydross_the_unstable_stop_dps_upon_phase_change;
|
||||
|
||||
creators["hydross the unstable manage timers"] =
|
||||
&RaidSSCActionContext::hydross_the_unstable_manage_timers;
|
||||
|
||||
// The Lurker Below
|
||||
creators["the lurker below run around behind boss"] =
|
||||
&RaidSSCActionContext::the_lurker_below_run_around_behind_boss;
|
||||
|
||||
creators["the lurker below position main tank"] =
|
||||
&RaidSSCActionContext::the_lurker_below_position_main_tank;
|
||||
|
||||
creators["the lurker below spread ranged in arc"] =
|
||||
&RaidSSCActionContext::the_lurker_below_spread_ranged_in_arc;
|
||||
|
||||
creators["the lurker below tanks pick up adds"] =
|
||||
&RaidSSCActionContext::the_lurker_below_tanks_pick_up_adds;
|
||||
|
||||
creators["the lurker below manage spout timer"] =
|
||||
&RaidSSCActionContext::the_lurker_below_manage_spout_timer;
|
||||
|
||||
// Leotheras the Blind
|
||||
creators["leotheras the blind target spellbinders"] =
|
||||
&RaidSSCActionContext::leotheras_the_blind_target_spellbinders;
|
||||
|
||||
creators["leotheras the blind demon form tank attack boss"] =
|
||||
&RaidSSCActionContext::leotheras_the_blind_demon_form_tank_attack_boss;
|
||||
|
||||
creators["leotheras the blind melee tanks don't attack demon form"] =
|
||||
&RaidSSCActionContext::leotheras_the_blind_melee_tanks_dont_attack_demon_form;
|
||||
|
||||
creators["leotheras the blind position ranged"] =
|
||||
&RaidSSCActionContext::leotheras_the_blind_position_ranged;
|
||||
|
||||
creators["leotheras the blind run away from whirlwind"] =
|
||||
&RaidSSCActionContext::leotheras_the_blind_run_away_from_whirlwind;
|
||||
|
||||
creators["leotheras the blind melee dps run away from boss"] =
|
||||
&RaidSSCActionContext::leotheras_the_blind_melee_dps_run_away_from_boss;
|
||||
|
||||
creators["leotheras the blind destroy inner demon"] =
|
||||
&RaidSSCActionContext::leotheras_the_blind_destroy_inner_demon;
|
||||
|
||||
creators["leotheras the blind final phase assign dps priority"] =
|
||||
&RaidSSCActionContext::leotheras_the_blind_final_phase_assign_dps_priority;
|
||||
|
||||
creators["leotheras the blind misdirect boss to demon form tank"] =
|
||||
&RaidSSCActionContext::leotheras_the_blind_misdirect_boss_to_demon_form_tank;
|
||||
|
||||
creators["leotheras the blind manage dps wait timers"] =
|
||||
&RaidSSCActionContext::leotheras_the_blind_manage_dps_wait_timers;
|
||||
|
||||
// Fathom-Lord Karathress
|
||||
creators["fathom-lord karathress main tank position boss"] =
|
||||
&RaidSSCActionContext::fathom_lord_karathress_main_tank_position_boss;
|
||||
|
||||
creators["fathom-lord karathress first assist tank position caribdis"] =
|
||||
&RaidSSCActionContext::fathom_lord_karathress_first_assist_tank_position_caribdis;
|
||||
|
||||
creators["fathom-lord karathress second assist tank position sharkkis"] =
|
||||
&RaidSSCActionContext::fathom_lord_karathress_second_assist_tank_position_sharkkis;
|
||||
|
||||
creators["fathom-lord karathress third assist tank position tidalvess"] =
|
||||
&RaidSSCActionContext::fathom_lord_karathress_third_assist_tank_position_tidalvess;
|
||||
|
||||
creators["fathom-lord karathress position caribdis tank healer"] =
|
||||
&RaidSSCActionContext::fathom_lord_karathress_position_caribdis_tank_healer;
|
||||
|
||||
creators["fathom-lord karathress misdirect bosses to tanks"] =
|
||||
&RaidSSCActionContext::fathom_lord_karathress_misdirect_bosses_to_tanks;
|
||||
|
||||
creators["fathom-lord karathress assign dps priority"] =
|
||||
&RaidSSCActionContext::fathom_lord_karathress_assign_dps_priority;
|
||||
|
||||
creators["fathom-lord karathress manage dps timer"] =
|
||||
&RaidSSCActionContext::fathom_lord_karathress_manage_dps_timer;
|
||||
|
||||
// Morogrim Tidewalker
|
||||
creators["morogrim tidewalker misdirect boss to main tank"] =
|
||||
&RaidSSCActionContext::morogrim_tidewalker_misdirect_boss_to_main_tank;
|
||||
|
||||
creators["morogrim tidewalker move boss to tank position"] =
|
||||
&RaidSSCActionContext::morogrim_tidewalker_move_boss_to_tank_position;
|
||||
|
||||
creators["morogrim tidewalker phase 2 reposition ranged"] =
|
||||
&RaidSSCActionContext::morogrim_tidewalker_phase_2_reposition_ranged;
|
||||
|
||||
// Lady Vashj <Coilfang Matron>
|
||||
creators["lady vashj main tank position boss"] =
|
||||
&RaidSSCActionContext::lady_vashj_main_tank_position_boss;
|
||||
|
||||
creators["lady vashj phase 1 spread ranged in arc"] =
|
||||
&RaidSSCActionContext::lady_vashj_phase_1_spread_ranged_in_arc;
|
||||
|
||||
creators["lady vashj set grounding totem in main tank group"] =
|
||||
&RaidSSCActionContext::lady_vashj_set_grounding_totem_in_main_tank_group;
|
||||
|
||||
creators["lady vashj static charge move away from group"] =
|
||||
&RaidSSCActionContext::lady_vashj_static_charge_move_away_from_group;
|
||||
|
||||
creators["lady vashj misdirect boss to main tank"] =
|
||||
&RaidSSCActionContext::lady_vashj_misdirect_boss_to_main_tank;
|
||||
|
||||
creators["lady vashj assign phase 2 and phase 3 dps priority"] =
|
||||
&RaidSSCActionContext::lady_vashj_assign_phase_2_and_phase_3_dps_priority;
|
||||
|
||||
creators["lady vashj misdirect strider to first assist tank"] =
|
||||
&RaidSSCActionContext::lady_vashj_misdirect_strider_to_first_assist_tank;
|
||||
|
||||
creators["lady vashj tank attack and move away strider"] =
|
||||
&RaidSSCActionContext::lady_vashj_tank_attack_and_move_away_strider;
|
||||
|
||||
creators["lady vashj loot tainted core"] =
|
||||
&RaidSSCActionContext::lady_vashj_loot_tainted_core;
|
||||
|
||||
creators["lady vashj teleport to tainted elemental"] =
|
||||
&RaidSSCActionContext::lady_vashj_teleport_to_tainted_elemental;
|
||||
|
||||
creators["lady vashj pass the tainted core"] =
|
||||
&RaidSSCActionContext::lady_vashj_pass_the_tainted_core;
|
||||
|
||||
creators["lady vashj destroy tainted core"] =
|
||||
&RaidSSCActionContext::lady_vashj_destroy_tainted_core;
|
||||
|
||||
creators["lady vashj erase core passing trackers"] =
|
||||
&RaidSSCActionContext::lady_vashj_erase_core_passing_trackers;
|
||||
|
||||
creators["lady vashj avoid toxic spores"] =
|
||||
&RaidSSCActionContext::lady_vashj_avoid_toxic_spores;
|
||||
|
||||
creators["lady vashj use free action abilities"] =
|
||||
&RaidSSCActionContext::lady_vashj_use_free_action_abilities;
|
||||
}
|
||||
|
||||
private:
|
||||
// General
|
||||
static Action* serpent_shrine_cavern_erase_timers_and_trackers(
|
||||
PlayerbotAI* botAI) { return new SerpentShrineCavernEraseTimersAndTrackersAction(botAI); }
|
||||
|
||||
// Trash
|
||||
static Action* underbog_colossus_escape_toxic_pool(
|
||||
PlayerbotAI* botAI) { return new UnderbogColossusEscapeToxicPoolAction(botAI); }
|
||||
|
||||
static Action* greyheart_tidecaller_mark_water_elemental_totem(
|
||||
PlayerbotAI* botAI) { return new GreyheartTidecallerMarkWaterElementalTotemAction(botAI); }
|
||||
|
||||
// Hydross the Unstable <Duke of Currents>
|
||||
static Action* hydross_the_unstable_position_frost_tank(
|
||||
PlayerbotAI* botAI) { return new HydrossTheUnstablePositionFrostTankAction(botAI); }
|
||||
|
||||
static Action* hydross_the_unstable_position_nature_tank(
|
||||
PlayerbotAI* botAI) { return new HydrossTheUnstablePositionNatureTankAction(botAI); }
|
||||
|
||||
static Action* hydross_the_unstable_prioritize_elemental_adds(
|
||||
PlayerbotAI* botAI) { return new HydrossTheUnstablePrioritizeElementalAddsAction(botAI); }
|
||||
|
||||
static Action* hydross_the_unstable_frost_phase_spread_out(
|
||||
PlayerbotAI* botAI) { return new HydrossTheUnstableFrostPhaseSpreadOutAction(botAI); }
|
||||
|
||||
static Action* hydross_the_unstable_misdirect_boss_to_tank(
|
||||
PlayerbotAI* botAI) { return new HydrossTheUnstableMisdirectBossToTankAction(botAI); }
|
||||
|
||||
static Action* hydross_the_unstable_stop_dps_upon_phase_change(
|
||||
PlayerbotAI* botAI) { return new HydrossTheUnstableStopDpsUponPhaseChangeAction(botAI); }
|
||||
|
||||
static Action* hydross_the_unstable_manage_timers(
|
||||
PlayerbotAI* botAI) { return new HydrossTheUnstableManageTimersAction(botAI); }
|
||||
|
||||
// The Lurker Below
|
||||
static Action* the_lurker_below_run_around_behind_boss(
|
||||
PlayerbotAI* botAI) { return new TheLurkerBelowRunAroundBehindBossAction(botAI); }
|
||||
|
||||
static Action* the_lurker_below_position_main_tank(
|
||||
PlayerbotAI* botAI) { return new TheLurkerBelowPositionMainTankAction(botAI); }
|
||||
|
||||
static Action* the_lurker_below_spread_ranged_in_arc(
|
||||
PlayerbotAI* botAI) { return new TheLurkerBelowSpreadRangedInArcAction(botAI); }
|
||||
|
||||
static Action* the_lurker_below_tanks_pick_up_adds(
|
||||
PlayerbotAI* botAI) { return new TheLurkerBelowTanksPickUpAddsAction(botAI); }
|
||||
|
||||
static Action* the_lurker_below_manage_spout_timer(
|
||||
PlayerbotAI* botAI) { return new TheLurkerBelowManageSpoutTimerAction(botAI); }
|
||||
|
||||
// Leotheras the Blind
|
||||
static Action* leotheras_the_blind_target_spellbinders(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindTargetSpellbindersAction(botAI); }
|
||||
|
||||
static Action* leotheras_the_blind_demon_form_tank_attack_boss(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindDemonFormTankAttackBossAction(botAI); }
|
||||
|
||||
static Action* leotheras_the_blind_melee_tanks_dont_attack_demon_form(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindMeleeTanksDontAttackDemonFormAction(botAI); }
|
||||
|
||||
static Action* leotheras_the_blind_position_ranged(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindPositionRangedAction(botAI); }
|
||||
|
||||
static Action* leotheras_the_blind_run_away_from_whirlwind(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindRunAwayFromWhirlwindAction(botAI); }
|
||||
|
||||
static Action* leotheras_the_blind_melee_dps_run_away_from_boss(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindMeleeDpsRunAwayFromBossAction(botAI); }
|
||||
|
||||
static Action* leotheras_the_blind_destroy_inner_demon(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindDestroyInnerDemonAction(botAI); }
|
||||
|
||||
static Action* leotheras_the_blind_misdirect_boss_to_demon_form_tank(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindMisdirectBossToDemonFormTankAction(botAI); }
|
||||
|
||||
static Action* leotheras_the_blind_final_phase_assign_dps_priority(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindFinalPhaseAssignDpsPriorityAction(botAI); }
|
||||
|
||||
static Action* leotheras_the_blind_manage_dps_wait_timers(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindManageDpsWaitTimersAction(botAI); }
|
||||
|
||||
// Fathom-Lord Karathress
|
||||
static Action* fathom_lord_karathress_main_tank_position_boss(
|
||||
PlayerbotAI* botAI) { return new FathomLordKarathressMainTankPositionBossAction(botAI); }
|
||||
|
||||
static Action* fathom_lord_karathress_first_assist_tank_position_caribdis(
|
||||
PlayerbotAI* botAI) { return new FathomLordKarathressFirstAssistTankPositionCaribdisAction(botAI); }
|
||||
|
||||
static Action* fathom_lord_karathress_second_assist_tank_position_sharkkis(
|
||||
PlayerbotAI* botAI) { return new FathomLordKarathressSecondAssistTankPositionSharkkisAction(botAI); }
|
||||
|
||||
static Action* fathom_lord_karathress_third_assist_tank_position_tidalvess(
|
||||
PlayerbotAI* botAI) { return new FathomLordKarathressThirdAssistTankPositionTidalvessAction(botAI); }
|
||||
|
||||
static Action* fathom_lord_karathress_position_caribdis_tank_healer(
|
||||
PlayerbotAI* botAI) { return new FathomLordKarathressPositionCaribdisTankHealerAction(botAI); }
|
||||
|
||||
static Action* fathom_lord_karathress_misdirect_bosses_to_tanks(
|
||||
PlayerbotAI* botAI) { return new FathomLordKarathressMisdirectBossesToTanksAction(botAI); }
|
||||
|
||||
static Action* fathom_lord_karathress_assign_dps_priority(
|
||||
PlayerbotAI* botAI) { return new FathomLordKarathressAssignDpsPriorityAction(botAI); }
|
||||
|
||||
static Action* fathom_lord_karathress_manage_dps_timer(
|
||||
PlayerbotAI* botAI) { return new FathomLordKarathressManageDpsTimerAction(botAI); }
|
||||
|
||||
// Morogrim Tidewalker
|
||||
static Action* morogrim_tidewalker_misdirect_boss_to_main_tank(
|
||||
PlayerbotAI* botAI) { return new MorogrimTidewalkerMisdirectBossToMainTankAction(botAI); }
|
||||
|
||||
static Action* morogrim_tidewalker_move_boss_to_tank_position(
|
||||
PlayerbotAI* botAI) { return new MorogrimTidewalkerMoveBossToTankPositionAction(botAI); }
|
||||
|
||||
static Action* morogrim_tidewalker_phase_2_reposition_ranged(
|
||||
PlayerbotAI* botAI) { return new MorogrimTidewalkerPhase2RepositionRangedAction(botAI); }
|
||||
|
||||
// Lady Vashj <Coilfang Matron>
|
||||
static Action* lady_vashj_main_tank_position_boss(
|
||||
PlayerbotAI* botAI) { return new LadyVashjMainTankPositionBossAction(botAI); }
|
||||
|
||||
static Action* lady_vashj_phase_1_spread_ranged_in_arc(
|
||||
PlayerbotAI* botAI) { return new LadyVashjPhase1SpreadRangedInArcAction(botAI); }
|
||||
|
||||
static Action* lady_vashj_set_grounding_totem_in_main_tank_group(
|
||||
PlayerbotAI* botAI) { return new LadyVashjSetGroundingTotemInMainTankGroupAction(botAI); }
|
||||
|
||||
static Action* lady_vashj_static_charge_move_away_from_group(
|
||||
PlayerbotAI* botAI) { return new LadyVashjStaticChargeMoveAwayFromGroupAction(botAI); }
|
||||
|
||||
static Action* lady_vashj_misdirect_boss_to_main_tank(
|
||||
PlayerbotAI* botAI) { return new LadyVashjMisdirectBossToMainTankAction(botAI); }
|
||||
|
||||
static Action* lady_vashj_assign_phase_2_and_phase_3_dps_priority(
|
||||
PlayerbotAI* botAI) { return new LadyVashjAssignPhase2AndPhase3DpsPriorityAction(botAI); }
|
||||
|
||||
static Action* lady_vashj_misdirect_strider_to_first_assist_tank(
|
||||
PlayerbotAI* botAI) { return new LadyVashjMisdirectStriderToFirstAssistTankAction(botAI); }
|
||||
|
||||
static Action* lady_vashj_tank_attack_and_move_away_strider(
|
||||
PlayerbotAI* botAI) { return new LadyVashjTankAttackAndMoveAwayStriderAction(botAI); }
|
||||
|
||||
static Action* lady_vashj_teleport_to_tainted_elemental(
|
||||
PlayerbotAI* botAI) { return new LadyVashjTeleportToTaintedElementalAction(botAI); }
|
||||
|
||||
static Action* lady_vashj_loot_tainted_core(
|
||||
PlayerbotAI* botAI) { return new LadyVashjLootTaintedCoreAction(botAI); }
|
||||
|
||||
static Action* lady_vashj_pass_the_tainted_core(
|
||||
PlayerbotAI* botAI) { return new LadyVashjPassTheTaintedCoreAction(botAI); }
|
||||
|
||||
static Action* lady_vashj_destroy_tainted_core(
|
||||
PlayerbotAI* botAI) { return new LadyVashjDestroyTaintedCoreAction(botAI); }
|
||||
|
||||
static Action* lady_vashj_erase_core_passing_trackers(
|
||||
PlayerbotAI* botAI) { return new LadyVashjEraseCorePassingTrackersAction(botAI); }
|
||||
|
||||
static Action* lady_vashj_avoid_toxic_spores(
|
||||
PlayerbotAI* botAI) { return new LadyVashjAvoidToxicSporesAction(botAI); }
|
||||
|
||||
static Action* lady_vashj_use_free_action_abilities(
|
||||
PlayerbotAI* botAI) { return new LadyVashjUseFreeActionAbilitiesAction(botAI); }
|
||||
};
|
||||
|
||||
#endif
|
||||
325
src/Ai/Raid/SerpentshrineCavern/RaidSSCTriggerContext.h
Normal file
325
src/Ai/Raid/SerpentshrineCavern/RaidSSCTriggerContext.h
Normal file
@@ -0,0 +1,325 @@
|
||||
#ifndef _PLAYERBOT_RAIDSSCTRIGGERCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDSSCTRIGGERCONTEXT_H
|
||||
|
||||
#include "RaidSSCTriggers.h"
|
||||
#include "AiObjectContext.h"
|
||||
|
||||
class RaidSSCTriggerContext : public NamedObjectContext<Trigger>
|
||||
{
|
||||
public:
|
||||
RaidSSCTriggerContext()
|
||||
{
|
||||
// General
|
||||
creators["serpent shrine cavern bot is not in combat"] =
|
||||
&RaidSSCTriggerContext::serpent_shrine_cavern_bot_is_not_in_combat;
|
||||
|
||||
// Trash
|
||||
creators["underbog colossus spawned toxic pool after death"] =
|
||||
&RaidSSCTriggerContext::underbog_colossus_spawned_toxic_pool_after_death;
|
||||
|
||||
creators["greyheart tidecaller water elemental totem spawned"] =
|
||||
&RaidSSCTriggerContext::greyheart_tidecaller_water_elemental_totem_spawned;
|
||||
|
||||
// Hydross the Unstable <Duke of Currents>
|
||||
creators["hydross the unstable bot is frost tank"] =
|
||||
&RaidSSCTriggerContext::hydross_the_unstable_bot_is_frost_tank;
|
||||
|
||||
creators["hydross the unstable bot is nature tank"] =
|
||||
&RaidSSCTriggerContext::hydross_the_unstable_bot_is_nature_tank;
|
||||
|
||||
creators["hydross the unstable elementals spawned"] =
|
||||
&RaidSSCTriggerContext::hydross_the_unstable_elementals_spawned;
|
||||
|
||||
creators["hydross the unstable danger from water tombs"] =
|
||||
&RaidSSCTriggerContext::hydross_the_unstable_danger_from_water_tombs;
|
||||
|
||||
creators["hydross the unstable tank needs aggro upon phase change"] =
|
||||
&RaidSSCTriggerContext::hydross_the_unstable_tank_needs_aggro_upon_phase_change;
|
||||
|
||||
creators["hydross the unstable aggro resets upon phase change"] =
|
||||
&RaidSSCTriggerContext::hydross_the_unstable_aggro_resets_upon_phase_change;
|
||||
|
||||
creators["hydross the unstable need to manage timers"] =
|
||||
&RaidSSCTriggerContext::hydross_the_unstable_need_to_manage_timers;
|
||||
|
||||
// The Lurker Below
|
||||
creators["the lurker below spout is active"] =
|
||||
&RaidSSCTriggerContext::the_lurker_below_spout_is_active;
|
||||
|
||||
creators["the lurker below boss is active for main tank"] =
|
||||
&RaidSSCTriggerContext::the_lurker_below_boss_is_active_for_main_tank;
|
||||
|
||||
creators["the lurker below boss casts geyser"] =
|
||||
&RaidSSCTriggerContext::the_lurker_below_boss_casts_geyser;
|
||||
|
||||
creators["the lurker below boss is submerged"] =
|
||||
&RaidSSCTriggerContext::the_lurker_below_boss_is_submerged;
|
||||
|
||||
creators["the lurker below need to prepare timer for spout"] =
|
||||
&RaidSSCTriggerContext::the_lurker_below_need_to_prepare_timer_for_spout;
|
||||
|
||||
// Leotheras the Blind
|
||||
creators["leotheras the blind boss is inactive"] =
|
||||
&RaidSSCTriggerContext::leotheras_the_blind_boss_is_inactive;
|
||||
|
||||
creators["leotheras the blind boss transformed into demon form"] =
|
||||
&RaidSSCTriggerContext::leotheras_the_blind_boss_transformed_into_demon_form;
|
||||
|
||||
creators["leotheras the blind only warlock should tank demon form"] =
|
||||
&RaidSSCTriggerContext::leotheras_the_blind_only_warlock_should_tank_demon_form;
|
||||
|
||||
creators["leotheras the blind boss engaged by ranged"] =
|
||||
&RaidSSCTriggerContext::leotheras_the_blind_boss_engaged_by_ranged;
|
||||
|
||||
creators["leotheras the blind boss channeling whirlwind"] =
|
||||
&RaidSSCTriggerContext::leotheras_the_blind_boss_channeling_whirlwind;
|
||||
|
||||
creators["leotheras the blind bot has too many chaos blast stacks"] =
|
||||
&RaidSSCTriggerContext::leotheras_the_blind_bot_has_too_many_chaos_blast_stacks;
|
||||
|
||||
creators["leotheras the blind inner demon has awakened"] =
|
||||
&RaidSSCTriggerContext::leotheras_the_blind_inner_demon_has_awakened;
|
||||
|
||||
creators["leotheras the blind entered final phase"] =
|
||||
&RaidSSCTriggerContext::leotheras_the_blind_entered_final_phase;
|
||||
|
||||
creators["leotheras the blind demon form tank needs aggro"] =
|
||||
&RaidSSCTriggerContext::leotheras_the_blind_demon_form_tank_needs_aggro;
|
||||
|
||||
creators["leotheras the blind boss wipes aggro upon phase change"] =
|
||||
&RaidSSCTriggerContext::leotheras_the_blind_boss_wipes_aggro_upon_phase_change;
|
||||
|
||||
// Fathom-Lord Karathress
|
||||
creators["fathom-lord karathress boss engaged by main tank"] =
|
||||
&RaidSSCTriggerContext::fathom_lord_karathress_boss_engaged_by_main_tank;
|
||||
|
||||
creators["fathom-lord karathress caribdis engaged by first assist tank"] =
|
||||
&RaidSSCTriggerContext::fathom_lord_karathress_caribdis_engaged_by_first_assist_tank;
|
||||
|
||||
creators["fathom-lord karathress sharkkis engaged by second assist tank"] =
|
||||
&RaidSSCTriggerContext::fathom_lord_karathress_sharkkis_engaged_by_second_assist_tank;
|
||||
|
||||
creators["fathom-lord karathress tidalvess engaged by third assist tank"] =
|
||||
&RaidSSCTriggerContext::fathom_lord_karathress_tidalvess_engaged_by_third_assist_tank;
|
||||
|
||||
creators["fathom-lord karathress caribdis tank needs dedicated healer"] =
|
||||
&RaidSSCTriggerContext::fathom_lord_karathress_caribdis_tank_needs_dedicated_healer;
|
||||
|
||||
creators["fathom-lord karathress pulling bosses"] =
|
||||
&RaidSSCTriggerContext::fathom_lord_karathress_pulling_bosses;
|
||||
|
||||
creators["fathom-lord karathress determining kill order"] =
|
||||
&RaidSSCTriggerContext::fathom_lord_karathress_determining_kill_order;
|
||||
|
||||
creators["fathom-lord karathress tanks need to establish aggro"] =
|
||||
&RaidSSCTriggerContext::fathom_lord_karathress_tanks_need_to_establish_aggro;
|
||||
|
||||
// Morogrim Tidewalker
|
||||
creators["morogrim tidewalker boss engaged by main tank"] =
|
||||
&RaidSSCTriggerContext::morogrim_tidewalker_boss_engaged_by_main_tank;
|
||||
|
||||
creators["morogrim tidewalker pulling boss"] =
|
||||
&RaidSSCTriggerContext::morogrim_tidewalker_pulling_boss;
|
||||
|
||||
creators["morogrim tidewalker water globules are incoming"] =
|
||||
&RaidSSCTriggerContext::morogrim_tidewalker_water_globules_are_incoming;
|
||||
|
||||
// Lady Vashj <Coilfang Matron>
|
||||
creators["lady vashj boss engaged by main tank"] =
|
||||
&RaidSSCTriggerContext::lady_vashj_boss_engaged_by_main_tank;
|
||||
|
||||
creators["lady vashj boss engaged by ranged in phase 1"] =
|
||||
&RaidSSCTriggerContext::lady_vashj_boss_engaged_by_ranged_in_phase_1;
|
||||
|
||||
creators["lady vashj casts shock blast on highest aggro"] =
|
||||
&RaidSSCTriggerContext::lady_vashj_casts_shock_blast_on_highest_aggro;
|
||||
|
||||
creators["lady vashj bot has static charge"] =
|
||||
&RaidSSCTriggerContext::lady_vashj_bot_has_static_charge;
|
||||
|
||||
creators["lady vashj pulling boss in phase 1 and phase 3"] =
|
||||
&RaidSSCTriggerContext::lady_vashj_pulling_boss_in_phase_1_and_phase_3;
|
||||
|
||||
creators["lady vashj adds spawn in phase 2 and phase 3"] =
|
||||
&RaidSSCTriggerContext::lady_vashj_adds_spawn_in_phase_2_and_phase_3;
|
||||
|
||||
creators["lady vashj coilfang strider is approaching"] =
|
||||
&RaidSSCTriggerContext::lady_vashj_coilfang_strider_is_approaching;
|
||||
|
||||
creators["lady vashj tainted elemental cheat"] =
|
||||
&RaidSSCTriggerContext::lady_vashj_tainted_elemental_cheat;
|
||||
|
||||
creators["lady vashj tainted core was looted"] =
|
||||
&RaidSSCTriggerContext::lady_vashj_tainted_core_was_looted;
|
||||
|
||||
creators["lady vashj tainted core is unusable"] =
|
||||
&RaidSSCTriggerContext::lady_vashj_tainted_core_is_unusable;
|
||||
|
||||
creators["lady vashj need to reset core passing trackers"] =
|
||||
&RaidSSCTriggerContext::lady_vashj_need_to_reset_core_passing_trackers;
|
||||
|
||||
creators["lady vashj toxic sporebats are spewing poison clouds"] =
|
||||
&RaidSSCTriggerContext::lady_vashj_toxic_sporebats_are_spewing_poison_clouds;
|
||||
|
||||
creators["lady vashj bot is entangled in toxic spores or static charge"] =
|
||||
&RaidSSCTriggerContext::lady_vashj_bot_is_entangled_in_toxic_spores_or_static_charge;
|
||||
}
|
||||
|
||||
private:
|
||||
// General
|
||||
static Trigger* serpent_shrine_cavern_bot_is_not_in_combat(
|
||||
PlayerbotAI* botAI) { return new SerpentShrineCavernBotIsNotInCombatTrigger(botAI); }
|
||||
|
||||
// Trash
|
||||
static Trigger* underbog_colossus_spawned_toxic_pool_after_death(
|
||||
PlayerbotAI* botAI) { return new UnderbogColossusSpawnedToxicPoolAfterDeathTrigger(botAI); }
|
||||
|
||||
static Trigger* greyheart_tidecaller_water_elemental_totem_spawned(
|
||||
PlayerbotAI* botAI) { return new GreyheartTidecallerWaterElementalTotemSpawnedTrigger(botAI); }
|
||||
|
||||
// Hydross the Unstable <Duke of Currents>
|
||||
static Trigger* hydross_the_unstable_bot_is_frost_tank(
|
||||
PlayerbotAI* botAI) { return new HydrossTheUnstableBotIsFrostTankTrigger(botAI); }
|
||||
|
||||
static Trigger* hydross_the_unstable_bot_is_nature_tank(
|
||||
PlayerbotAI* botAI) { return new HydrossTheUnstableBotIsNatureTankTrigger(botAI); }
|
||||
|
||||
static Trigger* hydross_the_unstable_elementals_spawned(
|
||||
PlayerbotAI* botAI) { return new HydrossTheUnstableElementalsSpawnedTrigger(botAI); }
|
||||
|
||||
static Trigger* hydross_the_unstable_danger_from_water_tombs(
|
||||
PlayerbotAI* botAI) { return new HydrossTheUnstableDangerFromWaterTombsTrigger(botAI); }
|
||||
|
||||
static Trigger* hydross_the_unstable_tank_needs_aggro_upon_phase_change(
|
||||
PlayerbotAI* botAI) { return new HydrossTheUnstableTankNeedsAggroUponPhaseChangeTrigger(botAI); }
|
||||
|
||||
static Trigger* hydross_the_unstable_aggro_resets_upon_phase_change(
|
||||
PlayerbotAI* botAI) { return new HydrossTheUnstableAggroResetsUponPhaseChangeTrigger(botAI); }
|
||||
|
||||
static Trigger* hydross_the_unstable_need_to_manage_timers(
|
||||
PlayerbotAI* botAI) { return new HydrossTheUnstableNeedToManageTimersTrigger(botAI); }
|
||||
|
||||
// The Lurker Below
|
||||
static Trigger* the_lurker_below_spout_is_active(
|
||||
PlayerbotAI* botAI) { return new TheLurkerBelowSpoutIsActiveTrigger(botAI); }
|
||||
|
||||
static Trigger* the_lurker_below_boss_is_active_for_main_tank(
|
||||
PlayerbotAI* botAI) { return new TheLurkerBelowBossIsActiveForMainTankTrigger(botAI); }
|
||||
|
||||
static Trigger* the_lurker_below_boss_casts_geyser(
|
||||
PlayerbotAI* botAI) { return new TheLurkerBelowBossCastsGeyserTrigger(botAI); }
|
||||
|
||||
static Trigger* the_lurker_below_boss_is_submerged(
|
||||
PlayerbotAI* botAI) { return new TheLurkerBelowBossIsSubmergedTrigger(botAI); }
|
||||
|
||||
static Trigger* the_lurker_below_need_to_prepare_timer_for_spout(
|
||||
PlayerbotAI* botAI) { return new TheLurkerBelowNeedToPrepareTimerForSpoutTrigger(botAI); }
|
||||
|
||||
// Leotheras the Blind
|
||||
static Trigger* leotheras_the_blind_boss_is_inactive(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindBossIsInactiveTrigger(botAI); }
|
||||
|
||||
static Trigger* leotheras_the_blind_boss_transformed_into_demon_form(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindBossTransformedIntoDemonFormTrigger(botAI); }
|
||||
|
||||
static Trigger* leotheras_the_blind_only_warlock_should_tank_demon_form(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindOnlyWarlockShouldTankDemonFormTrigger(botAI); }
|
||||
|
||||
static Trigger* leotheras_the_blind_boss_engaged_by_ranged(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindBossEngagedByRangedTrigger(botAI); }
|
||||
|
||||
static Trigger* leotheras_the_blind_boss_channeling_whirlwind(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindBossChannelingWhirlwindTrigger(botAI); }
|
||||
|
||||
static Trigger* leotheras_the_blind_bot_has_too_many_chaos_blast_stacks(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindBotHasTooManyChaosBlastStacksTrigger(botAI); }
|
||||
|
||||
static Trigger* leotheras_the_blind_inner_demon_has_awakened(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindInnerDemonHasAwakenedTrigger(botAI); }
|
||||
|
||||
static Trigger* leotheras_the_blind_entered_final_phase(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindEnteredFinalPhaseTrigger(botAI); }
|
||||
|
||||
static Trigger* leotheras_the_blind_demon_form_tank_needs_aggro(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindDemonFormTankNeedsAggro(botAI); }
|
||||
|
||||
static Trigger* leotheras_the_blind_boss_wipes_aggro_upon_phase_change(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindBossWipesAggroUponPhaseChangeTrigger(botAI); }
|
||||
|
||||
// Fathom-Lord Karathress
|
||||
static Trigger* fathom_lord_karathress_boss_engaged_by_main_tank(
|
||||
PlayerbotAI* botAI) { return new FathomLordKarathressBossEngagedByMainTankTrigger(botAI); }
|
||||
|
||||
static Trigger* fathom_lord_karathress_caribdis_engaged_by_first_assist_tank(
|
||||
PlayerbotAI* botAI) { return new FathomLordKarathressCaribdisEngagedByFirstAssistTankTrigger(botAI); }
|
||||
|
||||
static Trigger* fathom_lord_karathress_sharkkis_engaged_by_second_assist_tank(
|
||||
PlayerbotAI* botAI) { return new FathomLordKarathressSharkkisEngagedBySecondAssistTankTrigger(botAI); }
|
||||
|
||||
static Trigger* fathom_lord_karathress_tidalvess_engaged_by_third_assist_tank(
|
||||
PlayerbotAI* botAI) { return new FathomLordKarathressTidalvessEngagedByThirdAssistTankTrigger(botAI); }
|
||||
|
||||
static Trigger* fathom_lord_karathress_caribdis_tank_needs_dedicated_healer(
|
||||
PlayerbotAI* botAI) { return new FathomLordKarathressCaribdisTankNeedsDedicatedHealerTrigger(botAI); }
|
||||
|
||||
static Trigger* fathom_lord_karathress_pulling_bosses(
|
||||
PlayerbotAI* botAI) { return new FathomLordKarathressPullingBossesTrigger(botAI); }
|
||||
|
||||
static Trigger* fathom_lord_karathress_determining_kill_order(
|
||||
PlayerbotAI* botAI) { return new FathomLordKarathressDeterminingKillOrderTrigger(botAI); }
|
||||
|
||||
static Trigger* fathom_lord_karathress_tanks_need_to_establish_aggro(
|
||||
PlayerbotAI* botAI) { return new FathomLordKarathressTanksNeedToEstablishAggroTrigger(botAI); }
|
||||
|
||||
// Morogrim Tidewalker
|
||||
static Trigger* morogrim_tidewalker_boss_engaged_by_main_tank(
|
||||
PlayerbotAI* botAI) { return new MorogrimTidewalkerBossEngagedByMainTankTrigger(botAI); }
|
||||
|
||||
static Trigger* morogrim_tidewalker_pulling_boss(
|
||||
PlayerbotAI* botAI) { return new MorogrimTidewalkerPullingBossTrigger(botAI); }
|
||||
|
||||
static Trigger* morogrim_tidewalker_water_globules_are_incoming(
|
||||
PlayerbotAI* botAI) { return new MorogrimTidewalkerWaterGlobulesAreIncomingTrigger(botAI); }
|
||||
|
||||
// Lady Vashj <Coilfang Matron>
|
||||
static Trigger* lady_vashj_boss_engaged_by_main_tank(
|
||||
PlayerbotAI* botAI) { return new LadyVashjBossEngagedByMainTankTrigger(botAI); }
|
||||
|
||||
static Trigger* lady_vashj_boss_engaged_by_ranged_in_phase_1(
|
||||
PlayerbotAI* botAI) { return new LadyVashjBossEngagedByRangedInPhase1Trigger(botAI); }
|
||||
|
||||
static Trigger* lady_vashj_casts_shock_blast_on_highest_aggro(
|
||||
PlayerbotAI* botAI) { return new LadyVashjCastsShockBlastOnHighestAggroTrigger(botAI); }
|
||||
|
||||
static Trigger* lady_vashj_bot_has_static_charge(
|
||||
PlayerbotAI* botAI) { return new LadyVashjBotHasStaticChargeTrigger(botAI); }
|
||||
|
||||
static Trigger* lady_vashj_pulling_boss_in_phase_1_and_phase_3(
|
||||
PlayerbotAI* botAI) { return new LadyVashjPullingBossInPhase1AndPhase3Trigger(botAI); }
|
||||
|
||||
static Trigger* lady_vashj_adds_spawn_in_phase_2_and_phase_3(
|
||||
PlayerbotAI* botAI) { return new LadyVashjAddsSpawnInPhase2AndPhase3Trigger(botAI); }
|
||||
|
||||
static Trigger* lady_vashj_coilfang_strider_is_approaching(
|
||||
PlayerbotAI* botAI) { return new LadyVashjCoilfangStriderIsApproachingTrigger(botAI); }
|
||||
|
||||
static Trigger* lady_vashj_tainted_elemental_cheat(
|
||||
PlayerbotAI* botAI) { return new LadyVashjTaintedElementalCheatTrigger(botAI); }
|
||||
|
||||
static Trigger* lady_vashj_tainted_core_was_looted(
|
||||
PlayerbotAI* botAI) { return new LadyVashjTaintedCoreWasLootedTrigger(botAI); }
|
||||
|
||||
static Trigger* lady_vashj_tainted_core_is_unusable(
|
||||
PlayerbotAI* botAI) { return new LadyVashjTaintedCoreIsUnusableTrigger(botAI); }
|
||||
|
||||
static Trigger* lady_vashj_need_to_reset_core_passing_trackers(
|
||||
PlayerbotAI* botAI) { return new LadyVashjNeedToResetCorePassingTrackersTrigger(botAI); }
|
||||
|
||||
static Trigger* lady_vashj_toxic_sporebats_are_spewing_poison_clouds(
|
||||
PlayerbotAI* botAI) { return new LadyVashjToxicSporebatsAreSpewingPoisonCloudsTrigger(botAI); }
|
||||
|
||||
static Trigger* lady_vashj_bot_is_entangled_in_toxic_spores_or_static_charge(
|
||||
PlayerbotAI* botAI) { return new LadyVashjBotIsEntangledInToxicSporesOrStaticChargeTrigger(botAI); }
|
||||
};
|
||||
|
||||
#endif
|
||||
206
src/Ai/Raid/SerpentshrineCavern/Strategy/RaidSSCStrategy.cpp
Normal file
206
src/Ai/Raid/SerpentshrineCavern/Strategy/RaidSSCStrategy.cpp
Normal file
@@ -0,0 +1,206 @@
|
||||
#include "RaidSSCStrategy.h"
|
||||
#include "RaidSSCMultipliers.h"
|
||||
|
||||
void RaidSSCStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
// General
|
||||
triggers.push_back(new TriggerNode("serpent shrine cavern bot is not in combat", {
|
||||
NextAction("serpent shrine cavern erase timers and trackers", ACTION_EMERGENCY + 11) }));
|
||||
|
||||
// Trash Mobs
|
||||
triggers.push_back(new TriggerNode("underbog colossus spawned toxic pool after death", {
|
||||
NextAction("underbog colossus escape toxic pool", ACTION_EMERGENCY + 10) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("greyheart tidecaller water elemental totem spawned", {
|
||||
NextAction("greyheart tidecaller mark water elemental totem", ACTION_RAID + 1) }));
|
||||
|
||||
// Hydross the Unstable <Duke of Currents>
|
||||
triggers.push_back(new TriggerNode("hydross the unstable bot is frost tank", {
|
||||
NextAction("hydross the unstable position frost tank", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("hydross the unstable bot is nature tank", {
|
||||
NextAction("hydross the unstable position nature tank", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("hydross the unstable elementals spawned", {
|
||||
NextAction("hydross the unstable prioritize elemental adds", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("hydross the unstable danger from water tombs", {
|
||||
NextAction("hydross the unstable frost phase spread out", ACTION_EMERGENCY + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("hydross the unstable tank needs aggro upon phase change", {
|
||||
NextAction("hydross the unstable misdirect boss to tank", ACTION_EMERGENCY + 6) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("hydross the unstable aggro resets upon phase change", {
|
||||
NextAction("hydross the unstable stop dps upon phase change", ACTION_EMERGENCY + 9) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("hydross the unstable need to manage timers", {
|
||||
NextAction("hydross the unstable manage timers", ACTION_EMERGENCY + 10) }));
|
||||
|
||||
// The Lurker Below
|
||||
triggers.push_back(new TriggerNode("the lurker below spout is active", {
|
||||
NextAction("the lurker below run around behind boss", ACTION_EMERGENCY + 6) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("the lurker below boss is active for main tank", {
|
||||
NextAction("the lurker below position main tank", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("the lurker below boss casts geyser", {
|
||||
NextAction("the lurker below spread ranged in arc", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("the lurker below boss is submerged", {
|
||||
NextAction("the lurker below tanks pick up adds", ACTION_EMERGENCY + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("the lurker below need to prepare timer for spout", {
|
||||
NextAction("the lurker below manage spout timer", ACTION_EMERGENCY + 10) }));
|
||||
|
||||
// Leotheras the Blind
|
||||
triggers.push_back(new TriggerNode("leotheras the blind boss is inactive", {
|
||||
NextAction("leotheras the blind target spellbinders", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("leotheras the blind boss transformed into demon form", {
|
||||
NextAction("leotheras the blind demon form tank attack boss", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("leotheras the blind only warlock should tank demon form", {
|
||||
NextAction("leotheras the blind melee tanks don't attack demon form", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("leotheras the blind boss engaged by ranged", {
|
||||
NextAction("leotheras the blind position ranged", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("leotheras the blind boss channeling whirlwind", {
|
||||
NextAction("leotheras the blind run away from whirlwind", ACTION_EMERGENCY + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("leotheras the blind bot has too many chaos blast stacks", {
|
||||
NextAction("leotheras the blind melee dps run away from boss", ACTION_EMERGENCY + 6) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("leotheras the blind inner demon has awakened", {
|
||||
NextAction("leotheras the blind destroy inner demon", ACTION_EMERGENCY + 7) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("leotheras the blind entered final phase", {
|
||||
NextAction("leotheras the blind final phase assign dps priority", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("leotheras the blind demon form tank needs aggro", {
|
||||
NextAction("leotheras the blind misdirect boss to demon form tank", ACTION_RAID + 2) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("leotheras the blind boss wipes aggro upon phase change", {
|
||||
NextAction("leotheras the blind manage dps wait timers", ACTION_EMERGENCY + 10) }));
|
||||
|
||||
// Fathom-Lord Karathress
|
||||
triggers.push_back(new TriggerNode("fathom-lord karathress boss engaged by main tank", {
|
||||
NextAction("fathom-lord karathress main tank position boss", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("fathom-lord karathress caribdis engaged by first assist tank", {
|
||||
NextAction("fathom-lord karathress first assist tank position caribdis", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("fathom-lord karathress sharkkis engaged by second assist tank", {
|
||||
NextAction("fathom-lord karathress second assist tank position sharkkis", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("fathom-lord karathress tidalvess engaged by third assist tank", {
|
||||
NextAction("fathom-lord karathress third assist tank position tidalvess", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("fathom-lord karathress caribdis tank needs dedicated healer", {
|
||||
NextAction("fathom-lord karathress position caribdis tank healer", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("fathom-lord karathress pulling bosses", {
|
||||
NextAction("fathom-lord karathress misdirect bosses to tanks", ACTION_RAID + 2) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("fathom-lord karathress determining kill order", {
|
||||
NextAction("fathom-lord karathress assign dps priority", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("fathom-lord karathress tanks need to establish aggro", {
|
||||
NextAction("fathom-lord karathress manage dps timer", ACTION_EMERGENCY + 10) }));
|
||||
|
||||
// Morogrim Tidewalker
|
||||
triggers.push_back(new TriggerNode("morogrim tidewalker boss engaged by main tank", {
|
||||
NextAction("morogrim tidewalker move boss to tank position", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("morogrim tidewalker water globules are incoming", {
|
||||
NextAction("morogrim tidewalker phase 2 reposition ranged", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("morogrim tidewalker pulling boss", {
|
||||
NextAction("morogrim tidewalker misdirect boss to main tank", ACTION_RAID + 1) }));
|
||||
|
||||
// Lady Vashj <Coilfang Matron>
|
||||
triggers.push_back(new TriggerNode("lady vashj boss engaged by main tank", {
|
||||
NextAction("lady vashj main tank position boss", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("lady vashj boss engaged by ranged in phase 1", {
|
||||
NextAction("lady vashj phase 1 spread ranged in arc", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("lady vashj casts shock blast on highest aggro", {
|
||||
NextAction("lady vashj set grounding totem in main tank group", ACTION_EMERGENCY + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("lady vashj bot has static charge", {
|
||||
NextAction("lady vashj static charge move away from group", ACTION_EMERGENCY + 7) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("lady vashj pulling boss in phase 1 and phase 3", {
|
||||
NextAction("lady vashj misdirect boss to main tank", ACTION_RAID + 2) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("lady vashj tainted elemental cheat", {
|
||||
NextAction("lady vashj teleport to tainted elemental", ACTION_EMERGENCY + 10),
|
||||
NextAction("lady vashj loot tainted core", ACTION_EMERGENCY + 10) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("lady vashj tainted core was looted", {
|
||||
NextAction("lady vashj pass the tainted core", ACTION_EMERGENCY + 10) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("lady vashj tainted core is unusable", {
|
||||
NextAction("lady vashj destroy tainted core", ACTION_EMERGENCY + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("lady vashj need to reset core passing trackers", {
|
||||
NextAction("lady vashj erase core passing trackers", ACTION_EMERGENCY + 10) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("lady vashj adds spawn in phase 2 and phase 3", {
|
||||
NextAction("lady vashj assign phase 2 and phase 3 dps priority", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("lady vashj coilfang strider is approaching", {
|
||||
NextAction("lady vashj misdirect strider to first assist tank", ACTION_EMERGENCY + 2),
|
||||
NextAction("lady vashj tank attack and move away strider", ACTION_EMERGENCY + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("lady vashj toxic sporebats are spewing poison clouds", {
|
||||
NextAction("lady vashj avoid toxic spores", ACTION_EMERGENCY + 6) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("lady vashj bot is entangled in toxic spores or static charge", {
|
||||
NextAction("lady vashj use free action abilities", ACTION_EMERGENCY + 7) }));
|
||||
}
|
||||
|
||||
void RaidSSCStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||
{
|
||||
// Trash Mobs
|
||||
multipliers.push_back(new UnderbogColossusEscapeToxicPoolMultiplier(botAI));
|
||||
|
||||
// Hydross the Unstable <Duke of Currents>
|
||||
multipliers.push_back(new HydrossTheUnstableDisableTankActionsMultiplier(botAI));
|
||||
multipliers.push_back(new HydrossTheUnstableWaitForDpsMultiplier(botAI));
|
||||
multipliers.push_back(new HydrossTheUnstableControlMisdirectionMultiplier(botAI));
|
||||
|
||||
// The Lurker Below
|
||||
multipliers.push_back(new TheLurkerBelowStayAwayFromSpoutMultiplier(botAI));
|
||||
multipliers.push_back(new TheLurkerBelowMaintainRangedSpreadMultiplier(botAI));
|
||||
multipliers.push_back(new TheLurkerBelowDisableTankAssistMultiplier(botAI));
|
||||
|
||||
// Leotheras the Blind
|
||||
multipliers.push_back(new LeotherasTheBlindAvoidWhirlwindMultiplier(botAI));
|
||||
multipliers.push_back(new LeotherasTheBlindDisableTankActionsMultiplier(botAI));
|
||||
multipliers.push_back(new LeotherasTheBlindMeleeDpsAvoidChaosBlastMultiplier(botAI));
|
||||
multipliers.push_back(new LeotherasTheBlindFocusOnInnerDemonMultiplier(botAI));
|
||||
multipliers.push_back(new LeotherasTheBlindWaitForDpsMultiplier(botAI));
|
||||
multipliers.push_back(new LeotherasTheBlindDelayBloodlustAndHeroismMultiplier(botAI));
|
||||
|
||||
// Fathom-Lord Karathress
|
||||
multipliers.push_back(new FathomLordKarathressDisableTankActionsMultiplier(botAI));
|
||||
multipliers.push_back(new FathomLordKarathressDisableAoeMultiplier(botAI));
|
||||
multipliers.push_back(new FathomLordKarathressControlMisdirectionMultiplier(botAI));
|
||||
multipliers.push_back(new FathomLordKarathressWaitForDpsMultiplier(botAI));
|
||||
multipliers.push_back(new FathomLordKarathressCaribdisTankHealerMaintainPositionMultiplier(botAI));
|
||||
|
||||
// Morogrim Tidewalker
|
||||
multipliers.push_back(new MorogrimTidewalkerDelayBloodlustAndHeroismMultiplier(botAI));
|
||||
multipliers.push_back(new MorogrimTidewalkerDisableTankActionsMultiplier(botAI));
|
||||
multipliers.push_back(new MorogrimTidewalkerMaintainPhase2StackingMultiplier(botAI));
|
||||
|
||||
// Lady Vashj <Coilfang Matron>
|
||||
multipliers.push_back(new LadyVashjDelayCooldownsMultiplier(botAI));
|
||||
multipliers.push_back(new LadyVashjMaintainPhase1RangedSpreadMultiplier(botAI));
|
||||
multipliers.push_back(new LadyVashjStaticChargeStayAwayFromGroupMultiplier(botAI));
|
||||
multipliers.push_back(new LadyVashjDoNotLootTheTaintedCoreMultiplier(botAI));
|
||||
multipliers.push_back(new LadyVashjCorePassersPrioritizePositioningMultiplier(botAI));
|
||||
multipliers.push_back(new LadyVashjDisableAutomaticTargetingAndMovementModifier(botAI));
|
||||
}
|
||||
18
src/Ai/Raid/SerpentshrineCavern/Strategy/RaidSSCStrategy.h
Normal file
18
src/Ai/Raid/SerpentshrineCavern/Strategy/RaidSSCStrategy.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef _PLAYERBOT_RAIDSSCSTRATEGY_H_
|
||||
#define _PLAYERBOT_RAIDSSCSTRATEGY_H_
|
||||
|
||||
#include "Strategy.h"
|
||||
#include "Multiplier.h"
|
||||
|
||||
class RaidSSCStrategy : public Strategy
|
||||
{
|
||||
public:
|
||||
RaidSSCStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
|
||||
|
||||
std::string const getName() override { return "ssc"; }
|
||||
|
||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||
void InitMultipliers(std::vector<Multiplier*>& multipliers) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
670
src/Ai/Raid/SerpentshrineCavern/Trigger/RaidSSCTriggers.cpp
Normal file
670
src/Ai/Raid/SerpentshrineCavern/Trigger/RaidSSCTriggers.cpp
Normal file
@@ -0,0 +1,670 @@
|
||||
#include "RaidSSCTriggers.h"
|
||||
#include "RaidSSCHelpers.h"
|
||||
#include "RaidSSCActions.h"
|
||||
#include "AiFactory.h"
|
||||
#include "Corpse.h"
|
||||
#include "LootObjectStack.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "Playerbots.h"
|
||||
#include "RaidBossHelpers.h"
|
||||
|
||||
using namespace SerpentShrineCavernHelpers;
|
||||
|
||||
// General
|
||||
bool SerpentShrineCavernBotIsNotInCombatTrigger::IsActive()
|
||||
{
|
||||
return !bot->IsInCombat();
|
||||
}
|
||||
|
||||
// Trash Mobs
|
||||
|
||||
bool UnderbogColossusSpawnedToxicPoolAfterDeathTrigger::IsActive()
|
||||
{
|
||||
return bot->HasAura(SPELL_TOXIC_POOL);
|
||||
}
|
||||
|
||||
bool GreyheartTidecallerWaterElementalTotemSpawnedTrigger::IsActive()
|
||||
{
|
||||
return botAI->IsDps(bot) &&
|
||||
GetFirstAliveUnitByEntry(botAI, NPC_WATER_ELEMENTAL_TOTEM);
|
||||
}
|
||||
|
||||
// Hydross the Unstable <Duke of Currents>
|
||||
|
||||
bool HydrossTheUnstableBotIsFrostTankTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "hydross the unstable") &&
|
||||
botAI->IsMainTank(bot);
|
||||
}
|
||||
|
||||
bool HydrossTheUnstableBotIsNatureTankTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "hydross the unstable") &&
|
||||
botAI->IsAssistTankOfIndex(bot, 0, true);
|
||||
}
|
||||
|
||||
bool HydrossTheUnstableElementalsSpawnedTrigger::IsActive()
|
||||
{
|
||||
Unit* hydross = AI_VALUE2(Unit*, "find target", "hydross the unstable");
|
||||
if (hydross && hydross->GetHealthPct() < 10.0f)
|
||||
return false;
|
||||
|
||||
if (!AI_VALUE2(Unit*, "find target", "pure spawn of hydross") &&
|
||||
!AI_VALUE2(Unit*, "find target", "tainted spawn of hydross"))
|
||||
return false;
|
||||
|
||||
return !botAI->IsHeal(bot) && !botAI->IsMainTank(bot) &&
|
||||
!botAI->IsAssistTankOfIndex(bot, 0, true);
|
||||
}
|
||||
|
||||
bool HydrossTheUnstableDangerFromWaterTombsTrigger::IsActive()
|
||||
{
|
||||
return botAI->IsRanged(bot) &&
|
||||
AI_VALUE2(Unit*, "find target", "hydross the unstable");
|
||||
}
|
||||
|
||||
bool HydrossTheUnstableTankNeedsAggroUponPhaseChangeTrigger::IsActive()
|
||||
{
|
||||
return bot->getClass() == CLASS_HUNTER &&
|
||||
AI_VALUE2(Unit*, "find target", "hydross the unstable");
|
||||
}
|
||||
|
||||
bool HydrossTheUnstableAggroResetsUponPhaseChangeTrigger::IsActive()
|
||||
{
|
||||
if (!AI_VALUE2(Unit*, "find target", "hydross the unstable"))
|
||||
return false;
|
||||
|
||||
return bot->getClass() != CLASS_HUNTER &&
|
||||
!botAI->IsHeal(bot) &&
|
||||
!botAI->IsMainTank(bot) &&
|
||||
!botAI->IsAssistTankOfIndex(bot, 0, true);
|
||||
}
|
||||
|
||||
bool HydrossTheUnstableNeedToManageTimersTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "hydross the unstable") &&
|
||||
IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID, nullptr);
|
||||
}
|
||||
|
||||
// The Lurker Below
|
||||
|
||||
bool TheLurkerBelowSpoutIsActiveTrigger::IsActive()
|
||||
{
|
||||
Unit* lurker = AI_VALUE2(Unit*, "find target", "the lurker below");
|
||||
if (!lurker)
|
||||
return false;
|
||||
|
||||
const time_t now = std::time(nullptr);
|
||||
|
||||
auto it = lurkerSpoutTimer.find(lurker->GetMap()->GetInstanceId());
|
||||
return it != lurkerSpoutTimer.end() && it->second > now;
|
||||
}
|
||||
|
||||
bool TheLurkerBelowBossIsActiveForMainTankTrigger::IsActive()
|
||||
{
|
||||
Unit* lurker = AI_VALUE2(Unit*, "find target", "the lurker below");
|
||||
if (!lurker)
|
||||
return false;
|
||||
|
||||
if (!botAI->IsMainTank(bot))
|
||||
return false;
|
||||
|
||||
const time_t now = std::time(nullptr);
|
||||
|
||||
auto it = lurkerSpoutTimer.find(lurker->GetMap()->GetInstanceId());
|
||||
return lurker->getStandState() != UNIT_STAND_STATE_SUBMERGED &&
|
||||
(it == lurkerSpoutTimer.end() || it->second <= now);
|
||||
}
|
||||
|
||||
bool TheLurkerBelowBossCastsGeyserTrigger::IsActive()
|
||||
{
|
||||
if (!botAI->IsRanged(bot))
|
||||
return false;
|
||||
|
||||
Unit* lurker = AI_VALUE2(Unit*, "find target", "the lurker below");
|
||||
if (!lurker)
|
||||
return false;
|
||||
|
||||
const time_t now = std::time(nullptr);
|
||||
|
||||
auto it = lurkerSpoutTimer.find(lurker->GetMap()->GetInstanceId());
|
||||
return lurker->getStandState() != UNIT_STAND_STATE_SUBMERGED &&
|
||||
(it == lurkerSpoutTimer.end() || it->second <= now);
|
||||
}
|
||||
|
||||
// Trigger will be active only if there are at least 3 tanks in the raid
|
||||
bool TheLurkerBelowBossIsSubmergedTrigger::IsActive()
|
||||
{
|
||||
Unit* lurker = AI_VALUE2(Unit*, "find target", "the lurker below");
|
||||
if (!lurker || lurker->getStandState() != UNIT_STAND_STATE_SUBMERGED)
|
||||
return false;
|
||||
|
||||
Player* mainTank = nullptr;
|
||||
Player* firstAssistTank = nullptr;
|
||||
Player* secondAssistTank = nullptr;
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive())
|
||||
continue;
|
||||
|
||||
PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member);
|
||||
if (!memberAI)
|
||||
continue;
|
||||
|
||||
if (!mainTank && memberAI->IsMainTank(member))
|
||||
mainTank = member;
|
||||
else if (!firstAssistTank && memberAI->IsAssistTankOfIndex(member, 0, true))
|
||||
firstAssistTank = member;
|
||||
else if (!secondAssistTank && memberAI->IsAssistTankOfIndex(member, 1, true))
|
||||
secondAssistTank = member;
|
||||
}
|
||||
|
||||
if (!mainTank || !firstAssistTank || !secondAssistTank)
|
||||
return false;
|
||||
|
||||
return bot == mainTank || bot == firstAssistTank || bot == secondAssistTank;
|
||||
}
|
||||
|
||||
bool TheLurkerBelowNeedToPrepareTimerForSpoutTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "the lurker below") &&
|
||||
IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID, nullptr);
|
||||
}
|
||||
|
||||
// Leotheras the Blind
|
||||
|
||||
bool LeotherasTheBlindBossIsInactiveTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "greyheart spellbinder");
|
||||
}
|
||||
|
||||
bool LeotherasTheBlindBossTransformedIntoDemonFormTrigger::IsActive()
|
||||
{
|
||||
if (!AI_VALUE2(Unit*, "find target", "leotheras the blind"))
|
||||
return false;
|
||||
|
||||
if (GetLeotherasDemonFormTank(bot) != bot)
|
||||
return false;
|
||||
|
||||
return GetActiveLeotherasDemon(botAI);
|
||||
}
|
||||
|
||||
bool LeotherasTheBlindOnlyWarlockShouldTankDemonFormTrigger::IsActive()
|
||||
{
|
||||
if (botAI->IsRanged(bot) || !botAI->IsTank(bot))
|
||||
return false;
|
||||
|
||||
if (!AI_VALUE2(Unit*, "find target", "leotheras the blind"))
|
||||
return false;
|
||||
|
||||
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||
return false;
|
||||
|
||||
if (!GetLeotherasDemonFormTank(bot))
|
||||
return false;
|
||||
|
||||
return GetPhase2LeotherasDemon(botAI);
|
||||
}
|
||||
|
||||
bool LeotherasTheBlindBossEngagedByRangedTrigger::IsActive()
|
||||
{
|
||||
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||
return false;
|
||||
|
||||
if (!botAI->IsRanged(bot))
|
||||
return false;
|
||||
|
||||
Unit* leotheras = AI_VALUE2(Unit*, "find target", "leotheras the blind");
|
||||
if (!leotheras)
|
||||
return false;
|
||||
|
||||
return !leotheras->HasAura(SPELL_LEOTHERAS_BANISHED) &&
|
||||
!leotheras->HasAura(SPELL_WHIRLWIND) &&
|
||||
!leotheras->HasAura(SPELL_WHIRLWIND_CHANNEL);
|
||||
}
|
||||
|
||||
bool LeotherasTheBlindBossChannelingWhirlwindTrigger::IsActive()
|
||||
{
|
||||
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||
return false;
|
||||
|
||||
if (botAI->IsTank(bot) && botAI->IsMelee(bot))
|
||||
return false;
|
||||
|
||||
Unit* leotheras = AI_VALUE2(Unit*, "find target", "leotheras the blind");
|
||||
if (!leotheras || leotheras->HasAura(SPELL_LEOTHERAS_BANISHED))
|
||||
return false;
|
||||
|
||||
return leotheras->HasAura(SPELL_WHIRLWIND) ||
|
||||
leotheras->HasAura(SPELL_WHIRLWIND_CHANNEL);
|
||||
}
|
||||
|
||||
bool LeotherasTheBlindBotHasTooManyChaosBlastStacksTrigger::IsActive()
|
||||
{
|
||||
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||
return false;
|
||||
|
||||
if (botAI->IsRanged(bot))
|
||||
return false;
|
||||
|
||||
Aura* chaosBlast = bot->GetAura(SPELL_CHAOS_BLAST);
|
||||
if (!chaosBlast || chaosBlast->GetStackAmount() < 5)
|
||||
return false;
|
||||
|
||||
if (!GetLeotherasDemonFormTank(bot) && botAI->IsMainTank(bot))
|
||||
return false;
|
||||
|
||||
return GetPhase2LeotherasDemon(botAI);
|
||||
}
|
||||
|
||||
bool LeotherasTheBlindInnerDemonHasAwakenedTrigger::IsActive()
|
||||
{
|
||||
return bot->HasAura(SPELL_INSIDIOUS_WHISPER) &&
|
||||
GetLeotherasDemonFormTank(bot) != bot;
|
||||
}
|
||||
|
||||
bool LeotherasTheBlindEnteredFinalPhaseTrigger::IsActive()
|
||||
{
|
||||
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||
return false;
|
||||
|
||||
if (botAI->IsHeal(bot))
|
||||
return false;
|
||||
|
||||
if (GetLeotherasDemonFormTank(bot) == bot)
|
||||
return false;
|
||||
|
||||
return GetPhase3LeotherasDemon(botAI) &&
|
||||
GetLeotherasHuman(botAI);
|
||||
}
|
||||
|
||||
bool LeotherasTheBlindDemonFormTankNeedsAggro::IsActive()
|
||||
{
|
||||
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||
return false;
|
||||
|
||||
if (bot->getClass() != CLASS_HUNTER)
|
||||
return false;
|
||||
|
||||
return AI_VALUE2(Unit*, "find target", "leotheras the blind");
|
||||
}
|
||||
|
||||
bool LeotherasTheBlindBossWipesAggroUponPhaseChangeTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "leotheras the blind") &&
|
||||
IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID, nullptr);
|
||||
}
|
||||
|
||||
// Fathom-Lord Karathress
|
||||
|
||||
bool FathomLordKarathressBossEngagedByMainTankTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "fathom-lord karathress") &&
|
||||
botAI->IsMainTank(bot);
|
||||
}
|
||||
|
||||
bool FathomLordKarathressCaribdisEngagedByFirstAssistTankTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "fathom-guard caribdis") &&
|
||||
botAI->IsAssistTankOfIndex(bot, 0, false);
|
||||
}
|
||||
|
||||
bool FathomLordKarathressSharkkisEngagedBySecondAssistTankTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "fathom-guard sharkkis") &&
|
||||
botAI->IsAssistTankOfIndex(bot, 1, false);
|
||||
}
|
||||
|
||||
bool FathomLordKarathressTidalvessEngagedByThirdAssistTankTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "fathom-guard tidalvess") &&
|
||||
botAI->IsAssistTankOfIndex(bot, 2, false);
|
||||
}
|
||||
|
||||
bool FathomLordKarathressCaribdisTankNeedsDedicatedHealerTrigger::IsActive()
|
||||
{
|
||||
Unit* caribdis = AI_VALUE2(Unit*, "find target", "fathom-guard caribdis");
|
||||
if (!caribdis)
|
||||
return false;
|
||||
|
||||
if (!botAI->IsAssistHealOfIndex(bot, 0, true))
|
||||
return false;
|
||||
|
||||
Player* firstAssistTank = nullptr;
|
||||
if (Group* group = bot->GetGroup())
|
||||
{
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive())
|
||||
continue;
|
||||
|
||||
if (botAI->IsAssistTankOfIndex(member, 0, false))
|
||||
{
|
||||
firstAssistTank = member;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return firstAssistTank;
|
||||
}
|
||||
|
||||
bool FathomLordKarathressPullingBossesTrigger::IsActive()
|
||||
{
|
||||
if (bot->getClass() != CLASS_HUNTER)
|
||||
return false;
|
||||
|
||||
Unit* karathress = AI_VALUE2(Unit*, "find target", "fathom-lord karathress");
|
||||
return karathress && karathress->GetHealthPct() > 98.0f;
|
||||
}
|
||||
|
||||
bool FathomLordKarathressDeterminingKillOrderTrigger::IsActive()
|
||||
{
|
||||
if (!AI_VALUE2(Unit*, "find target", "fathom-lord karathress"))
|
||||
return false;
|
||||
|
||||
if (botAI->IsHeal(bot))
|
||||
return false;
|
||||
|
||||
if (botAI->IsDps(bot))
|
||||
return true;
|
||||
else if (botAI->IsAssistTankOfIndex(bot, 0, false))
|
||||
return !AI_VALUE2(Unit*, "find target", "fathom-guard caribdis");
|
||||
else if (botAI->IsAssistTankOfIndex(bot, 1, false))
|
||||
return !AI_VALUE2(Unit*, "find target", "fathom-guard sharkkis");
|
||||
else if (botAI->IsAssistTankOfIndex(bot, 2, false))
|
||||
return !AI_VALUE2(Unit*, "find target", "fathom-guard tidalvess");
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FathomLordKarathressTanksNeedToEstablishAggroTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "fathom-lord karathress") &&
|
||||
IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID, nullptr);
|
||||
}
|
||||
|
||||
// Morogrim Tidewalker
|
||||
|
||||
bool MorogrimTidewalkerPullingBossTrigger::IsActive()
|
||||
{
|
||||
if (bot->getClass() != CLASS_HUNTER)
|
||||
return false;
|
||||
|
||||
Unit* tidewalker = AI_VALUE2(Unit*, "find target", "morogrim tidewalker");
|
||||
return tidewalker && tidewalker->GetHealthPct() > 95.0f;
|
||||
}
|
||||
|
||||
bool MorogrimTidewalkerBossEngagedByMainTankTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "morogrim tidewalker") &&
|
||||
botAI->IsMainTank(bot);
|
||||
}
|
||||
|
||||
bool MorogrimTidewalkerWaterGlobulesAreIncomingTrigger::IsActive()
|
||||
{
|
||||
if (!botAI->IsRanged(bot))
|
||||
return false;
|
||||
|
||||
Unit* tidewalker = AI_VALUE2(Unit*, "find target", "morogrim tidewalker");
|
||||
return tidewalker && tidewalker->GetHealthPct() < 25.0f;
|
||||
}
|
||||
|
||||
// Lady Vashj <Coilfang Matron>
|
||||
|
||||
bool LadyVashjBossEngagedByMainTankTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "lady vashj") &&
|
||||
!IsLadyVashjInPhase2(botAI) && botAI->IsMainTank(bot);
|
||||
}
|
||||
|
||||
bool LadyVashjBossEngagedByRangedInPhase1Trigger::IsActive()
|
||||
{
|
||||
return botAI->IsRanged(bot) && IsLadyVashjInPhase1(botAI);
|
||||
}
|
||||
|
||||
bool LadyVashjCastsShockBlastOnHighestAggroTrigger::IsActive()
|
||||
{
|
||||
if (bot->getClass() != CLASS_SHAMAN)
|
||||
return false;
|
||||
|
||||
if (!AI_VALUE2(Unit*, "find target", "lady vashj") ||
|
||||
IsLadyVashjInPhase2(botAI))
|
||||
return false;
|
||||
|
||||
if (!IsMainTankInSameSubgroup(bot))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LadyVashjBotHasStaticChargeTrigger::IsActive()
|
||||
{
|
||||
if (!AI_VALUE2(Unit*, "find target", "lady vashj"))
|
||||
return false;
|
||||
|
||||
if (Group* group = bot->GetGroup())
|
||||
{
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (member && member->HasAura(SPELL_STATIC_CHARGE))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LadyVashjPullingBossInPhase1AndPhase3Trigger::IsActive()
|
||||
{
|
||||
if (bot->getClass() != CLASS_HUNTER)
|
||||
return false;
|
||||
|
||||
Unit* vashj = AI_VALUE2(Unit*, "find target", "lady vashj");
|
||||
if (!vashj)
|
||||
return false;
|
||||
|
||||
return (vashj->GetHealthPct() <= 100.0f && vashj->GetHealthPct() > 90.0f) ||
|
||||
(!vashj->HasUnitState(UNIT_STATE_ROOT) && vashj->GetHealthPct() <= 50.0f &&
|
||||
vashj->GetHealthPct() > 40.0f);
|
||||
}
|
||||
|
||||
bool LadyVashjAddsSpawnInPhase2AndPhase3Trigger::IsActive()
|
||||
{
|
||||
if (botAI->IsHeal(bot))
|
||||
return false;
|
||||
|
||||
return AI_VALUE2(Unit*, "find target", "lady vashj") &&
|
||||
!IsLadyVashjInPhase1(botAI);
|
||||
}
|
||||
|
||||
bool LadyVashjCoilfangStriderIsApproachingTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "coilfang strider");
|
||||
}
|
||||
|
||||
bool LadyVashjTaintedElementalCheatTrigger::IsActive()
|
||||
{
|
||||
if (!botAI->HasCheat(BotCheatMask::raid))
|
||||
return false;
|
||||
|
||||
if (!AI_VALUE2(Unit*, "find target", "lady vashj"))
|
||||
return false;
|
||||
|
||||
bool taintedPresent = false;
|
||||
Unit* taintedUnit = AI_VALUE2(Unit*, "find target", "tainted elemental");
|
||||
if (taintedUnit)
|
||||
taintedPresent = true;
|
||||
else
|
||||
{
|
||||
GuidVector corpses = AI_VALUE(GuidVector, "nearest corpses");
|
||||
for (auto const& guid : corpses)
|
||||
{
|
||||
LootObject loot(bot, guid);
|
||||
WorldObject* object = loot.GetWorldObject(bot);
|
||||
if (!object)
|
||||
continue;
|
||||
|
||||
if (Creature* creature = object->ToCreature())
|
||||
{
|
||||
if (creature->GetEntry() == NPC_TAINTED_ELEMENTAL && !creature->IsAlive())
|
||||
{
|
||||
taintedPresent = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!taintedPresent)
|
||||
return false;
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
return (GetDesignatedCoreLooter(group, botAI) == bot &&
|
||||
!bot->HasItemCount(ITEM_TAINTED_CORE, 1, false));
|
||||
}
|
||||
|
||||
bool LadyVashjTaintedCoreWasLootedTrigger::IsActive()
|
||||
{
|
||||
if (!AI_VALUE2(Unit*, "find target", "lady vashj") || !IsLadyVashjInPhase2(botAI))
|
||||
return false;
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
Player* designatedLooter = GetDesignatedCoreLooter(group, botAI);
|
||||
Player* firstCorePasser = GetFirstTaintedCorePasser(group, botAI);
|
||||
Player* secondCorePasser = GetSecondTaintedCorePasser(group, botAI);
|
||||
Player* thirdCorePasser = GetThirdTaintedCorePasser(group, botAI);
|
||||
Player* fourthCorePasser = GetFourthTaintedCorePasser(group, botAI);
|
||||
|
||||
auto hasCore = [](Player* player) -> bool
|
||||
{
|
||||
return player && player->HasItemCount(ITEM_TAINTED_CORE, 1, false);
|
||||
};
|
||||
|
||||
if (bot == designatedLooter)
|
||||
{
|
||||
if (!hasCore(bot))
|
||||
return false;
|
||||
}
|
||||
else if (bot == firstCorePasser)
|
||||
{
|
||||
if (hasCore(secondCorePasser) || hasCore(thirdCorePasser) ||
|
||||
hasCore(fourthCorePasser))
|
||||
return false;
|
||||
}
|
||||
else if (bot == secondCorePasser)
|
||||
{
|
||||
if (hasCore(thirdCorePasser) || hasCore(fourthCorePasser))
|
||||
return false;
|
||||
}
|
||||
else if (bot == thirdCorePasser)
|
||||
{
|
||||
if (hasCore(fourthCorePasser))
|
||||
return false;
|
||||
}
|
||||
else if (bot != fourthCorePasser)
|
||||
return false;
|
||||
|
||||
if (AnyRecentCoreInInventory(group, botAI))
|
||||
return true;
|
||||
|
||||
// First and second passers move to positions as soon as the elemental appears
|
||||
if (AI_VALUE2(Unit*, "find target", "tainted elemental") &&
|
||||
(bot == firstCorePasser || bot == secondCorePasser))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LadyVashjTaintedCoreIsUnusableTrigger::IsActive()
|
||||
{
|
||||
Unit* vashj = AI_VALUE2(Unit*, "find target", "lady vashj");
|
||||
if (!vashj)
|
||||
return false;
|
||||
|
||||
if (!IsLadyVashjInPhase2(botAI))
|
||||
return bot->HasItemCount(ITEM_TAINTED_CORE, 1, false);
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
Player* coreHandlers[] =
|
||||
{
|
||||
GetDesignatedCoreLooter(group, botAI),
|
||||
GetFirstTaintedCorePasser(group, botAI),
|
||||
GetSecondTaintedCorePasser(group, botAI),
|
||||
GetThirdTaintedCorePasser(group, botAI),
|
||||
GetFourthTaintedCorePasser(group, botAI)
|
||||
};
|
||||
|
||||
if (bot->HasItemCount(ITEM_TAINTED_CORE, 1, false))
|
||||
{
|
||||
for (Player* coreHandler : coreHandlers)
|
||||
{
|
||||
if (coreHandler && bot == coreHandler)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LadyVashjNeedToResetCorePassingTrackersTrigger::IsActive()
|
||||
{
|
||||
Unit* vashj = AI_VALUE2(Unit*, "find target", "lady vashj");
|
||||
if (!vashj || IsLadyVashjInPhase2(botAI))
|
||||
return false;
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
return IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID, nullptr) ||
|
||||
GetDesignatedCoreLooter(group, botAI) == bot ||
|
||||
GetFirstTaintedCorePasser(group, botAI) == bot ||
|
||||
GetSecondTaintedCorePasser(group, botAI) == bot ||
|
||||
GetThirdTaintedCorePasser(group, botAI) == bot ||
|
||||
GetFourthTaintedCorePasser(group, botAI) == bot;
|
||||
}
|
||||
|
||||
bool LadyVashjToxicSporebatsAreSpewingPoisonCloudsTrigger::IsActive()
|
||||
{
|
||||
return IsLadyVashjInPhase3(botAI);
|
||||
}
|
||||
|
||||
bool LadyVashjBotIsEntangledInToxicSporesOrStaticChargeTrigger::IsActive()
|
||||
{
|
||||
if (!AI_VALUE2(Unit*, "find target", "lady vashj"))
|
||||
return false;
|
||||
|
||||
if (Group* group = bot->GetGroup())
|
||||
{
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->HasAura(SPELL_ENTANGLE))
|
||||
continue;
|
||||
|
||||
if (botAI->IsMelee(member))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
414
src/Ai/Raid/SerpentshrineCavern/Trigger/RaidSSCTriggers.h
Normal file
414
src/Ai/Raid/SerpentshrineCavern/Trigger/RaidSSCTriggers.h
Normal file
@@ -0,0 +1,414 @@
|
||||
#ifndef _PLAYERBOT_RAIDSSCTRIGGERS_H
|
||||
#define _PLAYERBOT_RAIDSSCTRIGGERS_H
|
||||
|
||||
#include "Trigger.h"
|
||||
|
||||
// General
|
||||
|
||||
class SerpentShrineCavernBotIsNotInCombatTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
SerpentShrineCavernBotIsNotInCombatTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "serpent shrine cavern bot is not in combat") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
// Trash
|
||||
|
||||
class UnderbogColossusSpawnedToxicPoolAfterDeathTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
UnderbogColossusSpawnedToxicPoolAfterDeathTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "underbog colossus spawned toxic pool after death") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class GreyheartTidecallerWaterElementalTotemSpawnedTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
GreyheartTidecallerWaterElementalTotemSpawnedTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "greyheart tidecaller water elemental totem spawned") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
// Hydross the Unstable <Duke of Currents>
|
||||
|
||||
class HydrossTheUnstableBotIsFrostTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
HydrossTheUnstableBotIsFrostTankTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "hydross the unstable bot is frost tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class HydrossTheUnstableBotIsNatureTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
HydrossTheUnstableBotIsNatureTankTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "hydross the unstable bot is nature tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class HydrossTheUnstableElementalsSpawnedTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
HydrossTheUnstableElementalsSpawnedTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "hydross the unstable elementals spawned") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class HydrossTheUnstableDangerFromWaterTombsTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
HydrossTheUnstableDangerFromWaterTombsTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "hydross the unstable danger from water tombs") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class HydrossTheUnstableTankNeedsAggroUponPhaseChangeTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
HydrossTheUnstableTankNeedsAggroUponPhaseChangeTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "hydross the unstable tank needs aggro upon phase change") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class HydrossTheUnstableAggroResetsUponPhaseChangeTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
HydrossTheUnstableAggroResetsUponPhaseChangeTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "hydross the unstable aggro resets upon phase change") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class HydrossTheUnstableNeedToManageTimersTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
HydrossTheUnstableNeedToManageTimersTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "hydross the unstable need to manage timers") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
// The Lurker Below
|
||||
|
||||
class TheLurkerBelowSpoutIsActiveTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
TheLurkerBelowSpoutIsActiveTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "the lurker below spout is active") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class TheLurkerBelowBossIsActiveForMainTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
TheLurkerBelowBossIsActiveForMainTankTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "the lurker below boss is active for main tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class TheLurkerBelowBossCastsGeyserTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
TheLurkerBelowBossCastsGeyserTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "the lurker below boss casts geyser") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class TheLurkerBelowBossIsSubmergedTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
TheLurkerBelowBossIsSubmergedTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "the lurker below boss is submerged") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class TheLurkerBelowNeedToPrepareTimerForSpoutTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
TheLurkerBelowNeedToPrepareTimerForSpoutTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "the lurker below need to prepare timer for spout") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
// Leotheras the Blind
|
||||
|
||||
class LeotherasTheBlindBossIsInactiveTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindBossIsInactiveTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind boss is inactive") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LeotherasTheBlindOnlyWarlockShouldTankDemonFormTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindOnlyWarlockShouldTankDemonFormTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind only warlock should tank demon form") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LeotherasTheBlindBossTransformedIntoDemonFormTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindBossTransformedIntoDemonFormTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind boss transformed into demon form") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LeotherasTheBlindBossEngagedByRangedTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindBossEngagedByRangedTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind boss engaged by ranged") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LeotherasTheBlindBossChannelingWhirlwindTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindBossChannelingWhirlwindTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind boss channeling whirlwind") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LeotherasTheBlindBotHasTooManyChaosBlastStacksTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindBotHasTooManyChaosBlastStacksTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind bot has too many chaos blast stacks") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LeotherasTheBlindInnerDemonHasAwakenedTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindInnerDemonHasAwakenedTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind inner demon has awakened") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LeotherasTheBlindEnteredFinalPhaseTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindEnteredFinalPhaseTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind entered final phase") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LeotherasTheBlindDemonFormTankNeedsAggro : public Trigger
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindDemonFormTankNeedsAggro(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind demon form tank needs aggro") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LeotherasTheBlindBossWipesAggroUponPhaseChangeTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindBossWipesAggroUponPhaseChangeTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind boss wipes aggro upon phase change") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
// Fathom-Lord Karathress
|
||||
|
||||
class FathomLordKarathressBossEngagedByMainTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressBossEngagedByMainTankTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "fathom-lord karathress boss engaged by main tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class FathomLordKarathressCaribdisEngagedByFirstAssistTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressCaribdisEngagedByFirstAssistTankTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "fathom-lord karathress caribdis engaged by first assist tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class FathomLordKarathressSharkkisEngagedBySecondAssistTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressSharkkisEngagedBySecondAssistTankTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "fathom-lord karathress sharkkis engaged by second assist tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class FathomLordKarathressTidalvessEngagedByThirdAssistTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressTidalvessEngagedByThirdAssistTankTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "fathom-lord karathress tidalvess engaged by third assist tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class FathomLordKarathressCaribdisTankNeedsDedicatedHealerTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressCaribdisTankNeedsDedicatedHealerTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "fathom-lord karathress caribdis tank needs dedicated healer") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class FathomLordKarathressPullingBossesTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressPullingBossesTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "fathom-lord karathress pulling bosses") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class FathomLordKarathressDeterminingKillOrderTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressDeterminingKillOrderTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "fathom-lord karathress determining kill order") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class FathomLordKarathressTanksNeedToEstablishAggroTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressTanksNeedToEstablishAggroTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "fathom-lord karathress tanks need to establish aggro") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
// Morogrim Tidewalker
|
||||
|
||||
class MorogrimTidewalkerPullingBossTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
MorogrimTidewalkerPullingBossTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "morogrim tidewalker pulling boss") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class MorogrimTidewalkerBossEngagedByMainTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
MorogrimTidewalkerBossEngagedByMainTankTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "morogrim tidewalker boss engaged by main tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class MorogrimTidewalkerWaterGlobulesAreIncomingTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
MorogrimTidewalkerWaterGlobulesAreIncomingTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "morogrim tidewalker water globules are incoming") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
// Lady Vashj <Coilfang Matron>
|
||||
|
||||
class LadyVashjBossEngagedByMainTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LadyVashjBossEngagedByMainTankTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj boss engaged by main tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LadyVashjBossEngagedByRangedInPhase1Trigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LadyVashjBossEngagedByRangedInPhase1Trigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj boss engaged by ranged in phase 1") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LadyVashjCastsShockBlastOnHighestAggroTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LadyVashjCastsShockBlastOnHighestAggroTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj casts shock blast on highest aggro") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LadyVashjBotHasStaticChargeTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LadyVashjBotHasStaticChargeTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj bot has static charge") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LadyVashjPullingBossInPhase1AndPhase3Trigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LadyVashjPullingBossInPhase1AndPhase3Trigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj pulling boss in phase 1 and phase 3") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LadyVashjAddsSpawnInPhase2AndPhase3Trigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LadyVashjAddsSpawnInPhase2AndPhase3Trigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj adds spawn in phase 2 and phase 3") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LadyVashjCoilfangStriderIsApproachingTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LadyVashjCoilfangStriderIsApproachingTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj coilfang strider is approaching") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LadyVashjTaintedElementalCheatTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LadyVashjTaintedElementalCheatTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj tainted elemental cheat") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LadyVashjTaintedCoreWasLootedTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LadyVashjTaintedCoreWasLootedTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj tainted core was looted") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LadyVashjTaintedCoreIsUnusableTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LadyVashjTaintedCoreIsUnusableTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj tainted core is unusable") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LadyVashjNeedToResetCorePassingTrackersTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LadyVashjNeedToResetCorePassingTrackersTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj need to reset core passing trackers") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LadyVashjToxicSporebatsAreSpewingPoisonCloudsTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LadyVashjToxicSporebatsAreSpewingPoisonCloudsTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj toxic sporebats are spewing poison clouds") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LadyVashjBotIsEntangledInToxicSporesOrStaticChargeTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LadyVashjBotIsEntangledInToxicSporesOrStaticChargeTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj bot is entangled in toxic spores or static charge") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
583
src/Ai/Raid/SerpentshrineCavern/Util/RaidSSCHelpers.cpp
Normal file
583
src/Ai/Raid/SerpentshrineCavern/Util/RaidSSCHelpers.cpp
Normal file
@@ -0,0 +1,583 @@
|
||||
#include "RaidSSCHelpers.h"
|
||||
#include "AiFactory.h"
|
||||
#include "Creature.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "Playerbots.h"
|
||||
#include "RaidBossHelpers.h"
|
||||
|
||||
namespace SerpentShrineCavernHelpers
|
||||
{
|
||||
// Hydross the Unstable <Duke of Currents>
|
||||
|
||||
const Position HYDROSS_FROST_TANK_POSITION = { -236.669f, -358.352f, -0.828f };
|
||||
const Position HYDROSS_NATURE_TANK_POSITION = { -225.471f, -327.790f, -3.682f };
|
||||
|
||||
std::unordered_map<uint32, time_t> hydrossFrostDpsWaitTimer;
|
||||
std::unordered_map<uint32, time_t> hydrossNatureDpsWaitTimer;
|
||||
std::unordered_map<uint32, time_t> hydrossChangeToFrostPhaseTimer;
|
||||
std::unordered_map<uint32, time_t> hydrossChangeToNaturePhaseTimer;
|
||||
|
||||
bool HasMarkOfHydrossAt100Percent(Player* bot)
|
||||
{
|
||||
return bot->HasAura(SPELL_MARK_OF_HYDROSS_100) ||
|
||||
bot->HasAura(SPELL_MARK_OF_HYDROSS_250) ||
|
||||
bot->HasAura(SPELL_MARK_OF_HYDROSS_500);
|
||||
}
|
||||
|
||||
bool HasNoMarkOfHydross(Player* bot)
|
||||
{
|
||||
return !bot->HasAura(SPELL_MARK_OF_HYDROSS_10) &&
|
||||
!bot->HasAura(SPELL_MARK_OF_HYDROSS_25) &&
|
||||
!bot->HasAura(SPELL_MARK_OF_HYDROSS_50) &&
|
||||
!bot->HasAura(SPELL_MARK_OF_HYDROSS_100) &&
|
||||
!bot->HasAura(SPELL_MARK_OF_HYDROSS_250) &&
|
||||
!bot->HasAura(SPELL_MARK_OF_HYDROSS_500);
|
||||
}
|
||||
|
||||
bool HasMarkOfCorruptionAt100Percent(Player* bot)
|
||||
{
|
||||
return bot->HasAura(SPELL_MARK_OF_CORRUPTION_100) ||
|
||||
bot->HasAura(SPELL_MARK_OF_CORRUPTION_250) ||
|
||||
bot->HasAura(SPELL_MARK_OF_CORRUPTION_500);
|
||||
}
|
||||
|
||||
bool HasNoMarkOfCorruption(Player* bot)
|
||||
{
|
||||
return !bot->HasAura(SPELL_MARK_OF_CORRUPTION_10) &&
|
||||
!bot->HasAura(SPELL_MARK_OF_CORRUPTION_25) &&
|
||||
!bot->HasAura(SPELL_MARK_OF_CORRUPTION_50) &&
|
||||
!bot->HasAura(SPELL_MARK_OF_CORRUPTION_100) &&
|
||||
!bot->HasAura(SPELL_MARK_OF_CORRUPTION_250) &&
|
||||
!bot->HasAura(SPELL_MARK_OF_CORRUPTION_500);
|
||||
}
|
||||
|
||||
// The Lurker Below
|
||||
|
||||
const Position LURKER_MAIN_TANK_POSITION = { 23.706f, -406.038f, -19.686f };
|
||||
|
||||
std::unordered_map<uint32, time_t> lurkerSpoutTimer;
|
||||
std::unordered_map<ObjectGuid, Position> lurkerRangedPositions;
|
||||
|
||||
bool IsLurkerCastingSpout(Unit* lurker)
|
||||
{
|
||||
if (!lurker || !lurker->HasUnitState(UNIT_STATE_CASTING))
|
||||
return false;
|
||||
|
||||
Spell* currentSpell = lurker->GetCurrentSpell(CURRENT_GENERIC_SPELL);
|
||||
if (!currentSpell)
|
||||
return false;
|
||||
|
||||
uint32 spellId = currentSpell->m_spellInfo->Id;
|
||||
bool isSpout = spellId == SPELL_SPOUT_VISUAL;
|
||||
|
||||
return isSpout;
|
||||
}
|
||||
|
||||
// Leotheras the Blind
|
||||
|
||||
std::unordered_map<uint32, time_t> leotherasHumanFormDpsWaitTimer;
|
||||
std::unordered_map<uint32, time_t> leotherasDemonFormDpsWaitTimer;
|
||||
std::unordered_map<uint32, time_t> leotherasFinalPhaseDpsWaitTimer;
|
||||
|
||||
Unit* GetLeotherasHuman(PlayerbotAI* botAI)
|
||||
{
|
||||
auto const& npcs =
|
||||
botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest hostile npcs")->Get();
|
||||
for (auto const& guid : npcs)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(guid);
|
||||
if (unit && unit->GetEntry() == NPC_LEOTHERAS_THE_BLIND &&
|
||||
unit->IsInCombat() && !unit->HasAura(SPELL_METAMORPHOSIS))
|
||||
return unit;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Unit* GetPhase2LeotherasDemon(PlayerbotAI* botAI)
|
||||
{
|
||||
auto const& npcs =
|
||||
botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest hostile npcs")->Get();
|
||||
for (auto const& guid : npcs)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(guid);
|
||||
if (unit && unit->GetEntry() == NPC_LEOTHERAS_THE_BLIND &&
|
||||
unit->HasAura(SPELL_METAMORPHOSIS))
|
||||
return unit;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Unit* GetPhase3LeotherasDemon(PlayerbotAI* botAI)
|
||||
{
|
||||
auto const& npcs =
|
||||
botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest hostile npcs")->Get();
|
||||
for (auto const& guid : npcs)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(guid);
|
||||
if (unit && unit->GetEntry() == NPC_SHADOW_OF_LEOTHERAS)
|
||||
return unit;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Unit* GetActiveLeotherasDemon(PlayerbotAI* botAI)
|
||||
{
|
||||
Unit* phase2 = GetPhase2LeotherasDemon(botAI);
|
||||
Unit* phase3 = GetPhase3LeotherasDemon(botAI);
|
||||
return phase2 ? phase2 : phase3;
|
||||
}
|
||||
|
||||
Player* GetLeotherasDemonFormTank(Player* bot)
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return nullptr;
|
||||
|
||||
// (1) First loop: Return the first assistant Warlock (real player or bot)
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || member->getClass() != CLASS_WARLOCK)
|
||||
continue;
|
||||
|
||||
if (group->IsAssistant(member->GetGUID()))
|
||||
return member;
|
||||
}
|
||||
|
||||
// (2) Fall back to first found bot Warlock
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) ||
|
||||
member->getClass() != CLASS_WARLOCK)
|
||||
continue;
|
||||
|
||||
return member;
|
||||
}
|
||||
|
||||
// (3) Return nullptr if none found
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Fathom-Lord Karathress
|
||||
|
||||
const Position KARATHRESS_TANK_POSITION = { 474.403f, -531.118f, -7.548f };
|
||||
const Position TIDALVESS_TANK_POSITION = { 511.282f, -501.162f, -13.158f };
|
||||
const Position SHARKKIS_TANK_POSITION = { 508.057f, -541.109f, -10.133f };
|
||||
const Position CARIBDIS_TANK_POSITION = { 464.462f, -475.820f, -13.158f };
|
||||
const Position CARIBDIS_HEALER_POSITION = { 466.203f, -503.201f, -13.158f };
|
||||
const Position CARIBDIS_RANGED_DPS_POSITION = { 463.197f, -501.190f, -13.158f };
|
||||
|
||||
std::unordered_map<uint32, time_t> karathressDpsWaitTimer;
|
||||
|
||||
// Morogrim Tidewalker
|
||||
|
||||
const Position TIDEWALKER_PHASE_1_TANK_POSITION = { 410.925f, -741.916f, -7.146f };
|
||||
const Position TIDEWALKER_PHASE_TRANSITION_WAYPOINT = { 407.035f, -759.479f, -7.168f };
|
||||
const Position TIDEWALKER_PHASE_2_TANK_POSITION = { 446.571f, -767.155f, -7.144f };
|
||||
const Position TIDEWALKER_PHASE_2_RANGED_POSITION = { 432.595f, -766.288f, -7.145f };
|
||||
|
||||
std::unordered_map<ObjectGuid, uint8> tidewalkerTankStep;
|
||||
std::unordered_map<ObjectGuid, uint8> tidewalkerRangedStep;
|
||||
|
||||
// Lady Vashj <Coilfang Matron>
|
||||
|
||||
const Position VASHJ_PLATFORM_CENTER_POSITION = { 29.634f, -923.541f, 42.985f };
|
||||
|
||||
std::unordered_map<ObjectGuid, Position> vashjRangedPositions;
|
||||
std::unordered_map<ObjectGuid, bool> hasReachedVashjRangedPosition;
|
||||
std::unordered_map<uint32, ObjectGuid> nearestTriggerGuid;
|
||||
std::unordered_map<ObjectGuid, Position> intendedLineup;
|
||||
std::unordered_map<uint32, time_t> lastImbueAttempt;
|
||||
std::unordered_map<uint32, time_t> lastCoreInInventoryTime;
|
||||
|
||||
bool IsMainTankInSameSubgroup(Player* bot)
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group || !group->isRaidGroup())
|
||||
return false;
|
||||
|
||||
uint8 botSubGroup = group->GetMemberGroup(bot->GetGUID());
|
||||
if (botSubGroup >= MAX_RAID_SUBGROUPS)
|
||||
return false;
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || member == bot || !member->IsAlive())
|
||||
continue;
|
||||
|
||||
if (group->GetMemberGroup(member->GetGUID()) != botSubGroup)
|
||||
continue;
|
||||
|
||||
if (PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member))
|
||||
{
|
||||
if (memberAI->IsMainTank(member))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsLadyVashjInPhase1(PlayerbotAI* botAI)
|
||||
{
|
||||
Unit* vashj =
|
||||
botAI->GetAiObjectContext()->GetValue<Unit*>("find target", "lady vashj")->Get();
|
||||
if (!vashj)
|
||||
return false;
|
||||
|
||||
Creature* vashjCreature = vashj->ToCreature();
|
||||
return vashjCreature && vashjCreature->GetHealthPct() > 70.0f &&
|
||||
vashjCreature->GetReactState() != REACT_PASSIVE;
|
||||
}
|
||||
|
||||
bool IsLadyVashjInPhase2(PlayerbotAI* botAI)
|
||||
{
|
||||
Unit* vashj =
|
||||
botAI->GetAiObjectContext()->GetValue<Unit*>("find target", "lady vashj")->Get();
|
||||
if (!vashj)
|
||||
return false;
|
||||
|
||||
Creature* vashjCreature = vashj->ToCreature();
|
||||
return vashjCreature && vashjCreature->GetReactState() == REACT_PASSIVE;
|
||||
}
|
||||
|
||||
bool IsLadyVashjInPhase3(PlayerbotAI* botAI)
|
||||
{
|
||||
Unit* vashj =
|
||||
botAI->GetAiObjectContext()->GetValue<Unit*>("find target", "lady vashj")->Get();
|
||||
if (!vashj)
|
||||
return false;
|
||||
|
||||
Creature* vashjCreature = vashj->ToCreature();
|
||||
return vashjCreature && vashjCreature->GetHealthPct() <= 50.0f &&
|
||||
vashjCreature->GetReactState() != REACT_PASSIVE;
|
||||
}
|
||||
|
||||
bool IsValidLadyVashjCombatNpc(Unit* unit, PlayerbotAI* botAI)
|
||||
{
|
||||
if (!unit || !unit->IsAlive())
|
||||
return false;
|
||||
|
||||
uint32 entry = unit->GetEntry();
|
||||
|
||||
if (IsLadyVashjInPhase2(botAI))
|
||||
{
|
||||
return entry == NPC_TAINTED_ELEMENTAL || entry == NPC_ENCHANTED_ELEMENTAL ||
|
||||
entry == NPC_COILFANG_ELITE || entry == NPC_COILFANG_STRIDER;
|
||||
}
|
||||
else if (IsLadyVashjInPhase3(botAI))
|
||||
{
|
||||
return entry == NPC_TAINTED_ELEMENTAL || entry == NPC_ENCHANTED_ELEMENTAL ||
|
||||
entry == NPC_COILFANG_ELITE || entry == NPC_COILFANG_STRIDER ||
|
||||
entry == NPC_TOXIC_SPOREBAT || entry == NPC_LADY_VASHJ;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AnyRecentCoreInInventory(Group* group, PlayerbotAI* botAI, uint32 graceSeconds)
|
||||
{
|
||||
Unit* vashj =
|
||||
botAI->GetAiObjectContext()->GetValue<Unit*>("find target", "lady vashj")->Get();
|
||||
if (!vashj)
|
||||
return false;
|
||||
|
||||
if (group)
|
||||
{
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (member && member->HasItemCount(ITEM_TAINTED_CORE, 1, false))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const uint32 instanceId = vashj->GetMap()->GetInstanceId();
|
||||
const time_t now = std::time(nullptr);
|
||||
|
||||
auto it = lastCoreInInventoryTime.find(instanceId);
|
||||
if (it != lastCoreInInventoryTime.end())
|
||||
{
|
||||
if ((now - it->second) <= static_cast<time_t>(graceSeconds))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Player* GetDesignatedCoreLooter(Group* group, PlayerbotAI* botAI)
|
||||
{
|
||||
if (!group)
|
||||
return nullptr;
|
||||
|
||||
Player* leader = nullptr;
|
||||
ObjectGuid leaderGuid = group->GetLeaderGUID();
|
||||
if (!leaderGuid.IsEmpty())
|
||||
leader = ObjectAccessor::FindPlayer(leaderGuid);
|
||||
|
||||
if (!botAI->HasCheat(BotCheatMask::raid))
|
||||
return leader;
|
||||
|
||||
Player* fallback = leader;
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || member == leader)
|
||||
continue;
|
||||
|
||||
PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member);
|
||||
if (!memberAI)
|
||||
continue;
|
||||
|
||||
if (memberAI->IsMelee(member) && memberAI->IsDps(member))
|
||||
return member;
|
||||
|
||||
if (!fallback && memberAI->IsRangedDps(member))
|
||||
fallback = member;
|
||||
}
|
||||
|
||||
return fallback ? fallback : leader;
|
||||
}
|
||||
|
||||
Player* GetFirstTaintedCorePasser(Group* group, PlayerbotAI* botAI)
|
||||
{
|
||||
if (!group)
|
||||
return nullptr;
|
||||
|
||||
Player* designatedLooter = GetDesignatedCoreLooter(group, botAI);
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || member == designatedLooter)
|
||||
continue;
|
||||
|
||||
PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member);
|
||||
if (!memberAI)
|
||||
continue;
|
||||
|
||||
if (memberAI->IsAssistHealOfIndex(member, 0, true))
|
||||
return member;
|
||||
}
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) ||
|
||||
botAI->IsTank(member) || member == designatedLooter)
|
||||
continue;
|
||||
return member;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Player* GetSecondTaintedCorePasser(Group* group, PlayerbotAI* botAI)
|
||||
{
|
||||
if (!group)
|
||||
return nullptr;
|
||||
|
||||
Player* designatedLooter = GetDesignatedCoreLooter(group, botAI);
|
||||
Player* firstCorePasser = GetFirstTaintedCorePasser(group, botAI);
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || member == designatedLooter ||
|
||||
member == firstCorePasser)
|
||||
continue;
|
||||
|
||||
PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member);
|
||||
if (!memberAI)
|
||||
continue;
|
||||
|
||||
if (memberAI->IsAssistHealOfIndex(member, 1, true))
|
||||
return member;
|
||||
}
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) ||
|
||||
botAI->IsTank(member) || member == designatedLooter ||
|
||||
member == firstCorePasser)
|
||||
continue;
|
||||
return member;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Player* GetThirdTaintedCorePasser(Group* group, PlayerbotAI* botAI)
|
||||
{
|
||||
if (!group)
|
||||
return nullptr;
|
||||
|
||||
Player* designatedLooter = GetDesignatedCoreLooter(group, botAI);
|
||||
Player* firstCorePasser = GetFirstTaintedCorePasser(group, botAI);
|
||||
Player* secondCorePasser = GetSecondTaintedCorePasser(group, botAI);
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || member == designatedLooter ||
|
||||
member == firstCorePasser || member == secondCorePasser)
|
||||
continue;
|
||||
|
||||
PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member);
|
||||
if (!memberAI)
|
||||
continue;
|
||||
|
||||
if (memberAI->IsAssistHealOfIndex(member, 2, true))
|
||||
return member;
|
||||
}
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) ||
|
||||
botAI->IsTank(member) || member == designatedLooter ||
|
||||
member == firstCorePasser || member == secondCorePasser)
|
||||
continue;
|
||||
return member;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Player* GetFourthTaintedCorePasser(Group* group, PlayerbotAI* botAI)
|
||||
{
|
||||
if (!group)
|
||||
return nullptr;
|
||||
|
||||
Player* designatedLooter = GetDesignatedCoreLooter(group, botAI);
|
||||
Player* firstCorePasser = GetFirstTaintedCorePasser(group, botAI);
|
||||
Player* secondCorePasser = GetSecondTaintedCorePasser(group, botAI);
|
||||
Player* thirdCorePasser = GetThirdTaintedCorePasser(group, botAI);
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || member == designatedLooter ||
|
||||
member == firstCorePasser || member == secondCorePasser ||
|
||||
member == thirdCorePasser)
|
||||
continue;
|
||||
|
||||
PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member);
|
||||
if (!memberAI)
|
||||
continue;
|
||||
|
||||
if (memberAI->IsAssistRangedDpsOfIndex(member, 0, true))
|
||||
return member;
|
||||
}
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) ||
|
||||
botAI->IsTank(member) || member == designatedLooter ||
|
||||
member == firstCorePasser || member == secondCorePasser ||
|
||||
member == thirdCorePasser)
|
||||
continue;
|
||||
return member;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const std::vector<uint32> SHIELD_GENERATOR_DB_GUIDS =
|
||||
{
|
||||
47482, // NW
|
||||
47483, // NE
|
||||
47484, // SE
|
||||
47485 // SW
|
||||
};
|
||||
|
||||
// Get the positions of all active Shield Generators by their database GUIDs
|
||||
std::vector<GeneratorInfo> GetAllGeneratorInfosByDbGuids(
|
||||
Map* map, const std::vector<uint32>& generatorDbGuids)
|
||||
{
|
||||
std::vector<GeneratorInfo> generators;
|
||||
if (!map)
|
||||
return generators;
|
||||
|
||||
for (uint32 dbGuid : generatorDbGuids)
|
||||
{
|
||||
auto bounds = map->GetGameObjectBySpawnIdStore().equal_range(dbGuid);
|
||||
if (bounds.first == bounds.second)
|
||||
continue;
|
||||
|
||||
GameObject* go = bounds.first->second;
|
||||
if (!go)
|
||||
continue;
|
||||
|
||||
if (go->GetGoState() != GO_STATE_READY)
|
||||
continue;
|
||||
|
||||
GeneratorInfo info;
|
||||
info.guid = go->GetGUID();
|
||||
info.x = go->GetPositionX();
|
||||
info.y = go->GetPositionY();
|
||||
info.z = go->GetPositionZ();
|
||||
generators.push_back(info);
|
||||
}
|
||||
|
||||
return generators;
|
||||
}
|
||||
|
||||
// Returns the nearest active Shield Generator to the bot
|
||||
// Active generators are powered by NPC_WORLD_INVISIBLE_TRIGGER creatures,
|
||||
// which depawn after use
|
||||
Unit* GetNearestActiveShieldGeneratorTriggerByEntry(Unit* reference)
|
||||
{
|
||||
if (!reference)
|
||||
return nullptr;
|
||||
|
||||
std::list<Creature*> triggers;
|
||||
constexpr float searchRange = 150.0f;
|
||||
reference->GetCreatureListWithEntryInGrid(
|
||||
triggers, NPC_WORLD_INVISIBLE_TRIGGER, searchRange);
|
||||
|
||||
Creature* nearest = nullptr;
|
||||
float minDist = std::numeric_limits<float>::max();
|
||||
|
||||
for (Creature* creature : triggers)
|
||||
{
|
||||
if (!creature->IsAlive())
|
||||
continue;
|
||||
|
||||
float dist = reference->GetDistance(creature);
|
||||
if (dist < minDist)
|
||||
{
|
||||
minDist = dist;
|
||||
nearest = creature;
|
||||
}
|
||||
}
|
||||
|
||||
return nearest;
|
||||
}
|
||||
|
||||
const GeneratorInfo* GetNearestGeneratorToBot(
|
||||
Player* bot, const std::vector<GeneratorInfo>& generators)
|
||||
{
|
||||
if (!bot || generators.empty())
|
||||
return nullptr;
|
||||
|
||||
const GeneratorInfo* nearest = nullptr;
|
||||
float minDist = std::numeric_limits<float>::max();
|
||||
|
||||
for (auto const& gen : generators)
|
||||
{
|
||||
float dist = bot->GetExactDist(gen.x, gen.y, gen.z);
|
||||
if (dist < minDist)
|
||||
{
|
||||
minDist = dist;
|
||||
nearest = &gen;
|
||||
}
|
||||
}
|
||||
|
||||
return nearest;
|
||||
}
|
||||
}
|
||||
189
src/Ai/Raid/SerpentshrineCavern/Util/RaidSSCHelpers.h
Normal file
189
src/Ai/Raid/SerpentshrineCavern/Util/RaidSSCHelpers.h
Normal file
@@ -0,0 +1,189 @@
|
||||
#ifndef _PLAYERBOT_RAIDSSCHELPERS_H_
|
||||
#define _PLAYERBOT_RAIDSSCHELPERS_H_
|
||||
|
||||
#include <ctime>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "AiObject.h"
|
||||
#include "Position.h"
|
||||
#include "Unit.h"
|
||||
|
||||
namespace SerpentShrineCavernHelpers
|
||||
{
|
||||
enum SerpentShrineCavernSpells
|
||||
{
|
||||
// Trash Mobs
|
||||
SPELL_TOXIC_POOL = 38718,
|
||||
|
||||
// Hydross the Unstable <Duke of Currents>
|
||||
SPELL_MARK_OF_HYDROSS_10 = 38215,
|
||||
SPELL_MARK_OF_HYDROSS_25 = 38216,
|
||||
SPELL_MARK_OF_HYDROSS_50 = 38217,
|
||||
SPELL_MARK_OF_HYDROSS_100 = 38218,
|
||||
SPELL_MARK_OF_HYDROSS_250 = 38231,
|
||||
SPELL_MARK_OF_HYDROSS_500 = 40584,
|
||||
SPELL_MARK_OF_CORRUPTION_10 = 38219,
|
||||
SPELL_MARK_OF_CORRUPTION_25 = 38220,
|
||||
SPELL_MARK_OF_CORRUPTION_50 = 38221,
|
||||
SPELL_MARK_OF_CORRUPTION_100 = 38222,
|
||||
SPELL_MARK_OF_CORRUPTION_250 = 38230,
|
||||
SPELL_MARK_OF_CORRUPTION_500 = 40583,
|
||||
SPELL_CORRUPTION = 37961,
|
||||
|
||||
// The Lurker Below
|
||||
SPELL_SPOUT_VISUAL = 37431,
|
||||
|
||||
// Leotheras the Blind
|
||||
SPELL_LEOTHERAS_BANISHED = 37546,
|
||||
SPELL_WHIRLWIND = 37640,
|
||||
SPELL_WHIRLWIND_CHANNEL = 37641,
|
||||
SPELL_METAMORPHOSIS = 37673,
|
||||
SPELL_CHAOS_BLAST = 37675,
|
||||
SPELL_INSIDIOUS_WHISPER = 37676,
|
||||
|
||||
// Lady Vashj <Coilfang Matron>
|
||||
SPELL_FEAR_WARD = 6346,
|
||||
SPELL_POISON_BOLT = 38253,
|
||||
SPELL_STATIC_CHARGE = 38280,
|
||||
SPELL_ENTANGLE = 38316,
|
||||
|
||||
// Druid
|
||||
SPELL_CAT_FORM = 768,
|
||||
SPELL_BEAR_FORM = 5487,
|
||||
SPELL_DIRE_BEAR_FORM = 9634,
|
||||
SPELL_TREE_OF_LIFE = 33891,
|
||||
|
||||
// Hunter
|
||||
SPELL_MISDIRECTION = 35079,
|
||||
|
||||
// Mage
|
||||
SPELL_SLOW = 31589,
|
||||
|
||||
// Shaman
|
||||
SPELL_GROUNDING_TOTEM_EFFECT = 8178,
|
||||
|
||||
// Warlock
|
||||
SPELL_CURSE_OF_EXHAUSTION = 18223,
|
||||
|
||||
// Item
|
||||
SPELL_HEAVY_NETHERWEAVE_NET = 31368,
|
||||
};
|
||||
|
||||
enum SerpentShrineCavernNPCs
|
||||
{
|
||||
// Trash Mobs
|
||||
NPC_WATER_ELEMENTAL_TOTEM = 22236,
|
||||
|
||||
// Hydross the Unstable <Duke of Currents>
|
||||
NPC_PURE_SPAWN_OF_HYDROSS = 22035,
|
||||
NPC_TAINTED_SPAWN_OF_HYDROSS = 22036,
|
||||
|
||||
// The Lurker Below
|
||||
NPC_COILFANG_GUARDIAN = 21873,
|
||||
|
||||
// Leotheras the Blind
|
||||
NPC_LEOTHERAS_THE_BLIND = 21215,
|
||||
NPC_GREYHEART_SPELLBINDER = 21806,
|
||||
NPC_INNER_DEMON = 21857,
|
||||
NPC_SHADOW_OF_LEOTHERAS = 21875,
|
||||
|
||||
// Fathom-Lord Karathress
|
||||
NPC_SPITFIRE_TOTEM = 22091,
|
||||
|
||||
// Lady Vashj <Coilfang Matron>
|
||||
NPC_WORLD_INVISIBLE_TRIGGER = 12999,
|
||||
NPC_LADY_VASHJ = 21212,
|
||||
NPC_ENCHANTED_ELEMENTAL = 21958,
|
||||
NPC_TAINTED_ELEMENTAL = 22009,
|
||||
NPC_COILFANG_ELITE = 22055,
|
||||
NPC_COILFANG_STRIDER = 22056,
|
||||
NPC_TOXIC_SPOREBAT = 22140,
|
||||
NPC_SPORE_DROP_TRIGGER = 22207,
|
||||
};
|
||||
|
||||
enum SerpentShrineCavernItems
|
||||
{
|
||||
// Lady Vashj <Coilfang Matron>
|
||||
ITEM_TAINTED_CORE = 31088,
|
||||
|
||||
// Tailoring
|
||||
ITEM_HEAVY_NETHERWEAVE_NET = 24269,
|
||||
};
|
||||
|
||||
constexpr uint32 SSC_MAP_ID = 548;
|
||||
|
||||
// Hydross the Unstable <Duke of Currents>
|
||||
extern const Position HYDROSS_FROST_TANK_POSITION;
|
||||
extern const Position HYDROSS_NATURE_TANK_POSITION;
|
||||
extern std::unordered_map<uint32, time_t> hydrossFrostDpsWaitTimer;
|
||||
extern std::unordered_map<uint32, time_t> hydrossNatureDpsWaitTimer;
|
||||
extern std::unordered_map<uint32, time_t> hydrossChangeToFrostPhaseTimer;
|
||||
extern std::unordered_map<uint32, time_t> hydrossChangeToNaturePhaseTimer;
|
||||
bool HasMarkOfHydrossAt100Percent(Player* bot);
|
||||
bool HasNoMarkOfHydross(Player* bot);
|
||||
bool HasMarkOfCorruptionAt100Percent(Player* bot);
|
||||
bool HasNoMarkOfCorruption(Player* bot);
|
||||
|
||||
// The Lurker Below
|
||||
extern const Position LURKER_MAIN_TANK_POSITION;
|
||||
extern std::unordered_map<uint32, time_t> lurkerSpoutTimer;
|
||||
extern std::unordered_map<ObjectGuid, Position> lurkerRangedPositions;
|
||||
bool IsLurkerCastingSpout(Unit* lurker);
|
||||
|
||||
// Leotheras the Blind
|
||||
extern std::unordered_map<uint32, time_t> leotherasHumanFormDpsWaitTimer;
|
||||
extern std::unordered_map<uint32, time_t> leotherasDemonFormDpsWaitTimer;
|
||||
extern std::unordered_map<uint32, time_t> leotherasFinalPhaseDpsWaitTimer;
|
||||
Unit* GetLeotherasHuman(PlayerbotAI* botAI);
|
||||
Unit* GetPhase2LeotherasDemon(PlayerbotAI* botAI);
|
||||
Unit* GetPhase3LeotherasDemon(PlayerbotAI* botAI);
|
||||
Unit* GetActiveLeotherasDemon(PlayerbotAI* botAI);
|
||||
Player* GetLeotherasDemonFormTank(Player* bot);
|
||||
|
||||
// Fathom-Lord Karathress
|
||||
extern const Position KARATHRESS_TANK_POSITION;
|
||||
extern const Position TIDALVESS_TANK_POSITION;
|
||||
extern const Position SHARKKIS_TANK_POSITION;
|
||||
extern const Position CARIBDIS_TANK_POSITION;
|
||||
extern const Position CARIBDIS_HEALER_POSITION;
|
||||
extern const Position CARIBDIS_RANGED_DPS_POSITION;
|
||||
extern std::unordered_map<uint32, time_t> karathressDpsWaitTimer;
|
||||
|
||||
// Morogrim Tidewalker
|
||||
extern const Position TIDEWALKER_PHASE_1_TANK_POSITION;
|
||||
extern const Position TIDEWALKER_PHASE_TRANSITION_WAYPOINT;
|
||||
extern const Position TIDEWALKER_PHASE_2_TANK_POSITION;
|
||||
extern const Position TIDEWALKER_PHASE_2_RANGED_POSITION;
|
||||
extern std::unordered_map<ObjectGuid, uint8> tidewalkerTankStep;
|
||||
extern std::unordered_map<ObjectGuid, uint8> tidewalkerRangedStep;
|
||||
|
||||
// Lady Vashj <Coilfang Matron>
|
||||
constexpr float VASHJ_PLATFORM_Z = 42.985f;
|
||||
extern const Position VASHJ_PLATFORM_CENTER_POSITION;
|
||||
extern std::unordered_map<ObjectGuid, Position> vashjRangedPositions;
|
||||
extern std::unordered_map<ObjectGuid, bool> hasReachedVashjRangedPosition;
|
||||
extern std::unordered_map<uint32, ObjectGuid> nearestTriggerGuid;
|
||||
extern std::unordered_map<ObjectGuid, Position> intendedLineup;
|
||||
extern std::unordered_map<uint32, time_t> lastImbueAttempt;
|
||||
extern std::unordered_map<uint32, time_t> lastCoreInInventoryTime;
|
||||
bool IsMainTankInSameSubgroup(Player* bot);
|
||||
bool IsLadyVashjInPhase1(PlayerbotAI* botAI);
|
||||
bool IsLadyVashjInPhase2(PlayerbotAI* botAI);
|
||||
bool IsLadyVashjInPhase3(PlayerbotAI* botAI);
|
||||
bool IsValidLadyVashjCombatNpc(Unit* unit, PlayerbotAI* botAI);
|
||||
bool AnyRecentCoreInInventory(Group* group, PlayerbotAI* botAI, uint32 graceSeconds = 3);
|
||||
Player* GetDesignatedCoreLooter(Group* group, PlayerbotAI* botAI);
|
||||
Player* GetFirstTaintedCorePasser(Group* group, PlayerbotAI* botAI);
|
||||
Player* GetSecondTaintedCorePasser(Group* group, PlayerbotAI* botAI);
|
||||
Player* GetThirdTaintedCorePasser(Group* group, PlayerbotAI* botAI);
|
||||
Player* GetFourthTaintedCorePasser(Group* group, PlayerbotAI* botAI);
|
||||
struct GeneratorInfo { ObjectGuid guid; float x, y, z; };
|
||||
extern const std::vector<uint32> SHIELD_GENERATOR_DB_GUIDS;
|
||||
std::vector<GeneratorInfo> GetAllGeneratorInfosByDbGuids(
|
||||
Map* map, const std::vector<uint32>& generatorDbGuids);
|
||||
Unit* GetNearestActiveShieldGeneratorTriggerByEntry(Unit* reference);
|
||||
const GeneratorInfo* GetNearestGeneratorToBot(
|
||||
Player* bot, const std::vector<GeneratorInfo>& generators);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -41,6 +41,8 @@
|
||||
#include "Ai/Raid/Magtheridon/RaidMagtheridonTriggerContext.h"
|
||||
#include "Ai/Raid/GruulsLair/RaidGruulsLairActionContext.h"
|
||||
#include "Ai/Raid/GruulsLair/RaidGruulsLairTriggerContext.h"
|
||||
#include "Ai/Raid/SerpentshrineCavern/RaidSSCActionContext.h"
|
||||
#include "Ai/Raid/SerpentshrineCavern/RaidSSCTriggerContext.h"
|
||||
#include "Ai/Raid/EyeOfEternity/RaidEoEActionContext.h"
|
||||
#include "Ai/Raid/EyeOfEternity/RaidEoETriggerContext.h"
|
||||
#include "Ai/Raid/VaultOfArchavon/RaidVoAActionContext.h"
|
||||
@@ -115,6 +117,7 @@ void AiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList<Act
|
||||
actionContexts.Add(new RaidKarazhanActionContext());
|
||||
actionContexts.Add(new RaidMagtheridonActionContext());
|
||||
actionContexts.Add(new RaidGruulsLairActionContext());
|
||||
actionContexts.Add(new RaidSSCActionContext());
|
||||
actionContexts.Add(new RaidOsActionContext());
|
||||
actionContexts.Add(new RaidEoEActionContext());
|
||||
actionContexts.Add(new RaidVoAActionContext());
|
||||
@@ -149,6 +152,7 @@ void AiObjectContext::BuildSharedTriggerContexts(SharedNamedObjectContextList<Tr
|
||||
triggerContexts.Add(new RaidKarazhanTriggerContext());
|
||||
triggerContexts.Add(new RaidMagtheridonTriggerContext());
|
||||
triggerContexts.Add(new RaidGruulsLairTriggerContext());
|
||||
triggerContexts.Add(new RaidSSCTriggerContext());
|
||||
triggerContexts.Add(new RaidOsTriggerContext());
|
||||
triggerContexts.Add(new RaidEoETriggerContext());
|
||||
triggerContexts.Add(new RaidVoATriggerContext());
|
||||
|
||||
@@ -1554,6 +1554,9 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
|
||||
case 544:
|
||||
strategyName = "magtheridon"; // Magtheridon's Lair
|
||||
break;
|
||||
case 548:
|
||||
strategyName = "ssc"; // Serpentshrine Cavern
|
||||
break;
|
||||
case 565:
|
||||
strategyName = "gruulslair"; // Gruul's Lair
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user