mirror of
https://github.com/mod-playerbots/mod-playerbots.git
synced 2026-03-16 22:05:09 +00:00
Implement Tempest Keep: The Eye Strategies (#1943)
Edit: Descriptions of methods are out of date right now. To be updated. This one comes with the same caveats as SSC about requiring ownership from somebody with C++ knowledge, except I think the matter is even more acute here because these strategies incorporate a novel approach proposed by Timberpoes. By redeclaring the entire bossai class for Kael’thas, it was possible to add new member functions to the class in order to access its private member variables. This allows bots to have visibility into boss mechanics beyond what they could do with ordinary techniques and is similar in approach to what was done by the Naxx strategies, except that this approach does not require any modifications to the core. I used it for only one mechanic, which was to detect Kael’thas’s phase. That was very helpful because the fight is divided into 5 phases, and distinguishing between them with traditional techniques requires lookups of a dozen NPCs and comparisons of their various unit states, react states, and auras; by accessing his bossai, this can all be avoided. However, there is far more potential beyond this if the approach is an acceptable one. On with the (shit)show. ### Trash In a perfect world, there would be many strategies for TK trash, which is easily more difficult than two of the bosses. It’s a real pain to do though because to solve the biggest issues properly, each pack would have to be handled a little differently. So the only thing I’ve included is for Mages to cast polymorph on the Crimson Hand Centurions when they are channeling Arcane Flurry. The purpose is not to actually keep them CC’d but to interrupt their channel. ### Al’ar This fight sucked so much to write a strategy for. The only silver lining is that being the post-nerf version, the boss moves between only 4 platform locations (instead of 6), and movement between them is on a fixed rotation (interrupted by Flame Quills) instead of being random. Thus, a strategy can be consistently replicated, and the fight can be done with only 3 tanks (2 on the platforms for the boss and 1 below for adds). **Phase 1:** I’m going to call the platform that Al’ar lands at after the pull “platform 0” because that reflects the indices in the code. In a clockwise direction, the remaining platforms will be referred to as platforms 1, 2, and 3, respectively. The best way to pull is to first put all ranged, as well as tanks other than your main tank and first assistant tank, on nc +stay below platform 0. Then, go up the ramp to platform 0 with your main tank, first assistant tank, and melee dps following you, then hit Al’ar with any ranged attack or spell to start the fight. - Your main tank will start at platform 0, and your first assistant tank will immediately move to platform 1. When Al’ar moves to platform 1, your main tank will move to platform 2. When Al’ar moves to platform 2, your first assistant tank will move to platform 3. When Al’ar moves to platform 3, your main tank will move back to platform 0. This assures a tank is available to receive Al’ar after every platform movement (every 30 seconds). - Melee DPS will follow Al’ar as it moves between platforms. - Each platform is mapped to a corresponding ground location below it. Ranged DPS and healers will follow Al’ar by moving to the corresponding ground location as it flies between platforms. - After each platform move, an Ember of Al’ar will spawn. Your second assistant tank will pick up the Ember and move it to the point that is 25 yards away from the ground position corresponding to Al’ar’s platform (on an invisible line between such ground position and the middle of the room). Ranged DPS will then focus down the Ember before switching back to Al’ar (this positioning is so that ranged are not hit by the Ember Blast explosion that happens whenever an Ember dies). - Each time Al’ar leaves a platform, it has a chance to instead fly up high in the middle of the room to perform Flame Quills, which will one-shot anybody on the upper level or ramps. When Al’ar begins the Flame Quills sequence, all bots on the top level will jump off. FYI, Al’ar’s usage of Flame Quills is not entirely random: there is a 20% chance for it to do so after the first platform move, and the chance increases by another 20% after each subsequent platform move that does not trigger Flame Quills (reset after each Flame Quills sequence). - After Flame Quills, Al’ar will randomly land at either platform 0 or 3. To prepare for this, bots will move to assigned positions during the Flame Quills sequence: - Ranged and the second assistant tank will wait in the middle of the room. - Melee DPS will wait at a point that is between the base of each ramp. - The main tank will wait at the base of the ramp to platform 0. - The first assistant tank will wait at the base of the ramp to platform 3. - Once Al’ar lands, the regular Phase 1 strategies resume. - When Al’ar “dies,” it disappears and moves to the center of the room, where it casts Rebirth and returns to full HP. Bots will wait outside of the radius of the Rebirth explosion for Phase 2 to start. Phase 2: - Your main tank will tank Al’ar initially. When Al’ar casts Melt Armor, your first assistant tank will taunt Al’ar and take over. The tank swaps will continue back and forth every time Melt Armor is cast. - Bots will avoid Flame Patches. FWIW, the standard co +avoid aoe strategy does work for Flame Patches, but avoid aoe provides no buffer distance so as you’ve probably noticed, it doesn’t provide for preemptive avoidance. Also, avoid aoe does not consider multiple hazards together so it can be an issue when movement needs to take into account more than one hazard, plus when a strategy requires particular bot movement, it’s better to account for the hazards within that movement strategy instead of relying on separate methods that can create conflicts. - When Al’ar takes to the sky to perform Dive Bomb, bots will spread out (and continue to avoid Flame Patches). After the Dive Bomb, Al’ar does another Rebirth explosion. I have tried a million different things to properly detect this full sequence (even accessing the bossAI like I did with Kael’thas) and cannot get it to work properly. Ultimately, all I’ve been able to get to work at all with respect to the final explosion is for bots to detect the 2-second cast of the Rebirth and run out. It is not enough time for bots that are too close when the cast happens so some bots may get hit, but if you have adequate gear, they should survive. - After each Dive Bomb, 2 Embers will spawn. Your second assistant tank will tank one Ember, and either the main tank or first assistant tank, whichever one is not tanking Al’ar at the time, will tank the other Ember. They will both move the Embers away from bots, and ranged DPS will focus both Embers down before switching back to Al’ar. - Because the room is so large, it is possible for bots to get too far away from active combat (particularly if they are thrown across the room by Ember Blast) so there is also a method for them to run back toward the center if they get too far away. ### Void Reaver Ironically, what was often considered the easiest boss in 25-player content in TBC is the only boss with an ability (Arcane Orb) that I do not believe can be avoided by bots, even with access to Void Reaver’s boss script. Therefore, every single Arcane Orb is going to hit its target, so the strategy can only try to limit the damage by spreading ranged bots in two rings around Void Reaver (one for healers and one for ranged DPS, to try to ensure sufficient distribution of healers). The tanks will all fight for aggro (necessary due to Knock Away) and try to keep Void Reaver in the middle of the room. Bots that can wipe aggro or otherwise gain invulnerability are directed to use the applicable abilities as soon as they pick up aggro (e.g., Soulshatter). He’s still easy, but if you have IP nerfs, it’s a little bit of a gear check. ### High Astromancer Solarian No boss was hit harder by nerfs in TBC than Solarian, whose encounter went from a totally unique fight that required arcane resistance to a fight that is kind of just an easier Baron Geddon. IMO, she is the easiest boss in TBC 25-player raids. - Ranged bots stack up at a distance from Solarian; this leaves all bots with plenty of space to run away from other bots when they get Wrath of the Astromancer. - When Solarian vanishes, all bots will stack to AoE down the Solarium Agents that spawn. - When Solarian returns with two Solarium Priests, melee will divide into two groups, with one focused on each Solarium Priest. I think this method is not working correctly right now because when one Priest dies, the bots still on the second Priest are leaving it. I’ll need to decide whether I want to figure it out or just get rid of it because this fight is so easy regardless. - Priest bots will cast Fear Ward on the main tank to block the Psychic Scream during the final tank-and-spank Voidwalker phase, and the main tank will pick up Voidwalker Solarian as soon as she transforms. Note that the bots will not be knocked into the air by Wrath of the Astromancer. The issue is due to the presence of a check for knockbacks in Playerbots that causes bots to ignore knockbacks that would launch them at a velocity beyond a hardcoded value. I’ve increased that velocity limit on my own fork, and it does allow Wrath of the Astromancer (and other knockbacks that otherwise don’t work) to work on bots. But that’s obviously a broader issue and not addressed in this PR, and bots don’t take fall damage in any case. ### Kael’thas Sunstrider So this strategy has 23(!) action methods. But like in retail, this is actually an easy fight once it is learned because it is highly scripted. Unlike in other strategies I’ve done, the bots probably cannot do this fight by themselves unless they are way overgeared. This is because there are a few windows during which bots need to position themselves properly based on dynamic factors. But no RTSC is needed—you just need to have bots follow you to the right locations. Also note that the gear check for this strategy is higher than in retail because you have to get all of the legendary weapons down and looted before the advisors aggro in Phase 3, or it’s going to be an absolute shitshow (with human players, you can deal with there still being a couple of weapons up). For a point of reference, when I was first working on this strategy with damage reduced to 50% and bots pretty close to T4 BiS, I had almost no margin of error (I would usually get the weapons down with barely a second to spare). You will need at least 2 tanks, but 3 is better. Your main tank will need to be able to equip the legendary shield so you must use a Warrior or Paladin. However, it is ideal for the first assistant tank to be a Druid because they can equip the legendary staff. **Phase 1:** Fun fact—when you “kill” the advisors in this phase, they don’t actually die but get an aura applied called “Permanent Feign Death” (nice oxymoron). - _Thaladred_: You’re supposed to kite him, and bots can’t really kite, so the method is a poor man’s method of having the bot move away from him in a straight line when fixated. You want him to die in the far Southern part of the room. If he dies in a bad location, you may as well call a wipe and restart. What will work best for you will depend on your DPS since you don’t want to kill him before he gets to the location you want but also don’t want bots to be trapped up against a wall since they can’t properly kite him. The way that works best for me is to have bots stay back while I aggro the boss, and wait until right before Thaladred switches to his second fixate target before attacking. Note that if you do put bots on stay, when you put them back on follow, the bot that is then being fixated will remain on stay (because they need to disregard movement orders other than running away from Thaladred). So after Thaladred dies, make sure to manually type /follow or the bot that was fixated when you took the bots off of stay will not rejoin the fight. - _Sanguinar_: He will be tanked by your main tank, who will be targeted by your Priests for Fear Ward. Bots will wait to engage him; I made it a very generous time (12 seconds) because there is absolutely no rush in Phase 1. There’s no sense in being aggressive. During that time, the main tank will drag Sanguinar to the West wall. - _Capernian_: This is the first make-or-break part of the fight. Phase 1 Capernian was the most frequent cause for wipes for me. - She should be tanked by a Warlock. If you want to pick your Warlock tank, you can do so by the assistant flag, but if you don’t, the strategy will just pick your highest HP Warlock. If you raid without a Warlock, then you’re insane, but at least there’s a guard so your server won’t crash? - You do not need to add the tank strategy to your Warlock. There is a method that will automatically switch your selected tank Warlock between DPS and tank strategies at appropriate times because you need to squeeze out every drop of DPS you can get, particularly for Phase 2, where you’ll need your Warlock to be blowing up weapons with Seed of Corruption instead of spamming Searing Pain. You’ll want your Warlock to start with a DPS strategy as usual (since they should be DPSing Thaladred). - To engage Capernian, start running East right before Sanguinar dies. She will activate quickly, and you want to try to get in front of her (but not too close) before she aggros. - When Capernian aggros, your Warlock tank will immediately switch to the tank strategy and attack. Your main tank will run toward Capernian but not actually attack; their purpose will be to bait her Conflagration to reduce the chance that it hits your Warlock tank. Other melee will not engage Capernian. Ranged DPS will be idle for 12 seconds; during this time, you should run South to make sure they are not in range of Capernian. After 12 seconds, your ranged DPS will activate, move into range and spread out, and attack (it doesn’t seem possible to outrange Conflagrate, so if bots don’t spread, she will annihilate the entire ranged group with a single cast). Ideally, you kill her not too far from her starting position. If she ends up in the middle of the room, you should probably wipe and start over. - _Telonicus_: He is very easy in retail but actually is a big risk for wipes with respect to bots because his bombs will one-shot any non-tank, and bots will stupidly stand in front of him without a proper strategy. You should keep some distance from him before he aggros. Your first assistant tank will pick him up and move him to the West wall near Sanguinar. Again, there is a 12-second delay before DPS starts. Your melee DPS are coded to stay directly behind him and not get too close so they don’t get hit by bombs. **Phase 2:** Kael’thas will summon all weapons immediately after Telonicus is down. Just before Telonicus is down, you should move to the platform where the advisors originally were—you’ll be in better position for the raid to AoE down the weapons. - Your main tank will pick up the axe and move it away from the group. The axe is the biggest threat during this phase and can easily one-shot casters if not pulled away. - One of your Hunters will attempt to get aggro on the bow and move away from the group (as a hacky way of trying to turn the bow away from the group because you can’t really get a bot to do that directly). This method is hit or miss, but it shouldn’t be that big of a deal if your Hunter doesn’t pull it off properly. - Everybody else will prioritize weapons in the following order (but most damage will come from AoE, which is what you want or you will not beat the timer): staff, mace, sword, dagger, axe (ranged only), bow, and shield. - As weapons are defeated, bots will loot and equip them. If you have not disabled bot announcements in your config, you get to see your entire raid go nuts because they looted legendary items. - Here is what weapons bots will loot and equip. I don't know anything about DKs, having never played WotLK, so tell me if anything is wrong for them. - _Healers:_ Mace (if a healer normally uses a staff, it's best if they keep an OH in their bags for this fight) - _Tanks:_ Shield and sword for Paladins and DK, shield and dagger for warriors, staff for Druid - _Offensive_ casters: Staff - _Rogues:_ Sword and dagger if Combat or Subtlety, dagger only if Assassination - _DPS Death Knights, Retribution Paladins, Arms Warriors_: Axe - _Fury Warriors_: Dagger. I understand that due to Titan Grip, they should also have the Axe for best DPS; however, Fury Warriors have awful DPS (we’re talking barely above Prot-level) at this stage. Thus, my view is it is better to give them only the dagger so they will MH it and help break MC in Phase 4, since they will contribute hardly any DPS regardless. - _Cat Druids_: Staff - _Enhancement Shamans_: Dagger - _Hunters:_ Bow and dagger. Note that I do NOT have them loot the sword because they need the dagger in their mainhand to use to break MC in Phase 4; whatever marginal benefit they get from the sword as a stat stick is not worth losing this capability. If your Hunter uses a 2H, it is best to have them carry a 1H in their inventory so they can put something in the OH after they equip the dagger. - After looting weapons, bots with the staff will use it (once) to activate the Mental Protection Field. Hunters will use the bow to generate the legendary arrows and equip those (and will continue to do so during the fight if they use up the arrows). - If you wipe from this point forward, everybody will lose their legendary weapons, and by default, most bots will not automatically reequip their own weapons until a loot event occurs. This was extremely annoying, and therefore there is a noncombat method implemented that causes everybody to equip upgrades when they get within 150 yards of Kael’thas. I considered applying this to the whole instance, but I’m not sure if some people would not like that so I decided to limit things to the Kael’thas encounter. **Phase 3:** I highly recommend you have your Shamans drop Tremor Totems (co +tremor) during this phase. Doing so is not coded because I wanted to leave flexibility, but I think it is very helpful for Sanguinar. After the weapons die, you want to move your bots to a central location between the advisors. If Thaladred died closer to the middle of the room, ideally you position to the side of Thaladred so when he fixates he will not chase bots North into the other advisors. - Shamans will immediately use Heroism/Bloodlust. - Your melee tanks will bring Sanguinar and Telonicus to their tanking positions (same as Phase 1). If your first assistant tank is a Druid, they will be immune to Telonicus’s Remote Toy due to having the legendary staff’s aura activated and will also make your main tank immune. - One healer will stay by the Sanguinar and Telonicus tanking positions to heal the tanks. Once IsHealAssistantOfIndex() is fixed, you will be able to select this healer with the assistant flag. Right now, this will just be the last healer that joined your raid (per standard AC logic). - DPS priority will be Thaladred, Capernian (ranged only), Sanguinar, Telonicus. As with retail, the most chaotic period will be before Thaladred is killed, particularly if he chases bots into other advisors. I don’t have a great solution for this, but Capernian is significantly less dangerous during this phase thanks to the legendary staff. This is the last true breakpoint—if you get Thaladred down with your raid mostly intact, you are very likely to get the kill. **Phase 4:** Kael’thas will aggro immediately after all advisors are dead. - Your main tank will position Kael’thas at his original position. - Bots will move out of Flame Strikes. - Assist tanks will pick up Phoenixes. Since they die over time anyway, bots will not waste time attacking them. When Phoenixes die, they turn into an Egg—at that point, bots will switch to the Egg to destroy it before the Phoenix is reborn. - When Kael’thas puts up Shock Barrier and starts casting Pyroblast on your main tank (a one-shot), all bots will focus DPS on him (even if there is an egg up). You have 4 seconds to break the barrier (80K HP) and interrupt his Pyroblast. It is likely that you will not be able to if you are playing with IP nerfs and are in T4 gear. However, the main tank will use the legendary shield’s ability, which will allow them to absorb one cast, giving you 8 seconds to break the barrier and interrupt Pyroblast. Bots will put top priority on interrupting Pyroblast as soon as the barrier is down. - If a bot (or player) is mind controlled, bots with the legendary dagger (other than tanks) will move to MC’d players and use the following attacks to break MC: Shiv (Rogues), Hamstring (Warriors), Wing Clip (Hunters), and Stormstrike (Shamans). **Phase 5:** At 50% HP, Kael’thas enters a long RP sequence. This is a good time to kill any remaining Phoenixes and/or Eggs. - Kael’thas stops casting Pyroblast and Mind Control. - His main new ability is Gravity Lapse, and it doesn’t work properly on bots... He sucks in the entire raid then knocks everybody back in a different direction. What is supposed to happen is that players will end up floating in midair in different directions and at different heights. However, bots will immediately fall to the ground after getting knocked back. They will not actually hit the ground though and instead remain in a flying state right above the floor. - If you could move in 3D space, Netherbeam would be very easy to deal with. However, because that is not available to bots, they can spread only in 2D space and thus need to move farther to get properly spread, and they waste the first moments falling straight down. As a result, the damage from Netherbeam can be quite high, and the beginning of Gravity Lapse requires a lot of healing. I don’t really have a better way of dealing with this. - FWIW, I don’t think there is any existing method to make bots disperse in 3D anyway. - Kael’thas is supposed to use Nether Void when players are in midair, which creates clouds that reduce your max HP and thus make it more challenging to maneuver, but AC is bugged and he doesn’t use the ability at all (there’s been an open issue about this forever). For fuck's sake, that's all. --------- Co-authored-by: Keleborn <22352763+Celandriel@users.noreply.github.com> Co-authored-by: bash <hermensb@gmail.com> Co-authored-by: Revision <tkn963@gmail.com>
This commit is contained in:
@@ -77,39 +77,101 @@ void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target)
|
|||||||
// Intended for purposes of storing and erasing timers and trackers in associative containers
|
// Intended for purposes of storing and erasing timers and trackers in associative containers
|
||||||
bool IsMechanicTrackerBot(PlayerbotAI* botAI, Player* bot, uint32 mapId, Player* exclude)
|
bool IsMechanicTrackerBot(PlayerbotAI* botAI, Player* bot, uint32 mapId, Player* exclude)
|
||||||
{
|
{
|
||||||
if (Group* group = bot->GetGroup())
|
if (!botAI->IsDps(bot) || !bot->IsAlive() || bot->GetMapId() != mapId)
|
||||||
{
|
return false;
|
||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
|
||||||
{
|
|
||||||
Player* member = ref->GetSource();
|
|
||||||
if (!member || !member->IsAlive() || member->GetMapId() != mapId ||
|
|
||||||
!GET_PLAYERBOT_AI(member) || !botAI->IsDps(member))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (member != exclude)
|
Group* group = bot->GetGroup();
|
||||||
return member == bot;
|
if (!group)
|
||||||
}
|
return false;
|
||||||
|
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (!member || !member->IsAlive() || member->GetMapId() != mapId || member == exclude)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member);
|
||||||
|
if (!memberAI || !memberAI->IsDps(member))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return member == bot;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the first matching alive unit from a cell search of nearby npcs
|
// Requires the main tank to be alive
|
||||||
// More responsive than "find target," but performance cost is much higher
|
// Note that IsMainTank() will return the player with the main tank flag, even if dead
|
||||||
// Re: using the third parameter (false by default), some units are never considered
|
Player* GetGroupMainTank(PlayerbotAI* botAI, Player* bot)
|
||||||
// to be in combat (e.g., totems)
|
{
|
||||||
Unit* GetFirstAliveUnitByEntry(PlayerbotAI* botAI, uint32 entry, bool requireInCombat)
|
Group* group = bot->GetGroup();
|
||||||
|
if (!group)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (!member || !member->IsAlive())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (botAI->IsMainTank(member))
|
||||||
|
return member;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the alive assist tank of the specified index (0 = first, 1 = second, etc.)
|
||||||
|
// Priority: Assistants first, then Non-Assistants.
|
||||||
|
Player* GetGroupAssistTank(PlayerbotAI* botAI, Player* bot, uint8 index)
|
||||||
|
{
|
||||||
|
Group* group = bot->GetGroup();
|
||||||
|
if (!group)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
uint8 assistantCount = 0;
|
||||||
|
std::vector<Player*> nonAssistantTanks;
|
||||||
|
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (!member || !member->IsAlive())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (botAI->IsAssistTank(member))
|
||||||
|
{
|
||||||
|
if (group->IsAssistant(member->GetGUID()))
|
||||||
|
{
|
||||||
|
if (assistantCount == index)
|
||||||
|
return member;
|
||||||
|
|
||||||
|
assistantCount++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nonAssistantTanks.push_back(member);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the index wasn't found among assistants, check the non-assistants that were saved
|
||||||
|
uint8 nonAssistantIndex = index - assistantCount;
|
||||||
|
if (nonAssistantIndex < nonAssistantTanks.size())
|
||||||
|
return nonAssistantTanks[nonAssistantIndex];
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the first matching alive unit from PossibleTargetsValue within sightDistance from config
|
||||||
|
Unit* GetFirstAliveUnitByEntry(PlayerbotAI* botAI, uint32 entry)
|
||||||
{
|
{
|
||||||
auto const& npcs =
|
auto const& npcs =
|
||||||
botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest npcs")->Get();
|
botAI->GetAiObjectContext()->GetValue<GuidVector>("possible targets no los")->Get();
|
||||||
for (auto const& npcGuid : npcs)
|
for (auto const& npcGuid : npcs)
|
||||||
{
|
{
|
||||||
Unit* unit = botAI->GetUnit(npcGuid);
|
Unit* unit = botAI->GetUnit(npcGuid);
|
||||||
if (unit && unit->IsAlive() && unit->GetEntry() == entry)
|
if (unit && unit->IsAlive() && unit->GetEntry() == entry)
|
||||||
{
|
return unit;
|
||||||
if (!requireInCombat || unit->IsInCombat())
|
|
||||||
return unit;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|||||||
@@ -15,7 +15,10 @@ void MarkTargetWithCross(Player* bot, Unit* target);
|
|||||||
void MarkTargetWithMoon(Player* bot, Unit* target);
|
void MarkTargetWithMoon(Player* bot, Unit* target);
|
||||||
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target);
|
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target);
|
||||||
bool IsMechanicTrackerBot(PlayerbotAI* botAI, Player* bot, uint32 mapId, Player* exclude = nullptr);
|
bool IsMechanicTrackerBot(PlayerbotAI* botAI, Player* bot, uint32 mapId, Player* exclude = nullptr);
|
||||||
Unit* GetFirstAliveUnitByEntry(PlayerbotAI* botAI, uint32 entry, bool requireInCombat = false);
|
Player* GetGroupMainTank(PlayerbotAI* botAI, Player* bot);
|
||||||
|
Player* GetGroupAssistTank(PlayerbotAI* botAI, Player* bot, uint8 index);
|
||||||
|
Unit* GetFirstAliveUnitByEntry(
|
||||||
|
PlayerbotAI* botAI, uint32 entry);
|
||||||
Unit* GetNearestPlayerInRadius(Player* bot, float radius);
|
Unit* GetNearestPlayerInRadius(Player* bot, float radius);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -6,9 +6,10 @@
|
|||||||
#include "RaidMcStrategy.h"
|
#include "RaidMcStrategy.h"
|
||||||
#include "RaidBwlStrategy.h"
|
#include "RaidBwlStrategy.h"
|
||||||
#include "RaidKarazhanStrategy.h"
|
#include "RaidKarazhanStrategy.h"
|
||||||
#include "RaidMagtheridonStrategy.h"
|
|
||||||
#include "RaidGruulsLairStrategy.h"
|
#include "RaidGruulsLairStrategy.h"
|
||||||
|
#include "RaidMagtheridonStrategy.h"
|
||||||
#include "RaidSSCStrategy.h"
|
#include "RaidSSCStrategy.h"
|
||||||
|
#include "RaidTempestKeepStrategy.h"
|
||||||
#include "RaidOsStrategy.h"
|
#include "RaidOsStrategy.h"
|
||||||
#include "RaidEoEStrategy.h"
|
#include "RaidEoEStrategy.h"
|
||||||
#include "RaidVoAStrategy.h"
|
#include "RaidVoAStrategy.h"
|
||||||
@@ -25,9 +26,10 @@ public:
|
|||||||
creators["moltencore"] = &RaidStrategyContext::moltencore;
|
creators["moltencore"] = &RaidStrategyContext::moltencore;
|
||||||
creators["bwl"] = &RaidStrategyContext::bwl;
|
creators["bwl"] = &RaidStrategyContext::bwl;
|
||||||
creators["karazhan"] = &RaidStrategyContext::karazhan;
|
creators["karazhan"] = &RaidStrategyContext::karazhan;
|
||||||
creators["magtheridon"] = &RaidStrategyContext::magtheridon;
|
|
||||||
creators["gruulslair"] = &RaidStrategyContext::gruulslair;
|
creators["gruulslair"] = &RaidStrategyContext::gruulslair;
|
||||||
|
creators["magtheridon"] = &RaidStrategyContext::magtheridon;
|
||||||
creators["ssc"] = &RaidStrategyContext::ssc;
|
creators["ssc"] = &RaidStrategyContext::ssc;
|
||||||
|
creators["tempestkeep"] = &RaidStrategyContext::tempestkeep;
|
||||||
creators["wotlk-os"] = &RaidStrategyContext::wotlk_os;
|
creators["wotlk-os"] = &RaidStrategyContext::wotlk_os;
|
||||||
creators["wotlk-eoe"] = &RaidStrategyContext::wotlk_eoe;
|
creators["wotlk-eoe"] = &RaidStrategyContext::wotlk_eoe;
|
||||||
creators["voa"] = &RaidStrategyContext::voa;
|
creators["voa"] = &RaidStrategyContext::voa;
|
||||||
@@ -41,9 +43,10 @@ private:
|
|||||||
static Strategy* moltencore(PlayerbotAI* botAI) { return new RaidMcStrategy(botAI); }
|
static Strategy* moltencore(PlayerbotAI* botAI) { return new RaidMcStrategy(botAI); }
|
||||||
static Strategy* bwl(PlayerbotAI* botAI) { return new RaidBwlStrategy(botAI); }
|
static Strategy* bwl(PlayerbotAI* botAI) { return new RaidBwlStrategy(botAI); }
|
||||||
static Strategy* karazhan(PlayerbotAI* botAI) { return new RaidKarazhanStrategy(botAI); }
|
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* gruulslair(PlayerbotAI* botAI) { return new RaidGruulsLairStrategy(botAI); }
|
||||||
|
static Strategy* magtheridon(PlayerbotAI* botAI) { return new RaidMagtheridonStrategy(botAI); }
|
||||||
static Strategy* ssc(PlayerbotAI* botAI) { return new RaidSSCStrategy(botAI); }
|
static Strategy* ssc(PlayerbotAI* botAI) { return new RaidSSCStrategy(botAI); }
|
||||||
|
static Strategy* tempestkeep(PlayerbotAI* botAI) { return new RaidTempestKeepStrategy(botAI); }
|
||||||
static Strategy* wotlk_os(PlayerbotAI* botAI) { return new RaidOsStrategy(botAI); }
|
static Strategy* wotlk_os(PlayerbotAI* botAI) { return new RaidOsStrategy(botAI); }
|
||||||
static Strategy* wotlk_eoe(PlayerbotAI* botAI) { return new RaidEoEStrategy(botAI); }
|
static Strategy* wotlk_eoe(PlayerbotAI* botAI) { return new RaidEoEStrategy(botAI); }
|
||||||
static Strategy* voa(PlayerbotAI* botAI) { return new RaidVoAStrategy(botAI); }
|
static Strategy* voa(PlayerbotAI* botAI) { return new RaidVoAStrategy(botAI); }
|
||||||
|
|||||||
2050
src/Ai/Raid/TempestKeep/Action/RaidTempestKeepActions.cpp
Normal file
2050
src/Ai/Raid/TempestKeep/Action/RaidTempestKeepActions.cpp
Normal file
File diff suppressed because it is too large
Load Diff
413
src/Ai/Raid/TempestKeep/Action/RaidTempestKeepActions.h
Normal file
413
src/Ai/Raid/TempestKeep/Action/RaidTempestKeepActions.h
Normal file
@@ -0,0 +1,413 @@
|
|||||||
|
#ifndef _PLAYERBOT_RAIDTEMPESTKEEPACTIONS_H
|
||||||
|
#define _PLAYERBOT_RAIDTEMPESTKEEPACTIONS_H
|
||||||
|
|
||||||
|
#include "RaidTempestKeepHelpers.h"
|
||||||
|
#include "RaidTempestKeepKaelthasBossAI.h"
|
||||||
|
#include "Action.h"
|
||||||
|
#include "AttackAction.h"
|
||||||
|
#include "MovementActions.h"
|
||||||
|
|
||||||
|
using namespace TempestKeepHelpers;
|
||||||
|
|
||||||
|
// Trash
|
||||||
|
|
||||||
|
class CrimsonHandCenturionCastPolymorphAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CrimsonHandCenturionCastPolymorphAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "crimson hand centurion cast polymorph") : Action(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Al'ar <Phoenix God>
|
||||||
|
|
||||||
|
class AlarMisdirectBossToMainTankAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AlarMisdirectBossToMainTankAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "al'ar misdirect boss to main tank") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AlarBossTanksMoveBetweenPlatformsAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AlarBossTanksMoveBetweenPlatformsAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "al'ar boss tanks move between platforms") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool PositionMainTank(Unit* alar, int8 locationIndex);
|
||||||
|
bool PositionAssistTank(Unit* alar, int8 locationIndex);
|
||||||
|
};
|
||||||
|
|
||||||
|
class AlarMeleeDpsMoveBetweenPlatformsAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AlarMeleeDpsMoveBetweenPlatformsAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "al'ar melee dps move between platforms") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AlarRangedAndEmberTankMoveUnderPlatformsAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AlarRangedAndEmberTankMoveUnderPlatformsAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "al'ar ranged and ember tank move under platforms") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AlarAssistTanksPickUpEmbersAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AlarAssistTanksPickUpEmbersAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "al'ar assist tanks pick up embers") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool HandlePhase1Embers(Unit* alar);
|
||||||
|
bool HandlePhase2Embers(Unit* alar);
|
||||||
|
};
|
||||||
|
|
||||||
|
class AlarRangedDpsPrioritizeEmbersAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AlarRangedDpsPrioritizeEmbersAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "al'ar ranged dps prioritize embers") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AlarJumpFromPlatformAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AlarJumpFromPlatformAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "al'ar jump from platform") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AlarMoveAwayFromRebirthAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AlarMoveAwayFromRebirthAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "al'ar move away from rebirth") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AlarSwapTanksOnBossAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AlarSwapTanksOnBossAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "al'ar swap tanks on boss") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AlarAvoidFlamePatchesAndDiveBombsAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AlarAvoidFlamePatchesAndDiveBombsAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "al'ar avoid flame patches and dive bombs") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool AvoidFlamePatch();
|
||||||
|
bool HandleDiveBomb(Unit* alar);
|
||||||
|
};
|
||||||
|
|
||||||
|
class AlarReturnToRoomCenterAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AlarReturnToRoomCenterAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "al'ar return to room center") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AlarManagePhaseTrackerAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AlarManagePhaseTrackerAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "al'ar manage phase tracker") : Action(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Void Reaver
|
||||||
|
|
||||||
|
class VoidReaverTanksPositionBossAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VoidReaverTanksPositionBossAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "void reaver tanks position boss") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VoidReaverUseAggroDumpAbilityAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VoidReaverUseAggroDumpAbilityAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "void reaver use aggro dump ability") : Action(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VoidReaverSpreadRangedAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VoidReaverSpreadRangedAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "void reaver spread ranged") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int GetHealerIndex(Group* group, int& healerCount);
|
||||||
|
int GetRangedDpsIndex(Group* group, int& rangedDpsCount);
|
||||||
|
};
|
||||||
|
|
||||||
|
class VoidReaverAvoidArcaneOrbAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VoidReaverAvoidArcaneOrbAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "void reaver avoid arcane orb") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VoidReaverEraseTrackersAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VoidReaverEraseTrackersAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "void reaver erase trackers") : Action(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// High Astromancer Solarian
|
||||||
|
|
||||||
|
class HighAstromancerSolarianRangedLeaveSpaceForMeleeAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighAstromancerSolarianRangedLeaveSpaceForMeleeAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "high astromancer solarian ranged leave space for melee") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HighAstromancerSolarianMoveAwayFromGroupAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighAstromancerSolarianMoveAwayFromGroupAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "high astromancer solarian move away from group") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HighAstromancerSolarianStackForAoeAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighAstromancerSolarianStackForAoeAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "high astromancer solarian stack for aoe") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HighAstromancerSolarianTargetSolariumPriestsAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighAstromancerSolarianTargetSolariumPriestsAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "high astromancer solarian target solarium priests") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::pair<Unit*, Unit*> GetSolariumPriests(PlayerbotAI* botAI);
|
||||||
|
std::vector<Player*> GetMeleeBots(Group* group);
|
||||||
|
Unit* AssignSolariumPriestsToBots(const std::pair<Unit*, Unit*>& priestsPair, const std::vector<Player*>& meleeMembers);
|
||||||
|
};
|
||||||
|
|
||||||
|
class HighAstromancerSolarianCastFearWardOnMainTankAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighAstromancerSolarianCastFearWardOnMainTankAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "high astromancer solarian cast fear ward on main tank") : Action(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Kael'thas Sunstrider <Lord of the Blood Elves>
|
||||||
|
|
||||||
|
class KaelthasSunstriderKiteThaladredAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderKiteThaladredAction(
|
||||||
|
PlayerbotAI* botAI) : MovementAction(botAI, "kael'thas sunstrider kite thaladred") {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderMisdirectAdvisorsToTanksAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderMisdirectAdvisorsToTanksAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "kael'thas sunstrider misdirect advisors to tanks") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderMainTankPositionSanguinarAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderMainTankPositionSanguinarAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "kael'thas sunstrider main tank position sanguinar") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderCastFearWardOnSanguinarTankAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderCastFearWardOnSanguinarTankAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "kael'thas sunstrider cast fear ward on sanguinar tank") : Action(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderWarlockTankPositionCapernianAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderWarlockTankPositionCapernianAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "kael'thas sunstrider warlock tank position capernian") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderSpreadAndMoveAwayFromCapernianAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderSpreadAndMoveAwayFromCapernianAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "kael'thas sunstrider spread and move away from capernian") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool RangedBotsDisperse(boss_kaelthas* kaelAI, Unit* capernian);
|
||||||
|
bool MeleeStayBackFromCapernian(Unit* capernian);
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderFirstAssistTankPositionTelonicusAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderFirstAssistTankPositionTelonicusAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "kael'thas sunstrider first assist tank position telonicus") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderHandleAdvisorRolesInPhase3Action : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderHandleAdvisorRolesInPhase3Action(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "kael'thas sunstrider handle advisor roles in phase 3") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderAssignAdvisorDpsPriorityAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderAssignAdvisorDpsPriorityAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "kael'thas sunstrider assign advisor dps priority") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderManageAdvisorDpsTimerAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderManageAdvisorDpsTimerAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "kael'thas sunstrider manage advisor dps timer") : Action(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderAssignLegendaryWeaponDpsPriorityAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderAssignLegendaryWeaponDpsPriorityAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "kael'thas sunstrider assign legendary weapon dps priority") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderMoveDevastationAwayAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderMoveDevastationAwayAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "kael'thas sunstrider move devastation away") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderLootLegendaryWeaponsAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderLootLegendaryWeaponsAction(
|
||||||
|
PlayerbotAI* botAI) : MovementAction(botAI, "kael'thas sunstrider loot legendary weapons") {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool ShouldBotLootWeapon(uint32 weaponEntry);
|
||||||
|
bool LootWeapon(uint32 weaponEntry, uint32 itemId);
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderUseLegendaryWeaponsAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderUseLegendaryWeaponsAction(
|
||||||
|
PlayerbotAI* botAI) : Action(botAI, "kael'thas sunstrider use legendary weapons") {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool UsePhaseshiftBulwark();
|
||||||
|
bool UseStaffOfDisintegration();
|
||||||
|
bool UseNetherstrandLongbow();
|
||||||
|
bool UseEquippedItemWithPacket(Item* item);
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderReequipGearAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderReequipGearAction(
|
||||||
|
PlayerbotAI* botAI) : Action(botAI, "kael'thas sunstrider reequip gear") {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderMainTankPositionBossAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderMainTankPositionBossAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "kael'thas sunstrider main tank position boss") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderAvoidFlameStrikeAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderAvoidFlameStrikeAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "kael'thas sunstrider avoid flame strike") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderHandlePhoenixesAndEggsAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderHandlePhoenixesAndEggsAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "kael'thas sunstrider handle phoenixes and eggs") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool AssistTanksPickUpPhoenixes();
|
||||||
|
bool NonTanksDestroyEggsAndAvoidPhoenixes();
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderBreakMindControlAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderBreakMindControlAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "kael'thas sunstrider break mind control") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderBreakThroughShockBarrierAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderBreakThroughShockBarrierAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "kael'thas sunstrider break through shock barrier") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderSpreadOutInMidairAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderSpreadOutInMidairAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "kael'thas sunstrider spread out in midair") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,422 @@
|
|||||||
|
#include "RaidTempestKeepMultipliers.h"
|
||||||
|
#include "RaidTempestKeepActions.h"
|
||||||
|
#include "RaidTempestKeepHelpers.h"
|
||||||
|
#include "RaidTempestKeepKaelthasBossAI.h"
|
||||||
|
#include "ChooseTargetActions.h"
|
||||||
|
#include "DKActions.h"
|
||||||
|
#include "DruidActions.h"
|
||||||
|
#include "DruidBearActions.h"
|
||||||
|
#include "EquipAction.h"
|
||||||
|
#include "FollowActions.h"
|
||||||
|
#include "HunterActions.h"
|
||||||
|
#include "MageActions.h"
|
||||||
|
#include "PaladinActions.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
#include "RogueActions.h"
|
||||||
|
#include "ShamanActions.h"
|
||||||
|
#include "WarlockActions.h"
|
||||||
|
#include "WarriorActions.h"
|
||||||
|
|
||||||
|
// Al'ar <Phoenix God>
|
||||||
|
|
||||||
|
float AlarMoveBetweenPlatformsMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
Unit* alar = AI_VALUE2(Unit*, "find target", "al'ar");
|
||||||
|
if (!alar)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (isAlarInPhase2[alar->GetMap()->GetInstanceId()])
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<ReachTargetAction*>(action) ||
|
||||||
|
dynamic_cast<TankFaceAction*>(action) ||
|
||||||
|
dynamic_cast<CastKillingSpreeAction*>(action) ||
|
||||||
|
dynamic_cast<CastDisengageAction*>(action) ||
|
||||||
|
dynamic_cast<CastBlinkBackAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
if (botAI->IsDps(bot) &&
|
||||||
|
dynamic_cast<CastReachTargetSpellAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float AlarDisableDisperseMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (!AI_VALUE2(Unit*, "find target", "al'ar"))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<CombatFormationMoveAction*>(action) &&
|
||||||
|
!dynamic_cast<TankFaceAction*>(action) &&
|
||||||
|
!dynamic_cast<SetBehindTargetAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<FollowAction*>(action) ||
|
||||||
|
dynamic_cast<FleeAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float AlarDisableTankAssistMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (bot->GetVictim() == nullptr)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (!botAI->IsTank(bot))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (!AI_VALUE2(Unit*, "find target", "al'ar"))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<TankAssistAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float AlarStayAwayFromRebirthMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
Unit* alar = AI_VALUE2(Unit*, "find target", "al'ar");
|
||||||
|
if (!alar)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
Creature* alarCreature = alar->ToCreature();
|
||||||
|
if (!alarCreature || alarCreature->GetReactState() != REACT_PASSIVE)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<MovementAction*>(action) &&
|
||||||
|
!dynamic_cast<AlarMoveAwayFromRebirthAction*>(action) &&
|
||||||
|
!dynamic_cast<AlarAvoidFlamePatchesAndDiveBombsAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float AlarPhase2NoTankingIfArmorMeltedMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (!bot->HasAura(SPELL_MELT_ARMOR))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
Unit* alar = AI_VALUE2(Unit*, "find target", "al'ar");
|
||||||
|
if (!alar || bot->GetTarget() != alar->GetGUID())
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<CastTauntAction*>(action) ||
|
||||||
|
dynamic_cast<CastGrowlAction*>(action) ||
|
||||||
|
dynamic_cast<CastHandOfReckoningAction*>(action) ||
|
||||||
|
dynamic_cast<CastDarkCommandAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Void Reaver
|
||||||
|
|
||||||
|
float VoidReaverMaintainPositionsMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (!AI_VALUE2(Unit*, "find target", "void reaver"))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<CombatFormationMoveAction*>(action) &&
|
||||||
|
!dynamic_cast<SetBehindTargetAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// High Astromancer Solarian
|
||||||
|
|
||||||
|
float HighAstromancerSolarianMaintainPositionMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
Unit* astromancer = AI_VALUE2(Unit*, "find target", "high astromancer solarian");
|
||||||
|
if (!astromancer || astromancer->HasAura(SPELL_SOLARIAN_TRANSFORM))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (botAI->IsRanged(bot) &&
|
||||||
|
(dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||||
|
dynamic_cast<FleeAction*>(action) ||
|
||||||
|
dynamic_cast<CastBlinkBackAction*>(action) ||
|
||||||
|
dynamic_cast<CastDisengageAction*>(action)))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
if (!bot->HasAura(SPELL_WRATH_OF_THE_ASTROMANCER))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<CastReachTargetSpellAction*>(action) ||
|
||||||
|
(dynamic_cast<MovementAction*>(action) &&
|
||||||
|
!dynamic_cast<HighAstromancerSolarianMoveAwayFromGroupAction*>(action)))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float HighAstromancerSolarianDisableTankAssistMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (bot->GetVictim() == nullptr)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (!botAI->IsTank(bot))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (!AI_VALUE2(Unit*, "find target", "solarium priest"))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<TankAssistAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kael'thas Sunstrider <Lord of the Blood Elves>
|
||||||
|
|
||||||
|
float KaelthasSunstriderWaitForDpsMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
Unit* kaelthas = AI_VALUE2(Unit*, "find target", "kael'thas sunstrider");
|
||||||
|
if (!kaelthas)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
boss_kaelthas* kaelAI = dynamic_cast<boss_kaelthas*>(kaelthas->GetAI());
|
||||||
|
if (!kaelAI || kaelAI->GetPhase() != PHASE_SINGLE_ADVISOR)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<KaelthasSunstriderMisdirectAdvisorsToTanksAction*>(action))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
const time_t now = std::time(nullptr);
|
||||||
|
constexpr uint8 dpsWaitSeconds = 10;
|
||||||
|
|
||||||
|
auto it = advisorDpsWaitTimer.find(kaelthas->GetMap()->GetInstanceId());
|
||||||
|
if (it == advisorDpsWaitTimer.end() || (now - it->second) < dpsWaitSeconds)
|
||||||
|
{
|
||||||
|
Unit* sanguinar = AI_VALUE2(Unit*, "find target", "lord sanguinar");
|
||||||
|
Unit* capernian = AI_VALUE2(Unit*, "find target", "grand astromancer capernian");
|
||||||
|
Unit* telonicus = AI_VALUE2(Unit*, "find target", "master engineer telonicus");
|
||||||
|
|
||||||
|
auto isAdvisorActive = [](Unit* advisor)
|
||||||
|
{
|
||||||
|
return advisor && !advisor->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE) &&
|
||||||
|
!advisor->HasAura(SPELL_PERMANENT_FEIGN_DEATH);
|
||||||
|
};
|
||||||
|
|
||||||
|
if ((isAdvisorActive(sanguinar) && botAI->IsMainTank(bot)) ||
|
||||||
|
(isAdvisorActive(telonicus) && botAI->IsAssistTankOfIndex(bot, 0, true)) ||
|
||||||
|
(isAdvisorActive(capernian) && (botAI->IsMainTank(bot) || GetCapernianTank(bot) == bot)))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
bool shouldHoldDps =
|
||||||
|
(isAdvisorActive(sanguinar) && !botAI->IsMainTank(bot)) ||
|
||||||
|
(isAdvisorActive(telonicus) && !botAI->IsAssistTankOfIndex(bot, 0, true)) ||
|
||||||
|
(isAdvisorActive(capernian) && !botAI->IsMainTank(bot) && GetCapernianTank(bot) != bot);
|
||||||
|
|
||||||
|
if (shouldHoldDps &&
|
||||||
|
(dynamic_cast<AttackAction*>(action) ||
|
||||||
|
(dynamic_cast<CastSpellAction*>(action) &&
|
||||||
|
!dynamic_cast<CastHealingSpellAction*>(action))))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float KaelthasSunstriderKiteThaladredMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
Unit* kaelthas = AI_VALUE2(Unit*, "find target", "kael'thas sunstrider");
|
||||||
|
if (!kaelthas)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
boss_kaelthas* kaelAI = dynamic_cast<boss_kaelthas*>(kaelthas->GetAI());
|
||||||
|
if (!kaelAI)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (botAI->IsTank(bot) && kaelAI->GetPhase() == PHASE_ALL_ADVISORS)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
Unit* thaladred = AI_VALUE2(Unit*, "find target", "thaladred the darkener");
|
||||||
|
if (!thaladred || thaladred->GetVictim() != bot ||
|
||||||
|
thaladred->HasAura(SPELL_PERMANENT_FEIGN_DEATH))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<MovementAction*>(action) &&
|
||||||
|
!dynamic_cast<KaelthasSunstriderKiteThaladredAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float KaelthasSunstriderControlMisdirectionMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (bot->getClass() != CLASS_HUNTER)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
Unit* kaelthas = AI_VALUE2(Unit*, "find target", "kael'thas sunstrider");
|
||||||
|
if (!kaelthas)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
boss_kaelthas* kaelAI = dynamic_cast<boss_kaelthas*>(kaelthas->GetAI());
|
||||||
|
if (!kaelAI || kaelAI->GetPhase() == PHASE_FINAL)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<CastMisdirectionOnMainTankAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float KaelthasSunstriderKeepDistanceFromCapernianMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
Unit* kaelthas = AI_VALUE2(Unit*, "find target", "kael'thas sunstrider");
|
||||||
|
if (!kaelthas)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
boss_kaelthas* kaelAI = dynamic_cast<boss_kaelthas*>(kaelthas->GetAI());
|
||||||
|
if (!kaelAI || kaelAI->GetPhase() != PHASE_SINGLE_ADVISOR)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
Unit* capernian = AI_VALUE2(Unit*, "find target", "grand astromancer capernian");
|
||||||
|
if (!capernian || capernian->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE) ||
|
||||||
|
capernian->HasAura(SPELL_PERMANENT_FEIGN_DEATH))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<MovementAction*>(action) &&
|
||||||
|
!dynamic_cast<AttackAction*>(action) &&
|
||||||
|
!dynamic_cast<KaelthasSunstriderSpreadAndMoveAwayFromCapernianAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float KaelthasSunstriderManageWeaponTankingMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (!botAI->IsTank(bot))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
Unit* kaelthas = AI_VALUE2(Unit*, "find target", "kael'thas sunstrider");
|
||||||
|
if (!kaelthas)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
boss_kaelthas* kaelAI = dynamic_cast<boss_kaelthas*>(kaelthas->GetAI());
|
||||||
|
if (!kaelAI)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (kaelAI->GetPhase() != PHASE_WEAPONS &&
|
||||||
|
dynamic_cast<TankFaceAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
if (!botAI->IsMainTank(bot))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
// Try to keep main tank from grabbing aggro on any weapon other than the axe
|
||||||
|
if (kaelAI->GetPhase() == PHASE_WEAPONS &&
|
||||||
|
(dynamic_cast<TankAssistAction*>(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 KaelthasSunstriderDisableAdvisorTankAssistMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (bot->GetVictim() == nullptr || !botAI->IsTank(bot))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
Unit* kaelthas = AI_VALUE2(Unit*, "find target", "kael'thas sunstrider");
|
||||||
|
if (!kaelthas)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
boss_kaelthas* kaelAI = dynamic_cast<boss_kaelthas*>(kaelthas->GetAI());
|
||||||
|
if (!kaelAI)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (kaelAI->GetPhase() != PHASE_SINGLE_ADVISOR &&
|
||||||
|
kaelAI->GetPhase() != PHASE_ALL_ADVISORS)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<TankAssistAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float KaelthasSunstriderDisableDisperseMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (!AI_VALUE2(Unit*, "find target", "kael'thas sunstrider"))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<CombatFormationMoveAction*>(action) &&
|
||||||
|
!dynamic_cast<TankFaceAction*>(action) &&
|
||||||
|
!dynamic_cast<SetBehindTargetAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bloodlust/Heroism and other major cooldowns should be used at the start of Phase 3
|
||||||
|
float KaelthasSunstriderDelayCooldownsMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
Unit* kaelthas = AI_VALUE2(Unit*, "find target", "kael'thas sunstrider");
|
||||||
|
if (!kaelthas)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
boss_kaelthas* kaelAI = dynamic_cast<boss_kaelthas*>(kaelthas->GetAI());
|
||||||
|
if (!kaelAI || kaelAI->GetPhase() == PHASE_ALL_ADVISORS ||
|
||||||
|
kaelAI->GetPhase() == PHASE_FINAL)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (bot->getClass() == CLASS_SHAMAN &&
|
||||||
|
(dynamic_cast<CastBloodlustAction*>(action) ||
|
||||||
|
dynamic_cast<CastHeroismAction*>(action)))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
if (botAI->IsDps(bot) &&
|
||||||
|
(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 KaelthasSunstriderStaySpreadDuringGravityLapseMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (!bot->HasAura(SPELL_GRAVITY_LAPSE))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<MovementAction*>(action) &&
|
||||||
|
!dynamic_cast<KaelthasSunstriderSpreadOutInMidairAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
150
src/Ai/Raid/TempestKeep/Multiplier/RaidTempestKeepMultipliers.h
Normal file
150
src/Ai/Raid/TempestKeep/Multiplier/RaidTempestKeepMultipliers.h
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
#ifndef _PLAYERBOT_RAIDTEMPESTKEEPMULTIPLIERS_H
|
||||||
|
#define _PLAYERBOT_RAIDTEMPESTKEEPMULTIPLIERS_H
|
||||||
|
|
||||||
|
#include "Multiplier.h"
|
||||||
|
|
||||||
|
// Al'ar <Phoenix God>
|
||||||
|
|
||||||
|
class AlarMoveBetweenPlatformsMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AlarMoveBetweenPlatformsMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "al'ar move between platforms multiplier") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class AlarDisableDisperseMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AlarDisableDisperseMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "al'ar disable disperse multiplier") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class AlarDisableTankAssistMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AlarDisableTankAssistMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "al'ar disable tank assist multiplier") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class AlarStayAwayFromRebirthMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AlarStayAwayFromRebirthMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "al'ar stay away from rebirth multiplier") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class AlarPhase2NoTankingIfArmorMeltedMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AlarPhase2NoTankingIfArmorMeltedMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "al'ar phase 2 no tanking if armor melted multiplier") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Void Reaver
|
||||||
|
|
||||||
|
class VoidReaverMaintainPositionsMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VoidReaverMaintainPositionsMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "void reaver maintain positions multiplier") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
// High Astromancer Solarian
|
||||||
|
|
||||||
|
class HighAstromancerSolarianDisableTankAssistMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighAstromancerSolarianDisableTankAssistMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "high astromancer solarian disable tank assist multiplier") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class HighAstromancerSolarianMaintainPositionMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighAstromancerSolarianMaintainPositionMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "high astromancer solarian maintain position multiplier") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Kael'thas Sunstrider <Lord of the Blood Elves>
|
||||||
|
|
||||||
|
class KaelthasSunstriderWaitForDpsMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderWaitForDpsMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "kael'thas sunstrider wait for dps multiplier") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderKiteThaladredMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderKiteThaladredMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "kael'thas sunstrider kite thaladred multiplier") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderControlMisdirectionMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderControlMisdirectionMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "kael'thas sunstrider control misdirection multiplier") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderKeepDistanceFromCapernianMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderKeepDistanceFromCapernianMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "kael'thas sunstrider keep distance from capernian multiplier") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderManageWeaponTankingMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderManageWeaponTankingMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "kael'thas sunstrider manage weapon tanking multiplier") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderDisableAdvisorTankAssistMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderDisableAdvisorTankAssistMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "kael'thas sunstrider disable advisor tank assist multiplier") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderDisableDisperseMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderDisableDisperseMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "kael'thas sunstrider disable disperse multiplier") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderDelayCooldownsMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderDelayCooldownsMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "kael'thas sunstrider delay cooldowns multiplier") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderStaySpreadDuringGravityLapseMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderStaySpreadDuringGravityLapseMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "kael'thas sunstrider stay spread during gravity lapse multiplier") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
289
src/Ai/Raid/TempestKeep/RaidTempestKeepActionContext.h
Normal file
289
src/Ai/Raid/TempestKeep/RaidTempestKeepActionContext.h
Normal file
@@ -0,0 +1,289 @@
|
|||||||
|
#ifndef _PLAYERBOT_RAIDTEMPESTKEEPACTIONCONTEXT_H
|
||||||
|
#define _PLAYERBOT_RAIDTEMPESTKEEPACTIONCONTEXT_H
|
||||||
|
|
||||||
|
#include "RaidTempestKeepActions.h"
|
||||||
|
#include "NamedObjectContext.h"
|
||||||
|
|
||||||
|
class RaidTempestKeepActionContext : public NamedObjectContext<Action>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RaidTempestKeepActionContext()
|
||||||
|
{
|
||||||
|
// Trash
|
||||||
|
creators["crimson hand centurion cast polymorph"] =
|
||||||
|
&RaidTempestKeepActionContext::crimson_hand_centurion_cast_polymorph;
|
||||||
|
|
||||||
|
// Al'ar <Phoenix God>
|
||||||
|
creators["al'ar misdirect boss to main tank"] =
|
||||||
|
&RaidTempestKeepActionContext::alar_misdirect_boss_to_main_tank;
|
||||||
|
|
||||||
|
creators["al'ar boss tanks move between platforms"] =
|
||||||
|
&RaidTempestKeepActionContext::alar_boss_tanks_move_between_platforms;
|
||||||
|
|
||||||
|
creators["al'ar melee dps move between platforms"] =
|
||||||
|
&RaidTempestKeepActionContext::alar_melee_dps_move_between_platforms;
|
||||||
|
|
||||||
|
creators["al'ar ranged and ember tank move under platforms"] =
|
||||||
|
&RaidTempestKeepActionContext::alar_ranged_and_ember_tank_move_under_platforms;
|
||||||
|
|
||||||
|
creators["al'ar assist tanks pick up embers"] =
|
||||||
|
&RaidTempestKeepActionContext::alar_assist_tanks_pick_up_embers;
|
||||||
|
|
||||||
|
creators["al'ar ranged dps prioritize embers"] =
|
||||||
|
&RaidTempestKeepActionContext::alar_ranged_dps_prioritize_embers;
|
||||||
|
|
||||||
|
creators["al'ar jump from platform"] =
|
||||||
|
&RaidTempestKeepActionContext::alar_jump_from_platform;
|
||||||
|
|
||||||
|
creators["al'ar move away from rebirth"] =
|
||||||
|
&RaidTempestKeepActionContext::alar_move_away_from_rebirth;
|
||||||
|
|
||||||
|
creators["al'ar swap tanks on boss"] =
|
||||||
|
&RaidTempestKeepActionContext::alar_swap_tanks_on_boss;
|
||||||
|
|
||||||
|
creators["al'ar avoid flame patches and dive bombs"] =
|
||||||
|
&RaidTempestKeepActionContext::alar_avoid_flame_patches_and_dive_bombs;
|
||||||
|
|
||||||
|
creators["al'ar return to room center"] =
|
||||||
|
&RaidTempestKeepActionContext::alar_return_to_room_center;
|
||||||
|
|
||||||
|
creators["al'ar manage phase tracker"] =
|
||||||
|
&RaidTempestKeepActionContext::alar_manage_phase_tracker;
|
||||||
|
|
||||||
|
// Void Reaver
|
||||||
|
creators["void reaver tanks position boss"] =
|
||||||
|
&RaidTempestKeepActionContext::void_reaver_tanks_position_boss;
|
||||||
|
|
||||||
|
creators["void reaver use aggro dump ability"] =
|
||||||
|
&RaidTempestKeepActionContext::void_reaver_use_aggro_dump_ability;
|
||||||
|
|
||||||
|
creators["void reaver spread ranged"] =
|
||||||
|
&RaidTempestKeepActionContext::void_reaver_spread_ranged;
|
||||||
|
|
||||||
|
creators["void reaver avoid arcane orb"] =
|
||||||
|
&RaidTempestKeepActionContext::void_reaver_avoid_arcane_orb;
|
||||||
|
|
||||||
|
creators["void reaver erase trackers"] =
|
||||||
|
&RaidTempestKeepActionContext::void_reaver_erase_trackers;
|
||||||
|
|
||||||
|
// High Astromancer Solarian
|
||||||
|
creators["high astromancer solarian ranged leave space for melee"] =
|
||||||
|
&RaidTempestKeepActionContext::high_astromancer_solarian_ranged_leave_space_for_melee;
|
||||||
|
|
||||||
|
creators["high astromancer solarian move away from group"] =
|
||||||
|
&RaidTempestKeepActionContext::high_astromancer_solarian_move_away_from_group;
|
||||||
|
|
||||||
|
creators["high astromancer solarian stack for aoe"] =
|
||||||
|
&RaidTempestKeepActionContext::high_astromancer_solarian_stack_for_aoe;
|
||||||
|
|
||||||
|
creators["high astromancer solarian target solarium priests"] =
|
||||||
|
&RaidTempestKeepActionContext::high_astromancer_solarian_target_solarium_priests;
|
||||||
|
|
||||||
|
creators["high astromancer solarian cast fear ward on main tank"] =
|
||||||
|
&RaidTempestKeepActionContext::high_astromancer_solarian_cast_fear_ward_on_main_tank;
|
||||||
|
|
||||||
|
// Kael'thas Sunstrider <Lord of the Blood Elves>
|
||||||
|
creators["kael'thas sunstrider kite thaladred"] =
|
||||||
|
&RaidTempestKeepActionContext::kaelthas_sunstrider_kite_thaladred;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider misdirect advisors to tanks"] =
|
||||||
|
&RaidTempestKeepActionContext::kaelthas_sunstrider_misdirect_advisors_to_tanks;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider main tank position sanguinar"] =
|
||||||
|
&RaidTempestKeepActionContext::kaelthas_sunstrider_main_tank_position_sanguinar;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider cast fear ward on sanguinar tank"] =
|
||||||
|
&RaidTempestKeepActionContext::kaelthas_sunstrider_cast_fear_ward_on_sanguinar_tank;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider warlock tank position capernian"] =
|
||||||
|
&RaidTempestKeepActionContext::kaelthas_sunstrider_warlock_tank_position_capernian;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider spread and move away from capernian"] =
|
||||||
|
&RaidTempestKeepActionContext::kaelthas_sunstrider_spread_and_move_away_from_capernian;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider first assist tank position telonicus"] =
|
||||||
|
&RaidTempestKeepActionContext::kaelthas_sunstrider_first_assist_tank_position_telonicus;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider handle advisor roles in phase 3"] =
|
||||||
|
&RaidTempestKeepActionContext::kaelthas_sunstrider_handle_advisor_roles_in_phase_3;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider assign advisor dps priority"] =
|
||||||
|
&RaidTempestKeepActionContext::kaelthas_sunstrider_assign_advisor_dps_priority;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider manage advisor dps timer"] =
|
||||||
|
&RaidTempestKeepActionContext::kaelthas_sunstrider_manage_advisor_dps_timer;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider assign legendary weapon dps priority"] =
|
||||||
|
&RaidTempestKeepActionContext::kaelthas_sunstrider_assign_legendary_weapon_dps_priority;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider main tank move devastation away"] =
|
||||||
|
&RaidTempestKeepActionContext::kaelthas_sunstrider_main_tank_move_devastation_away;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider loot legendary weapons"] =
|
||||||
|
&RaidTempestKeepActionContext::kaelthas_sunstrider_loot_legendary_weapons;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider use legendary weapons"] =
|
||||||
|
&RaidTempestKeepActionContext::kaelthas_sunstrider_use_legendary_weapons;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider reequip gear"] =
|
||||||
|
&RaidTempestKeepActionContext::kaelthas_sunstrider_reequip_gear;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider main tank position boss"] =
|
||||||
|
&RaidTempestKeepActionContext::kaelthas_sunstrider_main_tank_position_boss;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider avoid flame strike"] =
|
||||||
|
&RaidTempestKeepActionContext::kaelthas_sunstrider_avoid_flame_strike;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider handle phoenixes and eggs"] =
|
||||||
|
&RaidTempestKeepActionContext::kaelthas_sunstrider_handle_phoenixes_and_eggs;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider break mind control"] =
|
||||||
|
&RaidTempestKeepActionContext::kaelthas_sunstrider_break_mind_control;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider break through shock barrier"] =
|
||||||
|
&RaidTempestKeepActionContext::kaelthas_sunstrider_break_through_shock_barrier;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider spread out in midair"] =
|
||||||
|
&RaidTempestKeepActionContext::kaelthas_sunstrider_spread_out_in_midair;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Trash
|
||||||
|
static Action* crimson_hand_centurion_cast_polymorph(
|
||||||
|
PlayerbotAI* botAI) { return new CrimsonHandCenturionCastPolymorphAction(botAI); }
|
||||||
|
|
||||||
|
// Al'ar <Phoenix God>
|
||||||
|
static Action* alar_misdirect_boss_to_main_tank(
|
||||||
|
PlayerbotAI* botAI) { return new AlarMisdirectBossToMainTankAction(botAI); }
|
||||||
|
|
||||||
|
static Action* alar_boss_tanks_move_between_platforms(
|
||||||
|
PlayerbotAI* botAI) { return new AlarBossTanksMoveBetweenPlatformsAction(botAI); }
|
||||||
|
|
||||||
|
static Action* alar_melee_dps_move_between_platforms(
|
||||||
|
PlayerbotAI* botAI) { return new AlarMeleeDpsMoveBetweenPlatformsAction(botAI); }
|
||||||
|
|
||||||
|
static Action* alar_ranged_and_ember_tank_move_under_platforms(
|
||||||
|
PlayerbotAI* botAI) { return new AlarRangedAndEmberTankMoveUnderPlatformsAction(botAI); }
|
||||||
|
|
||||||
|
static Action* alar_assist_tanks_pick_up_embers(
|
||||||
|
PlayerbotAI* botAI) { return new AlarAssistTanksPickUpEmbersAction(botAI); }
|
||||||
|
|
||||||
|
static Action* alar_ranged_dps_prioritize_embers(
|
||||||
|
PlayerbotAI* botAI) { return new AlarRangedDpsPrioritizeEmbersAction(botAI); }
|
||||||
|
|
||||||
|
static Action* alar_jump_from_platform(
|
||||||
|
PlayerbotAI* botAI) { return new AlarJumpFromPlatformAction(botAI); }
|
||||||
|
|
||||||
|
static Action* alar_move_away_from_rebirth(
|
||||||
|
PlayerbotAI* botAI) { return new AlarMoveAwayFromRebirthAction(botAI); }
|
||||||
|
|
||||||
|
static Action* alar_swap_tanks_on_boss(
|
||||||
|
PlayerbotAI* botAI) { return new AlarSwapTanksOnBossAction(botAI); }
|
||||||
|
|
||||||
|
static Action* alar_avoid_flame_patches_and_dive_bombs(
|
||||||
|
PlayerbotAI* botAI) { return new AlarAvoidFlamePatchesAndDiveBombsAction(botAI); }
|
||||||
|
|
||||||
|
static Action* alar_return_to_room_center(
|
||||||
|
PlayerbotAI* botAI) { return new AlarReturnToRoomCenterAction(botAI); }
|
||||||
|
|
||||||
|
static Action* alar_manage_phase_tracker(
|
||||||
|
PlayerbotAI* botAI) { return new AlarManagePhaseTrackerAction(botAI); }
|
||||||
|
|
||||||
|
// Void Reaver
|
||||||
|
static Action* void_reaver_tanks_position_boss(
|
||||||
|
PlayerbotAI* botAI) { return new VoidReaverTanksPositionBossAction(botAI); }
|
||||||
|
|
||||||
|
static Action* void_reaver_use_aggro_dump_ability(
|
||||||
|
PlayerbotAI* botAI) { return new VoidReaverUseAggroDumpAbilityAction(botAI); }
|
||||||
|
|
||||||
|
static Action* void_reaver_spread_ranged(
|
||||||
|
PlayerbotAI* botAI) { return new VoidReaverSpreadRangedAction(botAI); }
|
||||||
|
|
||||||
|
static Action* void_reaver_avoid_arcane_orb(
|
||||||
|
PlayerbotAI* botAI) { return new VoidReaverAvoidArcaneOrbAction(botAI); }
|
||||||
|
|
||||||
|
static Action* void_reaver_erase_trackers(
|
||||||
|
PlayerbotAI* botAI) { return new VoidReaverEraseTrackersAction(botAI); }
|
||||||
|
|
||||||
|
// High Astromancer Solarian
|
||||||
|
static Action* high_astromancer_solarian_ranged_leave_space_for_melee(
|
||||||
|
PlayerbotAI* botAI) { return new HighAstromancerSolarianRangedLeaveSpaceForMeleeAction(botAI); }
|
||||||
|
|
||||||
|
static Action* high_astromancer_solarian_move_away_from_group(
|
||||||
|
PlayerbotAI* botAI) { return new HighAstromancerSolarianMoveAwayFromGroupAction(botAI); }
|
||||||
|
|
||||||
|
static Action* high_astromancer_solarian_stack_for_aoe(
|
||||||
|
PlayerbotAI* botAI) { return new HighAstromancerSolarianStackForAoeAction(botAI); }
|
||||||
|
|
||||||
|
static Action* high_astromancer_solarian_target_solarium_priests(
|
||||||
|
PlayerbotAI* botAI) { return new HighAstromancerSolarianTargetSolariumPriestsAction(botAI); }
|
||||||
|
|
||||||
|
static Action* high_astromancer_solarian_cast_fear_ward_on_main_tank(
|
||||||
|
PlayerbotAI* botAI) { return new HighAstromancerSolarianCastFearWardOnMainTankAction(botAI); }
|
||||||
|
|
||||||
|
// Kael'thas Sunstrider <Lord of the Blood Elves>
|
||||||
|
static Action* kaelthas_sunstrider_kite_thaladred(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderKiteThaladredAction(botAI); }
|
||||||
|
|
||||||
|
static Action* kaelthas_sunstrider_misdirect_advisors_to_tanks(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderMisdirectAdvisorsToTanksAction(botAI); }
|
||||||
|
|
||||||
|
static Action* kaelthas_sunstrider_main_tank_position_sanguinar(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderMainTankPositionSanguinarAction(botAI); }
|
||||||
|
|
||||||
|
static Action* kaelthas_sunstrider_cast_fear_ward_on_sanguinar_tank(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderCastFearWardOnSanguinarTankAction(botAI); }
|
||||||
|
|
||||||
|
static Action* kaelthas_sunstrider_warlock_tank_position_capernian(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderWarlockTankPositionCapernianAction(botAI); }
|
||||||
|
|
||||||
|
static Action* kaelthas_sunstrider_spread_and_move_away_from_capernian(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderSpreadAndMoveAwayFromCapernianAction(botAI); }
|
||||||
|
|
||||||
|
static Action* kaelthas_sunstrider_first_assist_tank_position_telonicus(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderFirstAssistTankPositionTelonicusAction(botAI); }
|
||||||
|
|
||||||
|
static Action* kaelthas_sunstrider_handle_advisor_roles_in_phase_3(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderHandleAdvisorRolesInPhase3Action(botAI); }
|
||||||
|
|
||||||
|
static Action* kaelthas_sunstrider_assign_advisor_dps_priority(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderAssignAdvisorDpsPriorityAction(botAI); }
|
||||||
|
|
||||||
|
static Action* kaelthas_sunstrider_manage_advisor_dps_timer(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderManageAdvisorDpsTimerAction(botAI); }
|
||||||
|
|
||||||
|
static Action* kaelthas_sunstrider_assign_legendary_weapon_dps_priority(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderAssignLegendaryWeaponDpsPriorityAction(botAI); }
|
||||||
|
|
||||||
|
static Action* kaelthas_sunstrider_main_tank_move_devastation_away(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderMoveDevastationAwayAction(botAI); }
|
||||||
|
|
||||||
|
static Action* kaelthas_sunstrider_loot_legendary_weapons(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderLootLegendaryWeaponsAction(botAI); }
|
||||||
|
|
||||||
|
static Action* kaelthas_sunstrider_use_legendary_weapons(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderUseLegendaryWeaponsAction(botAI); }
|
||||||
|
|
||||||
|
static Action* kaelthas_sunstrider_reequip_gear(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderReequipGearAction(botAI); }
|
||||||
|
|
||||||
|
static Action* kaelthas_sunstrider_main_tank_position_boss(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderMainTankPositionBossAction(botAI); }
|
||||||
|
|
||||||
|
static Action* kaelthas_sunstrider_avoid_flame_strike(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderAvoidFlameStrikeAction(botAI); }
|
||||||
|
|
||||||
|
static Action* kaelthas_sunstrider_handle_phoenixes_and_eggs(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderHandlePhoenixesAndEggsAction(botAI); }
|
||||||
|
|
||||||
|
static Action* kaelthas_sunstrider_break_mind_control(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderBreakMindControlAction(botAI); }
|
||||||
|
|
||||||
|
static Action* kaelthas_sunstrider_break_through_shock_barrier(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderBreakThroughShockBarrierAction(botAI); }
|
||||||
|
|
||||||
|
static Action* kaelthas_sunstrider_spread_out_in_midair(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderSpreadOutInMidairAction(botAI); }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
265
src/Ai/Raid/TempestKeep/RaidTempestKeepTriggerContext.h
Normal file
265
src/Ai/Raid/TempestKeep/RaidTempestKeepTriggerContext.h
Normal file
@@ -0,0 +1,265 @@
|
|||||||
|
#ifndef _PLAYERBOT_RAIDTEMPESTKEEPTRIGGERCONTEXT_H
|
||||||
|
#define _PLAYERBOT_RAIDTEMPESTKEEPTRIGGERCONTEXT_H
|
||||||
|
|
||||||
|
#include "RaidTempestKeepTriggers.h"
|
||||||
|
#include "AiObjectContext.h"
|
||||||
|
|
||||||
|
class RaidTempestKeepTriggerContext : public NamedObjectContext<Trigger>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RaidTempestKeepTriggerContext()
|
||||||
|
{
|
||||||
|
// Trash
|
||||||
|
creators["crimson hand centurion casts arcane volley"] =
|
||||||
|
&RaidTempestKeepTriggerContext::crimson_hand_centurion_casts_arcane_volley;
|
||||||
|
|
||||||
|
// Al'ar <Phoenix God>
|
||||||
|
creators["al'ar pulling boss"] =
|
||||||
|
&RaidTempestKeepTriggerContext::alar_pulling_boss;
|
||||||
|
|
||||||
|
creators["al'ar boss is flying between platforms"] =
|
||||||
|
&RaidTempestKeepTriggerContext::alar_boss_is_flying_between_platforms;
|
||||||
|
|
||||||
|
creators["al'ar embers of al'ar explode upon death"] =
|
||||||
|
&RaidTempestKeepTriggerContext::alar_embers_of_alar_explode_upon_death;
|
||||||
|
|
||||||
|
creators["al'ar killing embers of al'ar damages boss"] =
|
||||||
|
&RaidTempestKeepTriggerContext::alar_killing_embers_of_alar_damages_boss;
|
||||||
|
|
||||||
|
creators["al'ar incoming flame quills"] =
|
||||||
|
&RaidTempestKeepTriggerContext::alar_incoming_flame_quills;
|
||||||
|
|
||||||
|
creators["al'ar rising from the ashes"] =
|
||||||
|
&RaidTempestKeepTriggerContext::alar_rising_from_the_ashes;
|
||||||
|
|
||||||
|
creators["al'ar everything is on fire in phase 2"] =
|
||||||
|
&RaidTempestKeepTriggerContext::alar_everything_is_on_fire_in_phase_2;
|
||||||
|
|
||||||
|
creators["al'ar phase 2 encounter is at room center"] =
|
||||||
|
&RaidTempestKeepTriggerContext::alar_phase_2_encounter_is_at_room_center;
|
||||||
|
|
||||||
|
creators["al'ar strategy changes between phases"] =
|
||||||
|
&RaidTempestKeepTriggerContext::alar_strategy_changes_between_phases;
|
||||||
|
|
||||||
|
// Void Reaver
|
||||||
|
creators["void reaver boss casts pounding"] =
|
||||||
|
&RaidTempestKeepTriggerContext::void_reaver_boss_casts_pounding;
|
||||||
|
|
||||||
|
creators["void reaver knock away reduces tank aggro"] =
|
||||||
|
&RaidTempestKeepTriggerContext::void_reaver_knock_away_reduces_tank_aggro;
|
||||||
|
|
||||||
|
creators["void reaver boss launches arcane orbs"] =
|
||||||
|
&RaidTempestKeepTriggerContext::void_reaver_boss_launches_arcane_orbs;
|
||||||
|
|
||||||
|
creators["void reaver arcane orb is incoming"] =
|
||||||
|
&RaidTempestKeepTriggerContext::void_reaver_arcane_orb_is_incoming;
|
||||||
|
|
||||||
|
creators["void reaver bot is not in combat"] =
|
||||||
|
&RaidTempestKeepTriggerContext::void_reaver_bot_is_not_in_combat;
|
||||||
|
|
||||||
|
// High Astromancer Solarian
|
||||||
|
creators["high astromancer solarian boss casts wrath of the astromancer"] =
|
||||||
|
&RaidTempestKeepTriggerContext::high_astromancer_solarian_boss_casts_wrath_of_the_astromancer;
|
||||||
|
|
||||||
|
creators["high astromancer solarian bot has wrath of the astromancer"] =
|
||||||
|
&RaidTempestKeepTriggerContext::high_astromancer_solarian_bot_has_wrath_of_the_astromancer;
|
||||||
|
|
||||||
|
creators["high astromancer solarian boss has vanished"] =
|
||||||
|
&RaidTempestKeepTriggerContext::high_astromancer_solarian_boss_has_vanished;
|
||||||
|
|
||||||
|
creators["high astromancer solarian solarium priests spawned"] =
|
||||||
|
&RaidTempestKeepTriggerContext::high_astromancer_solarian_solarium_priests_spawned;
|
||||||
|
|
||||||
|
creators["high astromancer solarian boss casts psychic scream"] =
|
||||||
|
&RaidTempestKeepTriggerContext::high_astromancer_solarian_boss_casts_psychic_scream;
|
||||||
|
|
||||||
|
// Kael'thas Sunstrider <Lord of the Blood Elves>
|
||||||
|
creators["kael'thas sunstrider thaladred is fixated on bot"] =
|
||||||
|
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_thaladred_is_fixated_on_bot;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider pulling tankable advisors"] =
|
||||||
|
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_pulling_tankable_advisors;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider sanguinar engaged by main tank"] =
|
||||||
|
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_sanguinar_engaged_by_main_tank;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider sanguinar casts bellowing roar"] =
|
||||||
|
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_sanguinar_casts_bellowing_roar;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider capernian should be tanked by a warlock"] =
|
||||||
|
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_capernian_should_be_tanked_by_a_warlock;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider capernian casts arcane burst and conflagration"] =
|
||||||
|
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_capernian_casts_arcane_burst_and_conflagration;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider telonicus engaged by first assist tank"] =
|
||||||
|
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_telonicus_engaged_by_first_assist_tank;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider bots have specific roles in phase 3"] =
|
||||||
|
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_bots_have_specific_roles_in_phase_3;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider determining advisor kill order"] =
|
||||||
|
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_determining_advisor_kill_order;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider waiting for tanks to get aggro on advisors"] =
|
||||||
|
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_waiting_for_tanks_to_get_aggro_on_advisors;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider legendary weapons are alive"] =
|
||||||
|
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_legendary_weapons_are_alive;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider legendary axe casts whirlwind"] =
|
||||||
|
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_legendary_axe_casts_whirlwind;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider legendary weapons are dead and lootable"] =
|
||||||
|
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_legendary_weapons_are_dead_and_lootable;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider legendary weapons are equipped"] =
|
||||||
|
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_legendary_weapons_are_equipped;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider legendary weapons were lost"] =
|
||||||
|
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_legendary_weapons_were_lost;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider boss has entered the fight"] =
|
||||||
|
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_boss_has_entered_the_fight;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider raid member is mind controlled"] =
|
||||||
|
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_raid_member_is_mind_controlled;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider phoenixes and eggs are spawning"] =
|
||||||
|
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_phoenixes_and_eggs_are_spawning;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider boss is casting pyroblast"] =
|
||||||
|
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_boss_is_casting_pyroblast;
|
||||||
|
|
||||||
|
creators["kael'thas sunstrider boss is manipulating gravity"] =
|
||||||
|
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_boss_is_manipulating_gravity;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Trash
|
||||||
|
static Trigger* crimson_hand_centurion_casts_arcane_volley(
|
||||||
|
PlayerbotAI* botAI) { return new CrimsonHandCenturionCastsArcaneVolleyTrigger(botAI); }
|
||||||
|
|
||||||
|
// Al'ar <Phoenix God>
|
||||||
|
static Trigger* alar_pulling_boss(
|
||||||
|
PlayerbotAI* botAI) { return new AlarPullingBossTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* alar_boss_is_flying_between_platforms(
|
||||||
|
PlayerbotAI* botAI) { return new AlarBossIsFlyingBetweenPlatformsTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* alar_embers_of_alar_explode_upon_death(
|
||||||
|
PlayerbotAI* botAI) { return new AlarEmbersOfAlarExplodeUponDeathTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* alar_killing_embers_of_alar_damages_boss(
|
||||||
|
PlayerbotAI* botAI) { return new AlarKillingEmbersOfAlarDamagesBossTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* alar_incoming_flame_quills(
|
||||||
|
PlayerbotAI* botAI) { return new AlarIncomingFlameQuillsTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* alar_rising_from_the_ashes(
|
||||||
|
PlayerbotAI* botAI) { return new AlarRisingFromTheAshesTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* alar_everything_is_on_fire_in_phase_2(
|
||||||
|
PlayerbotAI* botAI) { return new AlarEverythingIsOnFireInPhase2Trigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* alar_phase_2_encounter_is_at_room_center(
|
||||||
|
PlayerbotAI* botAI) { return new AlarPhase2EncounterIsAtRoomCenterTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* alar_strategy_changes_between_phases(
|
||||||
|
PlayerbotAI* botAI) { return new AlarStrategyChangesBetweenPhasesTrigger(botAI); }
|
||||||
|
|
||||||
|
// Void Reaver
|
||||||
|
static Trigger* void_reaver_boss_casts_pounding(
|
||||||
|
PlayerbotAI* botAI) { return new VoidReaverBossCastsPoundingTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* void_reaver_knock_away_reduces_tank_aggro(
|
||||||
|
PlayerbotAI* botAI) { return new VoidReaverKnockAwayReducesTankAggroTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* void_reaver_boss_launches_arcane_orbs(
|
||||||
|
PlayerbotAI* botAI) { return new VoidReaverBossLaunchesArcaneOrbsTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* void_reaver_arcane_orb_is_incoming(
|
||||||
|
PlayerbotAI* botAI) { return new VoidReaverArcaneOrbIsIncomingTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* void_reaver_bot_is_not_in_combat(
|
||||||
|
PlayerbotAI* botAI) { return new VoidReaverBotIsNotInCombatTrigger(botAI); }
|
||||||
|
|
||||||
|
// High Astromancer Solarian
|
||||||
|
static Trigger* high_astromancer_solarian_boss_casts_wrath_of_the_astromancer(
|
||||||
|
PlayerbotAI* botAI) { return new HighAstromancerSolarianBossCastsWrathOfTheAstromancerTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* high_astromancer_solarian_bot_has_wrath_of_the_astromancer(
|
||||||
|
PlayerbotAI* botAI) { return new HighAstromancerSolarianBotHasWrathOfTheAstromancerTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* high_astromancer_solarian_boss_has_vanished(
|
||||||
|
PlayerbotAI* botAI) { return new HighAstromancerSolarianBossHasVanishedTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* high_astromancer_solarian_solarium_priests_spawned(
|
||||||
|
PlayerbotAI* botAI) { return new HighAstromancerSolarianSolariumPriestsSpawnedTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* high_astromancer_solarian_boss_casts_psychic_scream(
|
||||||
|
PlayerbotAI* botAI) { return new HighAstromancerSolarianBossCastsPsychicScreamTrigger(botAI); }
|
||||||
|
|
||||||
|
// Kael'thas Sunstrider <Lord of the Blood Elves>
|
||||||
|
static Trigger* kaelthas_sunstrider_thaladred_is_fixated_on_bot(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderThaladredIsFixatedOnBotTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* kaelthas_sunstrider_pulling_tankable_advisors(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderPullingTankableAdvisorsTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* kaelthas_sunstrider_sanguinar_engaged_by_main_tank(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderSanguinarEngagedByMainTankTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* kaelthas_sunstrider_sanguinar_casts_bellowing_roar(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderSanguinarCastsBellowingRoarTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* kaelthas_sunstrider_capernian_should_be_tanked_by_a_warlock(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderCapernianShouldBeTankedByAWarlockTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* kaelthas_sunstrider_capernian_casts_arcane_burst_and_conflagration(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderCapernianCastsArcaneBurstAndConflagrationTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* kaelthas_sunstrider_telonicus_engaged_by_first_assist_tank(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderTelonicusEngagedByFirstAssistTankTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* kaelthas_sunstrider_bots_have_specific_roles_in_phase_3(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderBotsHaveSpecificRolesInPhase3Trigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* kaelthas_sunstrider_determining_advisor_kill_order(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderDeterminingAdvisorKillOrderTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* kaelthas_sunstrider_waiting_for_tanks_to_get_aggro_on_advisors(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderWaitingForTanksToGetAggroOnAdvisorsTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* kaelthas_sunstrider_legendary_weapons_are_alive(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderLegendaryWeaponsAreAliveTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* kaelthas_sunstrider_legendary_axe_casts_whirlwind(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderLegendaryAxeCastsWhirlwindTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* kaelthas_sunstrider_legendary_weapons_are_dead_and_lootable(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderLegendaryWeaponsAreDeadAndLootableTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* kaelthas_sunstrider_legendary_weapons_are_equipped(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderLegendaryWeaponsAreEquippedTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* kaelthas_sunstrider_legendary_weapons_were_lost(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderLegendaryWeaponsWereLostTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* kaelthas_sunstrider_boss_has_entered_the_fight(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderBossHasEnteredTheFightTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* kaelthas_sunstrider_raid_member_is_mind_controlled(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderRaidMemberIsMindControlledTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* kaelthas_sunstrider_phoenixes_and_eggs_are_spawning(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderPhoenixesAndEggsAreSpawningTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* kaelthas_sunstrider_boss_is_casting_pyroblast(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderBossIsCastingPyroblastTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* kaelthas_sunstrider_boss_is_manipulating_gravity(
|
||||||
|
PlayerbotAI* botAI) { return new KaelthasSunstriderBossIsManipulatingGravityTrigger(botAI); }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
162
src/Ai/Raid/TempestKeep/Strategy/RaidTempestKeepStrategy.cpp
Normal file
162
src/Ai/Raid/TempestKeep/Strategy/RaidTempestKeepStrategy.cpp
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
#include "RaidTempestKeepStrategy.h"
|
||||||
|
#include "RaidTempestKeepMultipliers.h"
|
||||||
|
|
||||||
|
void RaidTempestKeepStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
|
{
|
||||||
|
// Trash
|
||||||
|
triggers.push_back(new TriggerNode("crimson hand centurion casts arcane volley", {
|
||||||
|
NextAction("crimson hand centurion cast polymorph", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
// Al'ar <Phoenix God>
|
||||||
|
triggers.push_back(new TriggerNode("al'ar pulling boss", {
|
||||||
|
NextAction("al'ar misdirect boss to main tank", ACTION_EMERGENCY + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("al'ar boss is flying between platforms", {
|
||||||
|
NextAction("al'ar boss tanks move between platforms", ACTION_RAID + 1),
|
||||||
|
NextAction("al'ar melee dps move between platforms", ACTION_RAID + 1),
|
||||||
|
NextAction("al'ar ranged and ember tank move under platforms", ACTION_RAID + 4) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("al'ar embers of al'ar explode upon death", {
|
||||||
|
NextAction("al'ar assist tanks pick up embers", ACTION_RAID + 3) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("al'ar killing embers of al'ar damages boss", {
|
||||||
|
NextAction("al'ar ranged dps prioritize embers", ACTION_RAID + 2) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("al'ar incoming flame quills", {
|
||||||
|
NextAction("al'ar jump from platform", ACTION_EMERGENCY + 7) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("al'ar rising from the ashes", {
|
||||||
|
NextAction("al'ar move away from rebirth", ACTION_EMERGENCY + 7) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("al'ar everything is on fire in phase 2", {
|
||||||
|
NextAction("al'ar swap tanks on boss", ACTION_EMERGENCY + 2),
|
||||||
|
NextAction("al'ar avoid flame patches and dive bombs", ACTION_EMERGENCY + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("al'ar phase 2 encounter is at room center", {
|
||||||
|
NextAction("al'ar return to room center", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("al'ar strategy changes between phases", {
|
||||||
|
NextAction("al'ar manage phase tracker", ACTION_EMERGENCY + 10) }));
|
||||||
|
|
||||||
|
// Void Reaver
|
||||||
|
triggers.push_back(new TriggerNode("void reaver boss casts pounding", {
|
||||||
|
NextAction("void reaver tanks position boss", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("void reaver knock away reduces tank aggro", {
|
||||||
|
NextAction("void reaver use aggro dump ability", ACTION_EMERGENCY + 6) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("void reaver boss launches arcane orbs", {
|
||||||
|
NextAction("void reaver spread ranged", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("void reaver arcane orb is incoming", {
|
||||||
|
NextAction("void reaver avoid arcane orb", ACTION_EMERGENCY + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("void reaver bot is not in combat", {
|
||||||
|
NextAction("void reaver erase trackers", ACTION_EMERGENCY + 11) }));
|
||||||
|
|
||||||
|
// High Astromancer Solarian
|
||||||
|
triggers.push_back(new TriggerNode("high astromancer solarian boss casts wrath of the astromancer", {
|
||||||
|
NextAction("high astromancer solarian ranged leave space for melee", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("high astromancer solarian bot has wrath of the astromancer", {
|
||||||
|
NextAction("high astromancer solarian move away from group", ACTION_EMERGENCY + 6) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("high astromancer solarian boss has vanished", {
|
||||||
|
NextAction("high astromancer solarian stack for aoe", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("high astromancer solarian solarium priests spawned", {
|
||||||
|
NextAction("high astromancer solarian target solarium priests", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("high astromancer solarian boss casts psychic scream", {
|
||||||
|
NextAction("high astromancer solarian cast fear ward on main tank", ACTION_RAID + 2) }));
|
||||||
|
|
||||||
|
// Kael'thas Sunstrider <Lord of the Blood Elves>
|
||||||
|
triggers.push_back(new TriggerNode("kael'thas sunstrider thaladred is fixated on bot", {
|
||||||
|
NextAction("kael'thas sunstrider kite thaladred", ACTION_EMERGENCY + 6) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("kael'thas sunstrider pulling tankable advisors", {
|
||||||
|
NextAction("kael'thas sunstrider misdirect advisors to tanks", ACTION_EMERGENCY + 2) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("kael'thas sunstrider sanguinar engaged by main tank", {
|
||||||
|
NextAction("kael'thas sunstrider main tank position sanguinar", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("kael'thas sunstrider sanguinar casts bellowing roar", {
|
||||||
|
NextAction("kael'thas sunstrider cast fear ward on sanguinar tank", ACTION_RAID + 2) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("kael'thas sunstrider capernian should be tanked by a warlock", {
|
||||||
|
NextAction("kael'thas sunstrider warlock tank position capernian", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("kael'thas sunstrider capernian casts arcane burst and conflagration", {
|
||||||
|
NextAction("kael'thas sunstrider spread and move away from capernian", ACTION_RAID + 3) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("kael'thas sunstrider telonicus engaged by first assist tank", {
|
||||||
|
NextAction("kael'thas sunstrider first assist tank position telonicus", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("kael'thas sunstrider bots have specific roles in phase 3", {
|
||||||
|
NextAction("kael'thas sunstrider handle advisor roles in phase 3", ACTION_RAID + 2) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("kael'thas sunstrider determining advisor kill order", {
|
||||||
|
NextAction("kael'thas sunstrider assign advisor dps priority", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("kael'thas sunstrider waiting for tanks to get aggro on advisors", {
|
||||||
|
NextAction("kael'thas sunstrider manage advisor dps timer", ACTION_EMERGENCY + 10) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("kael'thas sunstrider legendary weapons are alive", {
|
||||||
|
NextAction("kael'thas sunstrider assign legendary weapon dps priority", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("kael'thas sunstrider legendary axe casts whirlwind", {
|
||||||
|
NextAction("kael'thas sunstrider main tank move devastation away", ACTION_EMERGENCY + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("kael'thas sunstrider legendary weapons are dead and lootable", {
|
||||||
|
NextAction("kael'thas sunstrider loot legendary weapons", ACTION_RAID) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("kael'thas sunstrider legendary weapons are equipped", {
|
||||||
|
NextAction("kael'thas sunstrider use legendary weapons", ACTION_EMERGENCY + 6) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("kael'thas sunstrider legendary weapons were lost", {
|
||||||
|
NextAction("kael'thas sunstrider reequip gear", ACTION_EMERGENCY + 11) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("kael'thas sunstrider boss has entered the fight", {
|
||||||
|
NextAction("kael'thas sunstrider main tank position boss", ACTION_RAID + 1),
|
||||||
|
NextAction("kael'thas sunstrider avoid flame strike", ACTION_EMERGENCY + 8) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("kael'thas sunstrider phoenixes and eggs are spawning", {
|
||||||
|
NextAction("kael'thas sunstrider handle phoenixes and eggs", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("kael'thas sunstrider raid member is mind controlled", {
|
||||||
|
NextAction("kael'thas sunstrider break mind control", ACTION_EMERGENCY + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("kael'thas sunstrider boss is casting pyroblast", {
|
||||||
|
NextAction("kael'thas sunstrider break through shock barrier", ACTION_EMERGENCY + 7) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("kael'thas sunstrider boss is manipulating gravity", {
|
||||||
|
NextAction("kael'thas sunstrider spread out in midair", ACTION_EMERGENCY + 1) }));
|
||||||
|
}
|
||||||
|
|
||||||
|
void RaidTempestKeepStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||||
|
{
|
||||||
|
// Alar <Phoenix God>
|
||||||
|
multipliers.push_back(new AlarMoveBetweenPlatformsMultiplier(botAI));
|
||||||
|
multipliers.push_back(new AlarDisableDisperseMultiplier(botAI));
|
||||||
|
multipliers.push_back(new AlarDisableTankAssistMultiplier(botAI));
|
||||||
|
multipliers.push_back(new AlarStayAwayFromRebirthMultiplier(botAI));
|
||||||
|
multipliers.push_back(new AlarPhase2NoTankingIfArmorMeltedMultiplier(botAI));
|
||||||
|
|
||||||
|
// Void Reaver
|
||||||
|
multipliers.push_back(new VoidReaverMaintainPositionsMultiplier(botAI));
|
||||||
|
|
||||||
|
// High Astromancer Solarian
|
||||||
|
multipliers.push_back(new HighAstromancerSolarianDisableTankAssistMultiplier(botAI));
|
||||||
|
multipliers.push_back(new HighAstromancerSolarianMaintainPositionMultiplier(botAI));
|
||||||
|
|
||||||
|
// Kael'thas Sunstrider <Lord of the Blood Elves>
|
||||||
|
multipliers.push_back(new KaelthasSunstriderWaitForDpsMultiplier(botAI));
|
||||||
|
multipliers.push_back(new KaelthasSunstriderKiteThaladredMultiplier(botAI));
|
||||||
|
multipliers.push_back(new KaelthasSunstriderControlMisdirectionMultiplier(botAI));
|
||||||
|
multipliers.push_back(new KaelthasSunstriderKeepDistanceFromCapernianMultiplier(botAI));
|
||||||
|
multipliers.push_back(new KaelthasSunstriderManageWeaponTankingMultiplier(botAI));
|
||||||
|
multipliers.push_back(new KaelthasSunstriderDisableAdvisorTankAssistMultiplier(botAI));
|
||||||
|
multipliers.push_back(new KaelthasSunstriderDisableDisperseMultiplier(botAI));
|
||||||
|
multipliers.push_back(new KaelthasSunstriderDelayCooldownsMultiplier(botAI));
|
||||||
|
multipliers.push_back(new KaelthasSunstriderStaySpreadDuringGravityLapseMultiplier(botAI));
|
||||||
|
}
|
||||||
18
src/Ai/Raid/TempestKeep/Strategy/RaidTempestKeepStrategy.h
Normal file
18
src/Ai/Raid/TempestKeep/Strategy/RaidTempestKeepStrategy.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#ifndef _PLAYERBOT_RAIDTEMPESTKEEPSTRATEGY_H_
|
||||||
|
#define _PLAYERBOT_RAIDTEMPESTKEEPSTRATEGY_H_
|
||||||
|
|
||||||
|
#include "Strategy.h"
|
||||||
|
#include "Multiplier.h"
|
||||||
|
|
||||||
|
class RaidTempestKeepStrategy : public Strategy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RaidTempestKeepStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
|
||||||
|
|
||||||
|
std::string const getName() override { return "tempestkeep"; }
|
||||||
|
|
||||||
|
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||||
|
void InitMultipliers(std::vector<Multiplier*>& multipliers) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
527
src/Ai/Raid/TempestKeep/Trigger/RaidTempestKeepTriggers.cpp
Normal file
527
src/Ai/Raid/TempestKeep/Trigger/RaidTempestKeepTriggers.cpp
Normal file
@@ -0,0 +1,527 @@
|
|||||||
|
#include "RaidTempestKeepTriggers.h"
|
||||||
|
#include "RaidTempestKeepHelpers.h"
|
||||||
|
#include "RaidTempestKeepActions.h"
|
||||||
|
#include "RaidTempestKeepKaelthasBossAI.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
#include "RaidBossHelpers.h"
|
||||||
|
|
||||||
|
using namespace TempestKeepHelpers;
|
||||||
|
|
||||||
|
// Trash
|
||||||
|
|
||||||
|
bool CrimsonHandCenturionCastsArcaneVolleyTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return bot->getClass() == CLASS_MAGE &&
|
||||||
|
AI_VALUE2(Unit*, "find target", "crimson hand centurion");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Al'ar <Phoenix God>
|
||||||
|
|
||||||
|
bool AlarPullingBossTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (bot->getClass() != CLASS_HUNTER)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* alar = AI_VALUE2(Unit*, "find target", "al'ar");
|
||||||
|
return alar && alar->GetHealthPct() > 98.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AlarBossIsFlyingBetweenPlatformsTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* alar = AI_VALUE2(Unit*, "find target", "al'ar");
|
||||||
|
if (!alar || !alar->IsVisible())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (isAlarInPhase2[alar->GetMap()->GetInstanceId()])
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int8 locationIndex = GetAlarCurrentLocationIndex(alar);
|
||||||
|
if (locationIndex == LOCATION_NONE)
|
||||||
|
{
|
||||||
|
Position dest;
|
||||||
|
locationIndex = GetAlarDestinationLocationIndex(alar, dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
return locationIndex != POINT_QUILL_OR_DIVE_IDX &&
|
||||||
|
locationIndex != POINT_MIDDLE_IDX;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AlarEmbersOfAlarExplodeUponDeathTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return botAI->IsTank(bot) && AI_VALUE2(Unit*, "find target", "ember of al'ar");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AlarKillingEmbersOfAlarDamagesBossTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return botAI->IsRangedDps(bot) &&
|
||||||
|
AI_VALUE2(Unit*, "find target", "ember of al'ar");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AlarIncomingFlameQuillsTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* alar = AI_VALUE2(Unit*, "find target", "al'ar");
|
||||||
|
if (!alar || isAlarInPhase2[alar->GetMap()->GetInstanceId()])
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Position dest;
|
||||||
|
return GetAlarCurrentLocationIndex(alar) == POINT_QUILL_OR_DIVE_IDX ||
|
||||||
|
GetAlarDestinationLocationIndex(alar, dest) == POINT_QUILL_OR_DIVE_IDX;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AlarRisingFromTheAshesTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* alar = AI_VALUE2(Unit*, "find target", "al'ar");
|
||||||
|
if (!alar)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (isAlarInPhase2[alar->GetMap()->GetInstanceId()])
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Creature* alarCreature = alar->ToCreature();
|
||||||
|
return alarCreature && alarCreature->GetReactState() == REACT_PASSIVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AlarEverythingIsOnFireInPhase2Trigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* alar = AI_VALUE2(Unit*, "find target", "al'ar");
|
||||||
|
return alar && isAlarInPhase2[alar->GetMap()->GetInstanceId()];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AlarPhase2EncounterIsAtRoomCenterTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (bot->GetVictim())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* alar = AI_VALUE2(Unit*, "find target", "al'ar");
|
||||||
|
if (!alar || !isAlarInPhase2[alar->GetMap()->GetInstanceId()])
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Creature* alarCreature = alar->ToCreature();
|
||||||
|
if (alarCreature && alarCreature->GetReactState() == REACT_PASSIVE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Position dest;
|
||||||
|
return GetAlarCurrentLocationIndex(alar) != POINT_QUILL_OR_DIVE_IDX &&
|
||||||
|
GetAlarDestinationLocationIndex(alar, dest) != POINT_QUILL_OR_DIVE_IDX;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AlarStrategyChangesBetweenPhasesTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return botAI->IsDps(bot) && IsMechanicTrackerBot(botAI, bot, TEMPEST_KEEP_MAP_ID) &&
|
||||||
|
AI_VALUE2(Unit*, "find target", "al'ar");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Void Reaver
|
||||||
|
|
||||||
|
bool VoidReaverBossCastsPoundingTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!botAI->IsTank(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* voidReaver = AI_VALUE2(Unit*, "find target", "void reaver");
|
||||||
|
return voidReaver && voidReaver->GetVictim() == bot;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VoidReaverKnockAwayReducesTankAggroTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (botAI->IsTank(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (bot->getClass() == CLASS_DEATH_KNIGHT ||
|
||||||
|
bot->getClass() == CLASS_DRUID ||
|
||||||
|
bot->getClass() == CLASS_SHAMAN ||
|
||||||
|
bot->getClass() == CLASS_WARRIOR)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* voidReaver = AI_VALUE2(Unit*, "find target", "void reaver");
|
||||||
|
return voidReaver && voidReaver->GetVictim() == bot;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VoidReaverBossLaunchesArcaneOrbsTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!botAI->IsRanged(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* voidReaver = AI_VALUE2(Unit*, "find target", "void reaver");
|
||||||
|
return voidReaver && voidReaver->GetVictim() != bot;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VoidReaverArcaneOrbIsIncomingTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (botAI->IsTank(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* voidReaver = AI_VALUE2(Unit*, "find target", "void reaver");
|
||||||
|
if (!voidReaver || voidReaver->GetVictim() == bot)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto it = voidReaverArcaneOrbs.find(bot->GetMap()->GetInstanceId());
|
||||||
|
if (it == voidReaverArcaneOrbs.end() || it->second.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uint32 currentTime = getMSTime();
|
||||||
|
constexpr uint32 orbDuration = 7000;
|
||||||
|
constexpr float safeDistance = 22.0f;
|
||||||
|
|
||||||
|
for (auto const& orb : it->second)
|
||||||
|
{
|
||||||
|
if (getMSTimeDiff(orb.castTime, currentTime) <= orbDuration &&
|
||||||
|
bot->GetExactDist2d(orb.destination.GetPositionX(),
|
||||||
|
orb.destination.GetPositionY()) < safeDistance)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VoidReaverBotIsNotInCombatTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return !bot->IsInCombat();
|
||||||
|
}
|
||||||
|
|
||||||
|
// High Astromancer Solarian
|
||||||
|
|
||||||
|
bool HighAstromancerSolarianBossCastsWrathOfTheAstromancerTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (bot->HasAura(SPELL_WRATH_OF_THE_ASTROMANCER))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!botAI->IsRanged(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* astromancer = AI_VALUE2(Unit*, "find target", "high astromancer solarian");
|
||||||
|
return astromancer && !astromancer->HasAura(SPELL_SOLARIAN_TRANSFORM);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HighAstromancerSolarianBotHasWrathOfTheAstromancerTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return bot->HasAura(SPELL_WRATH_OF_THE_ASTROMANCER);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HighAstromancerSolarianBossHasVanishedTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (bot->HasAura(SPELL_WRATH_OF_THE_ASTROMANCER))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* astromancer = AI_VALUE2(Unit*, "find target", "high astromancer solarian");
|
||||||
|
if (!astromancer)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Creature* astromancerCreature = astromancer->ToCreature();
|
||||||
|
return astromancerCreature &&
|
||||||
|
astromancerCreature->GetReactState() == REACT_PASSIVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HighAstromancerSolarianSolariumPriestsSpawnedTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return botAI->IsMelee(bot) && AI_VALUE2(Unit*, "find target", "solarium priest");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HighAstromancerSolarianBossCastsPsychicScreamTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (bot->getClass() != CLASS_PRIEST)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* astromancer = AI_VALUE2(Unit*, "find target", "high astromancer solarian");
|
||||||
|
return astromancer && astromancer->HasAura(SPELL_SOLARIAN_TRANSFORM);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kael'thas Sunstrider <Lord of the Blood Elves>
|
||||||
|
|
||||||
|
bool KaelthasSunstriderThaladredIsFixatedOnBotTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* kaelthas = AI_VALUE2(Unit*, "find target", "kael'thas sunstrider");
|
||||||
|
if (!kaelthas)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* thaladred = AI_VALUE2(Unit*, "find target", "thaladred the darkener");
|
||||||
|
if (!thaladred || thaladred->GetVictim() != bot)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
boss_kaelthas* kaelAI = dynamic_cast<boss_kaelthas*>(kaelthas->GetAI());
|
||||||
|
if (!kaelAI)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return !(botAI->IsTank(bot) && kaelAI->GetPhase() == PHASE_ALL_ADVISORS);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KaelthasSunstriderPullingTankableAdvisorsTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (bot->getClass() != CLASS_HUNTER)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* kaelthas = AI_VALUE2(Unit*, "find target", "kael'thas sunstrider");
|
||||||
|
if (!kaelthas)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
boss_kaelthas* kaelAI = dynamic_cast<boss_kaelthas*>(kaelthas->GetAI());
|
||||||
|
return kaelAI && (kaelAI->GetPhase() == PHASE_SINGLE_ADVISOR ||
|
||||||
|
kaelAI->GetPhase() == PHASE_ALL_ADVISORS);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KaelthasSunstriderSanguinarEngagedByMainTankTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!botAI->IsMainTank(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* sanguinar = AI_VALUE2(Unit*, "find target", "lord sanguinar");
|
||||||
|
return sanguinar && !sanguinar->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE) &&
|
||||||
|
!sanguinar->HasAura(SPELL_PERMANENT_FEIGN_DEATH);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KaelthasSunstriderSanguinarCastsBellowingRoarTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (bot->getClass() != CLASS_PRIEST)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* kaelthas = AI_VALUE2(Unit*, "find target", "kael'thas sunstrider");
|
||||||
|
if (!kaelthas)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
boss_kaelthas* kaelAI = dynamic_cast<boss_kaelthas*>(kaelthas->GetAI());
|
||||||
|
if (!kaelAI)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (kaelAI->GetPhase() != PHASE_SINGLE_ADVISOR &&
|
||||||
|
kaelAI->GetPhase() != PHASE_TRANSITION &&
|
||||||
|
kaelAI->GetPhase() != PHASE_ALL_ADVISORS)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Player* mainTank = GetGroupMainTank(botAI, bot);
|
||||||
|
if (!mainTank || mainTank->HasAura(SPELL_FEAR_WARD))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return botAI->CanCastSpell("fear ward", mainTank);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KaelthasSunstriderCapernianShouldBeTankedByAWarlockTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (bot->getClass() != CLASS_WARLOCK)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* capernian = AI_VALUE2(Unit*, "find target", "grand astromancer capernian");
|
||||||
|
if (!capernian || capernian->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE) ||
|
||||||
|
capernian->HasAura(SPELL_PERMANENT_FEIGN_DEATH))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return GetCapernianTank(bot) == bot;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KaelthasSunstriderCapernianCastsArcaneBurstAndConflagrationTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* capernian = AI_VALUE2(Unit*, "find target", "grand astromancer capernian");
|
||||||
|
if (!capernian || capernian->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE) ||
|
||||||
|
capernian->HasAura(SPELL_PERMANENT_FEIGN_DEATH))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return GetCapernianTank(bot) != bot;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KaelthasSunstriderTelonicusEngagedByFirstAssistTankTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!botAI->IsAssistTankOfIndex(bot, 0, false))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* telonicus = AI_VALUE2(Unit*, "find target", "master engineer telonicus");
|
||||||
|
return telonicus && !telonicus->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE) &&
|
||||||
|
!telonicus->HasAura(SPELL_PERMANENT_FEIGN_DEATH);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KaelthasSunstriderBotsHaveSpecificRolesInPhase3Trigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* kaelthas = AI_VALUE2(Unit*, "find target", "kael'thas sunstrider");
|
||||||
|
if (!kaelthas)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!AI_VALUE2(Unit*, "find target", "master engineer telonicus") &&
|
||||||
|
!AI_VALUE2(Unit*, "find target", "lord sanguinar"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
boss_kaelthas* kaelAI = dynamic_cast<boss_kaelthas*>(kaelthas->GetAI());
|
||||||
|
if (!kaelAI || kaelAI->GetPhase() != PHASE_ALL_ADVISORS)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return botAI->IsAssistHealOfIndex(bot, 0, true) ||
|
||||||
|
botAI->IsMainTank(bot) || botAI->IsAssistTankOfIndex(bot, 0, true) ||
|
||||||
|
(bot->getClass() == CLASS_WARLOCK && GetCapernianTank(bot) == bot);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KaelthasSunstriderDeterminingAdvisorKillOrderTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (botAI->IsHeal(bot) ||
|
||||||
|
botAI->IsMainTank(bot) ||
|
||||||
|
botAI->IsAssistTankOfIndex(bot, 0, true))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* kaelthas = AI_VALUE2(Unit*, "find target", "kael'thas sunstrider");
|
||||||
|
if (!kaelthas)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
boss_kaelthas* kaelAI = dynamic_cast<boss_kaelthas*>(kaelthas->GetAI());
|
||||||
|
return kaelAI && (kaelAI->GetPhase() == PHASE_SINGLE_ADVISOR ||
|
||||||
|
kaelAI->GetPhase() == PHASE_ALL_ADVISORS);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KaelthasSunstriderWaitingForTanksToGetAggroOnAdvisorsTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!botAI->IsDps(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* kaelthas = AI_VALUE2(Unit*, "find target", "kael'thas sunstrider");
|
||||||
|
if (!kaelthas)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
boss_kaelthas* kaelAI = dynamic_cast<boss_kaelthas*>(kaelthas->GetAI());
|
||||||
|
if (!kaelAI || kaelAI->GetPhase() != PHASE_SINGLE_ADVISOR)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return IsMechanicTrackerBot(botAI, bot, TEMPEST_KEEP_MAP_ID, GetCapernianTank(bot));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KaelthasSunstriderLegendaryWeaponsAreAliveTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (botAI->IsMainTank(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* kaelthas = AI_VALUE2(Unit*, "find target", "kael'thas sunstrider");
|
||||||
|
if (!kaelthas)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
boss_kaelthas* kaelAI = dynamic_cast<boss_kaelthas*>(kaelthas->GetAI());
|
||||||
|
return kaelAI && kaelAI->GetPhase() == PHASE_WEAPONS;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KaelthasSunstriderLegendaryAxeCastsWhirlwindTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return botAI->IsMainTank(bot) &&
|
||||||
|
AI_VALUE2(Unit*, "find target", "devastation");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KaelthasSunstriderLegendaryWeaponsAreDeadAndLootableTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* kaelthas = AI_VALUE2(Unit*, "find target", "kael'thas sunstrider");
|
||||||
|
if (!kaelthas)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
boss_kaelthas* kaelAI = dynamic_cast<boss_kaelthas*>(kaelthas->GetAI());
|
||||||
|
if (!kaelAI ||
|
||||||
|
(kaelAI->GetPhase() != PHASE_WEAPONS && kaelAI->GetPhase() != PHASE_ALL_ADVISORS))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* axe = AI_VALUE2(Unit*, "find target", "devastation");
|
||||||
|
if (axe && axe->GetVictim() == bot)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return IsAnyLegendaryWeaponDead(bot);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KaelthasSunstriderLegendaryWeaponsAreEquippedTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!AI_VALUE2(Unit*, "find target", "kael'thas sunstrider"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return bot->HasItemCount(ITEM_STAFF_OF_DISINTEGRATION, 1, false) ||
|
||||||
|
bot->HasItemCount(ITEM_NETHERSTRAND_LONGBOW, 1, false) ||
|
||||||
|
bot->HasItemCount(ITEM_PHASESHIFT_BULWARK, 1, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KaelthasSunstriderLegendaryWeaponsWereLostTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (bot->GetMapId() != TEMPEST_KEEP_MAP_ID)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Map* map = bot->GetMap();
|
||||||
|
if (!map)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
constexpr uint32 KAELTHAS_DB_GUID = 158218;
|
||||||
|
auto const& creatureStore = map->GetCreatureBySpawnIdStore();
|
||||||
|
auto it = creatureStore.find(KAELTHAS_DB_GUID);
|
||||||
|
if (it == creatureStore.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Creature* kaelthas = it->second;
|
||||||
|
if (!kaelthas || bot->GetExactDist2d(kaelthas) > 150.0f)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const std::array<uint8, 3> weaponSlots =
|
||||||
|
{
|
||||||
|
EQUIPMENT_SLOT_MAINHAND,
|
||||||
|
EQUIPMENT_SLOT_OFFHAND,
|
||||||
|
EQUIPMENT_SLOT_RANGED
|
||||||
|
};
|
||||||
|
|
||||||
|
for (uint8 slot : weaponSlots)
|
||||||
|
{
|
||||||
|
if (!bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot) &&
|
||||||
|
HasEquippableItemForSlot(bot, slot))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KaelthasSunstriderBossHasEnteredTheFightTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* kaelthas = AI_VALUE2(Unit*, "find target", "kael'thas sunstrider");
|
||||||
|
if (!kaelthas)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
boss_kaelthas* kaelAI = dynamic_cast<boss_kaelthas*>(kaelthas->GetAI());
|
||||||
|
return kaelAI && kaelAI->GetPhase() == PHASE_FINAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KaelthasSunstriderPhoenixesAndEggsAreSpawningTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* kaelthas = AI_VALUE2(Unit*, "find target", "kael'thas sunstrider");
|
||||||
|
if (!kaelthas)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (botAI->IsTank(bot) && kaelthas->GetVictim() == bot)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return AI_VALUE2(Unit*, "find target", "phoenix") ||
|
||||||
|
AI_VALUE2(Unit*, "find target", "phoenix egg");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KaelthasSunstriderRaidMemberIsMindControlledTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* kaelthas = AI_VALUE2(Unit*, "find target", "kael'thas sunstrider");
|
||||||
|
if (!kaelthas)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (botAI->IsTank(bot) && kaelthas->GetVictim() == bot)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!bot->HasItemCount(ITEM_INFINITY_BLADE, 1, true))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (Group* group = bot->GetGroup())
|
||||||
|
{
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (!member || !member->IsAlive())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (member->HasAura(SPELL_KAELTHAS_MIND_CONTROL))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KaelthasSunstriderBossIsCastingPyroblastTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!botAI->IsDps(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* kaelthas = AI_VALUE2(Unit*, "find target", "kael'thas sunstrider");
|
||||||
|
return kaelthas && kaelthas->HasAura(SPELL_SHOCK_BARRIER);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KaelthasSunstriderBossIsManipulatingGravityTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return bot->HasAura(SPELL_GRAVITY_LAPSE);
|
||||||
|
}
|
||||||
338
src/Ai/Raid/TempestKeep/Trigger/RaidTempestKeepTriggers.h
Normal file
338
src/Ai/Raid/TempestKeep/Trigger/RaidTempestKeepTriggers.h
Normal file
@@ -0,0 +1,338 @@
|
|||||||
|
#ifndef _PLAYERBOT_RAIDTEMPESTKEEPTRIGGERS_H
|
||||||
|
#define _PLAYERBOT_RAIDTEMPESTKEEPTRIGGERS_H
|
||||||
|
|
||||||
|
#include "Trigger.h"
|
||||||
|
|
||||||
|
// General
|
||||||
|
|
||||||
|
// Trash
|
||||||
|
|
||||||
|
class CrimsonHandCenturionCastsArcaneVolleyTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CrimsonHandCenturionCastsArcaneVolleyTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "crimson hand centurion casts arcane volley") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Al'ar <Phoenix God>
|
||||||
|
|
||||||
|
class AlarPullingBossTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AlarPullingBossTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "al'ar pulling boss") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AlarBossIsFlyingBetweenPlatformsTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AlarBossIsFlyingBetweenPlatformsTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "al'ar boss is flying between platforms") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AlarEmbersOfAlarExplodeUponDeathTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AlarEmbersOfAlarExplodeUponDeathTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "al'ar embers of al'ar explode upon death") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AlarKillingEmbersOfAlarDamagesBossTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AlarKillingEmbersOfAlarDamagesBossTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "al'ar killing embers of al'ar damages boss") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AlarIncomingFlameQuillsTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AlarIncomingFlameQuillsTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "al'ar incoming flame quills") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AlarRisingFromTheAshesTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AlarRisingFromTheAshesTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "al'ar rising from the ashes") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AlarEverythingIsOnFireInPhase2Trigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AlarEverythingIsOnFireInPhase2Trigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "al'ar everything is on fire in phase 2") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AlarPhase2EncounterIsAtRoomCenterTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AlarPhase2EncounterIsAtRoomCenterTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "al'ar phase 2 encounter is at room center") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AlarStrategyChangesBetweenPhasesTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AlarStrategyChangesBetweenPhasesTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "al'ar strategy changes between phases") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Void Reaver
|
||||||
|
|
||||||
|
class VoidReaverBossCastsPoundingTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VoidReaverBossCastsPoundingTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "void reaver boss casts pounding") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VoidReaverKnockAwayReducesTankAggroTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VoidReaverKnockAwayReducesTankAggroTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "void reaver knock away reduces tank aggro") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VoidReaverBossLaunchesArcaneOrbsTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VoidReaverBossLaunchesArcaneOrbsTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "void reaver boss launches arcane orbs") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VoidReaverArcaneOrbIsIncomingTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VoidReaverArcaneOrbIsIncomingTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "void reaver arcane orb is incoming") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VoidReaverBotIsNotInCombatTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VoidReaverBotIsNotInCombatTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "void reaver bot is not in combat") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// High Astromancer Solarian
|
||||||
|
|
||||||
|
class HighAstromancerSolarianBossCastsWrathOfTheAstromancerTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighAstromancerSolarianBossCastsWrathOfTheAstromancerTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "high astromancer solarian boss casts wrath of the astromancer") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HighAstromancerSolarianBotHasWrathOfTheAstromancerTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighAstromancerSolarianBotHasWrathOfTheAstromancerTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "high astromancer solarian bot has wrath of the astromancer") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HighAstromancerSolarianBossHasVanishedTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighAstromancerSolarianBossHasVanishedTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "high astromancer solarian boss has vanished") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HighAstromancerSolarianSolariumPriestsSpawnedTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighAstromancerSolarianSolariumPriestsSpawnedTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "high astromancer solarian solarium priests spawned") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HighAstromancerSolarianBossCastsPsychicScreamTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighAstromancerSolarianBossCastsPsychicScreamTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "high astromancer boss casts psychic scream") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Kael'thas Sunstrider <Lord of the Blood Elves>
|
||||||
|
|
||||||
|
class KaelthasSunstriderThaladredIsFixatedOnBotTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderThaladredIsFixatedOnBotTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider thaladred is fixated on bot") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderPullingTankableAdvisorsTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderPullingTankableAdvisorsTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider pulling tankable advisors") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderSanguinarEngagedByMainTankTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderSanguinarEngagedByMainTankTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider sanguinar engaged by main tank") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderSanguinarCastsBellowingRoarTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderSanguinarCastsBellowingRoarTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider sanguinar casts bellowing roar") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderCapernianShouldBeTankedByAWarlockTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderCapernianShouldBeTankedByAWarlockTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider capernian should be tanked by a warlock") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderCapernianCastsArcaneBurstAndConflagrationTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderCapernianCastsArcaneBurstAndConflagrationTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider capernian casts arcane burst and conflagration") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderTelonicusEngagedByFirstAssistTankTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderTelonicusEngagedByFirstAssistTankTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider telonicus engaged by first assist tank") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderBotsHaveSpecificRolesInPhase3Trigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderBotsHaveSpecificRolesInPhase3Trigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider bots have specific roles in phase 3") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderDeterminingAdvisorKillOrderTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderDeterminingAdvisorKillOrderTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider determining advisor kill order") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderWaitingForTanksToGetAggroOnAdvisorsTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderWaitingForTanksToGetAggroOnAdvisorsTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider waiting for tanks to get aggro on advisors") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderLegendaryWeaponsAreAliveTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderLegendaryWeaponsAreAliveTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider legendary weapons are alive") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderLegendaryAxeCastsWhirlwindTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderLegendaryAxeCastsWhirlwindTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider legendary axe casts whirlwind") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderLegendaryWeaponsAreDeadAndLootableTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderLegendaryWeaponsAreDeadAndLootableTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider legendary weapons are dead and lootable") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderLegendaryWeaponsAreEquippedTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderLegendaryWeaponsAreEquippedTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider legendary weapons are equipped") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderLegendaryWeaponsWereLostTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderLegendaryWeaponsWereLostTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider legendary weapons were lost") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderBossHasEnteredTheFightTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderBossHasEnteredTheFightTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider boss has entered the fight") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderPhoenixesAndEggsAreSpawningTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderPhoenixesAndEggsAreSpawningTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider phoenixes and eggs are spawning") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderRaidMemberIsMindControlledTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderRaidMemberIsMindControlledTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider raid member is mind controlled") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderBossIsCastingPyroblastTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderBossIsCastingPyroblastTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider boss is casting pyroblast") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KaelthasSunstriderBossIsManipulatingGravityTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KaelthasSunstriderBossIsManipulatingGravityTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider boss is manipulating gravity") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
425
src/Ai/Raid/TempestKeep/Util/RaidTempestKeepHelpers.cpp
Normal file
425
src/Ai/Raid/TempestKeep/Util/RaidTempestKeepHelpers.cpp
Normal file
@@ -0,0 +1,425 @@
|
|||||||
|
#include "RaidTempestKeepHelpers.h"
|
||||||
|
#include "RaidTempestKeepActions.h"
|
||||||
|
#include "LootObjectStack.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
#include "RaidBossHelpers.h"
|
||||||
|
|
||||||
|
namespace TempestKeepHelpers
|
||||||
|
{
|
||||||
|
// General
|
||||||
|
|
||||||
|
Unit* GetNearestNonTankPlayerInRadius(PlayerbotAI* botAI, Player* bot, float radius)
|
||||||
|
{
|
||||||
|
Unit* nearestPlayer = nullptr;
|
||||||
|
float nearestDistance = radius;
|
||||||
|
|
||||||
|
Group* group = bot->GetGroup();
|
||||||
|
if (!group)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref != nullptr; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (!member || !member->IsAlive() || member == bot || botAI->IsTank(member))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
float distance = bot->GetExactDist2d(member);
|
||||||
|
if (distance < nearestDistance)
|
||||||
|
{
|
||||||
|
nearestDistance = distance;
|
||||||
|
nearestPlayer = member;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nearestPlayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Unit*> GetAllHazardTriggers(Player* bot, uint32 npcEntry, float searchRadius)
|
||||||
|
{
|
||||||
|
std::vector<Unit*> hazardTriggers;
|
||||||
|
|
||||||
|
std::list<Creature*> creatureList;
|
||||||
|
bot->GetCreatureListWithEntryInGrid(creatureList, npcEntry, searchRadius);
|
||||||
|
|
||||||
|
for (Creature* creature : creatureList)
|
||||||
|
{
|
||||||
|
if (creature && creature->IsAlive())
|
||||||
|
hazardTriggers.push_back(creature);
|
||||||
|
}
|
||||||
|
|
||||||
|
return hazardTriggers;
|
||||||
|
}
|
||||||
|
|
||||||
|
Position FindSafestNearbyPosition(Player* bot, const std::vector<Unit*>& hazards,
|
||||||
|
float hazardRadius, const Position* center)
|
||||||
|
{
|
||||||
|
constexpr float searchStep = M_PI / 8.0f;
|
||||||
|
constexpr float minDistance = 2.0f;
|
||||||
|
constexpr float maxDistance = 30.0f;
|
||||||
|
constexpr float distanceStep = 1.0f;
|
||||||
|
|
||||||
|
Position bestPos;
|
||||||
|
float minMoveDistance = std::numeric_limits<float>::max();
|
||||||
|
bool foundSafe = false;
|
||||||
|
|
||||||
|
for (float distance = minDistance; distance <= maxDistance; distance += distanceStep)
|
||||||
|
{
|
||||||
|
for (float angle = 0.0f; angle < 2 * M_PI; angle += searchStep)
|
||||||
|
{
|
||||||
|
const Position& searchCenter = center ? *center : bot->GetPosition();
|
||||||
|
float x = searchCenter.GetPositionX() + distance * std::cos(angle);
|
||||||
|
float y = searchCenter.GetPositionY() + distance * std::sin(angle);
|
||||||
|
|
||||||
|
bool isSafe = true;
|
||||||
|
for (Unit* hazard : hazards)
|
||||||
|
{
|
||||||
|
if (hazard->GetExactDist2d(x, y) < hazardRadius)
|
||||||
|
{
|
||||||
|
isSafe = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isSafe)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Position testPos(x, y, bot->GetPositionZ());
|
||||||
|
|
||||||
|
bool pathSafe = IsPathSafeFromHazards(bot->GetPosition(), testPos, hazards, hazardRadius);
|
||||||
|
if (pathSafe || !foundSafe)
|
||||||
|
{
|
||||||
|
float moveDistance = bot->GetExactDist2d(x, y);
|
||||||
|
|
||||||
|
if (pathSafe && (!foundSafe || moveDistance < minMoveDistance))
|
||||||
|
{
|
||||||
|
bestPos = testPos;
|
||||||
|
minMoveDistance = moveDistance;
|
||||||
|
foundSafe = true;
|
||||||
|
}
|
||||||
|
else if (!foundSafe && moveDistance < minMoveDistance)
|
||||||
|
{
|
||||||
|
bestPos = testPos;
|
||||||
|
minMoveDistance = moveDistance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundSafe)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bestPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsPathSafeFromHazards(
|
||||||
|
const Position& start, const Position& end, const std::vector<Unit*>& hazards, float hazardRadius)
|
||||||
|
{
|
||||||
|
constexpr uint8 numChecks = 10;
|
||||||
|
float dx = end.GetPositionX() - start.GetPositionX();
|
||||||
|
float dy = end.GetPositionY() - start.GetPositionY();
|
||||||
|
|
||||||
|
for (uint8 i = 1; i <= numChecks; ++i)
|
||||||
|
{
|
||||||
|
float ratio = static_cast<float>(i) / numChecks;
|
||||||
|
float checkX = start.GetPositionX() + dx * ratio;
|
||||||
|
float checkY = start.GetPositionY() + dy * ratio;
|
||||||
|
|
||||||
|
for (Unit* hazard : hazards)
|
||||||
|
{
|
||||||
|
float distToHazard = hazard->GetExactDist2d(checkX, checkY);
|
||||||
|
if (distToHazard < hazardRadius)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Al'ar <Phoenix God>
|
||||||
|
|
||||||
|
const Position ALAR_PLATFORM_0 = { 335.638f, 59.4879f, 17.9319f }; // West Platform
|
||||||
|
const Position ALAR_PLATFORM_1 = { 388.751f, 31.7312f, 20.2636f }; // Northwest Platform
|
||||||
|
const Position ALAR_PLATFORM_2 = { 388.791f, -33.1059f, 20.2636f }; // Northeast Platform
|
||||||
|
const Position ALAR_PLATFORM_3 = { 332.723f, -61.159f, 17.9791f }; // East Platform
|
||||||
|
const std::array<Position, 4> PLATFORM_POSITIONS =
|
||||||
|
{
|
||||||
|
ALAR_PLATFORM_0,
|
||||||
|
ALAR_PLATFORM_1,
|
||||||
|
ALAR_PLATFORM_2,
|
||||||
|
ALAR_PLATFORM_3
|
||||||
|
};
|
||||||
|
const Position ALAR_GROUND_0 = { 336.439f, 48.181f, -2.389f }; // Ground counterpart to West Platform
|
||||||
|
const Position ALAR_GROUND_1 = { 379.122f, 25.146f, -2.385f }; // Ground counterpart to Northwest Platform
|
||||||
|
const Position ALAR_GROUND_2 = { 378.583f, -27.481f, -2.385f }; // Ground counterpart to Northeast Platform
|
||||||
|
const Position ALAR_GROUND_3 = { 331.631f, -49.716f, -2.389f }; // Ground counterpart to East Platform
|
||||||
|
const std::array<Position, 4> GROUND_POSITIONS =
|
||||||
|
{
|
||||||
|
ALAR_GROUND_0,
|
||||||
|
ALAR_GROUND_1,
|
||||||
|
ALAR_GROUND_2,
|
||||||
|
ALAR_GROUND_3
|
||||||
|
};
|
||||||
|
const Position ALAR_ROOM_CENTER = { 330.611f, -2.540f, -2.389f };
|
||||||
|
const Position ALAR_POINT_QUILL_OR_DIVE = { 332.000f, 0.010f, 43.000f };
|
||||||
|
const Position ALAR_POINT_MIDDLE = { 331.000f, 0.010f, -2.380f };
|
||||||
|
const Position ALAR_SE_RAMP_BASE = { 281.064f, -36.590f, -2.389f };
|
||||||
|
const Position ALAR_SW_RAMP_BASE = { 281.064f, 36.590f, -2.389f };
|
||||||
|
const Position ALAR_ROOM_S_CENTER = { 281.064f, 0.000f, -2.389f };
|
||||||
|
|
||||||
|
std::unordered_map<uint32, bool> lastRebirthState;
|
||||||
|
std::unordered_map<uint32, bool> isAlarInPhase2;
|
||||||
|
|
||||||
|
int8 GetAlarDestinationLocationIndex(Unit* alar, Position& dest)
|
||||||
|
{
|
||||||
|
if (!alar)
|
||||||
|
return LOCATION_NONE;
|
||||||
|
|
||||||
|
float x, y, z;
|
||||||
|
if (!alar->GetMotionMaster()->GetDestination(x, y, z))
|
||||||
|
return LOCATION_NONE;
|
||||||
|
|
||||||
|
dest.Relocate(x, y, z);
|
||||||
|
|
||||||
|
const std::array<Position, 6> locations =
|
||||||
|
{
|
||||||
|
ALAR_PLATFORM_0,
|
||||||
|
ALAR_PLATFORM_1,
|
||||||
|
ALAR_PLATFORM_2,
|
||||||
|
ALAR_PLATFORM_3,
|
||||||
|
ALAR_POINT_QUILL_OR_DIVE,
|
||||||
|
ALAR_POINT_MIDDLE,
|
||||||
|
};
|
||||||
|
|
||||||
|
float minDist = std::numeric_limits<float>::max();
|
||||||
|
int8 locationIndex = LOCATION_NONE;
|
||||||
|
for (int8 i = 0; i < TOTAL_ALAR_LOCATIONS; ++i)
|
||||||
|
{
|
||||||
|
float dist = dest.GetExactDist2d(&locations[i]);
|
||||||
|
if (dist < minDist)
|
||||||
|
{
|
||||||
|
minDist = dist;
|
||||||
|
locationIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (minDist > 0.1f)
|
||||||
|
return LOCATION_NONE;
|
||||||
|
|
||||||
|
return locationIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
int8 GetAlarCurrentLocationIndex(Unit* alar)
|
||||||
|
{
|
||||||
|
if (!alar)
|
||||||
|
return LOCATION_NONE;
|
||||||
|
|
||||||
|
const std::array<Position, 6> locations =
|
||||||
|
{
|
||||||
|
ALAR_PLATFORM_0,
|
||||||
|
ALAR_PLATFORM_1,
|
||||||
|
ALAR_PLATFORM_2,
|
||||||
|
ALAR_PLATFORM_3,
|
||||||
|
ALAR_POINT_QUILL_OR_DIVE,
|
||||||
|
ALAR_POINT_MIDDLE,
|
||||||
|
};
|
||||||
|
|
||||||
|
float minDist = std::numeric_limits<float>::max();
|
||||||
|
int8 locationIndex = LOCATION_NONE;
|
||||||
|
for (int8 i = 0; i < TOTAL_ALAR_LOCATIONS; ++i)
|
||||||
|
{
|
||||||
|
float dist = alar->GetPosition().GetExactDist2d(&locations[i]);
|
||||||
|
if (dist < minDist)
|
||||||
|
{
|
||||||
|
minDist = dist;
|
||||||
|
locationIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (minDist > 0.1f)
|
||||||
|
return LOCATION_NONE;
|
||||||
|
|
||||||
|
return locationIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetClosestPlatformAndGround(const Position& botPos, int8& closestPlatform, Position& ground)
|
||||||
|
{
|
||||||
|
float minDist = std::numeric_limits<float>::max();
|
||||||
|
closestPlatform = -1;
|
||||||
|
for (int8 i = 0; i < 4; ++i)
|
||||||
|
{
|
||||||
|
float dist = botPos.GetExactDist2d(&PLATFORM_POSITIONS[i]);
|
||||||
|
if (dist < minDist)
|
||||||
|
{
|
||||||
|
minDist = dist;
|
||||||
|
closestPlatform = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ground = GROUND_POSITIONS[closestPlatform];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<Unit*, Unit*> GetFirstTwoEmbersOfAlar(PlayerbotAI* botAI)
|
||||||
|
{
|
||||||
|
Unit* firstEmber = nullptr;
|
||||||
|
Unit* secondEmber = nullptr;
|
||||||
|
|
||||||
|
for (auto const& guid :
|
||||||
|
botAI->GetAiObjectContext()->GetValue<GuidVector>("possible targets no los")->Get())
|
||||||
|
{
|
||||||
|
Unit* unit = botAI->GetUnit(guid);
|
||||||
|
if (unit && unit->IsAlive() && unit->GetEntry() == NPC_EMBER_OF_ALAR)
|
||||||
|
{
|
||||||
|
if (!firstEmber)
|
||||||
|
{
|
||||||
|
firstEmber = unit;
|
||||||
|
}
|
||||||
|
else if (!secondEmber)
|
||||||
|
{
|
||||||
|
secondEmber = unit;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { firstEmber, secondEmber };
|
||||||
|
}
|
||||||
|
|
||||||
|
Player* GetSecondEmberTank(PlayerbotAI* botAI)
|
||||||
|
{
|
||||||
|
Player* mainTank = GetGroupMainTank(botAI, botAI->GetBot());
|
||||||
|
Player* assistTank = GetGroupAssistTank(botAI, botAI->GetBot(), 0);
|
||||||
|
|
||||||
|
bool mainTankHasMelt = mainTank && mainTank->HasAura(SPELL_MELT_ARMOR);
|
||||||
|
bool assistTankHasMelt = assistTank && assistTank->HasAura(SPELL_MELT_ARMOR);
|
||||||
|
|
||||||
|
if (mainTankHasMelt)
|
||||||
|
return mainTank;
|
||||||
|
|
||||||
|
if (assistTankHasMelt || (!mainTankHasMelt && !assistTankHasMelt))
|
||||||
|
return assistTank;
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Void Reaver
|
||||||
|
|
||||||
|
const Position VOID_REAVER_TANK_POSITION = { 423.845f, 371.733f, 14.897f };
|
||||||
|
|
||||||
|
std::unordered_map<ObjectGuid, bool> hasReachedVoidReaverPosition;
|
||||||
|
std::unordered_map<uint32, std::vector<ArcaneOrbData>> voidReaverArcaneOrbs;
|
||||||
|
|
||||||
|
// Kael'thas Sunstrider <Lord of the Blood Elves>
|
||||||
|
|
||||||
|
const Position SANGUINAR_TANK_POSITION = { 775.478f, 39.888f, 46.780f };
|
||||||
|
const Position SANGUINAR_WAITING_POSITION = { 761.850f, 27.459f, 46.779f };
|
||||||
|
const Position TELONICUS_TANK_POSITION = { 773.717f, 44.091f, 46.780f };
|
||||||
|
const Position TELONICUS_WAITING_POSITION = { 754.347f, 31.739f, 46.796f };
|
||||||
|
const Position ADVISOR_HEAL_POSITION = { 752.171f, 19.494f, 46.779f };
|
||||||
|
const Position CAPERNIAN_WAITING_POSITION = { 743.897f, -11.575f, 46.779f };
|
||||||
|
const Position KAELTHAS_TANK_POSITION = { 799.390f, -0.837f, 48.729f };
|
||||||
|
|
||||||
|
std::unordered_map<uint32, time_t> advisorDpsWaitTimer;
|
||||||
|
|
||||||
|
// (1) First priority is an assistant Warlock (real player or bot)
|
||||||
|
// (2) If no assistant Warlock, then look for any Warlock bot
|
||||||
|
Player* GetCapernianTank(Player* bot)
|
||||||
|
{
|
||||||
|
Group* group = bot->GetGroup();
|
||||||
|
if (!group)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
Player* fallbackWarlock = nullptr;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
if (!fallbackWarlock && GET_PLAYERBOT_AI(member))
|
||||||
|
fallbackWarlock = member;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fallbackWarlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
// One Hunter will start on Sanguinar in Phase 3 with Melee to apply Armor Disruption
|
||||||
|
// (1) First priority is an assistant Hunter (real player or bot)
|
||||||
|
// (2) If no assistant Hunter, then look for any Hunter bot
|
||||||
|
bool IsDebuffHunter(Player* bot)
|
||||||
|
{
|
||||||
|
if (bot->getClass() != CLASS_HUNTER || !bot->IsAlive())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Group* group = bot->GetGroup();
|
||||||
|
if (!group)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Player* fallbackHunter = nullptr;
|
||||||
|
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (!member || !member->IsAlive() || member->getClass() != CLASS_HUNTER)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (group->IsAssistant(member->GetGUID()))
|
||||||
|
return member == bot;
|
||||||
|
|
||||||
|
if (!fallbackHunter && GET_PLAYERBOT_AI(member))
|
||||||
|
fallbackHunter = member;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fallbackHunter == bot;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsAnyLegendaryWeaponDead(Player* bot)
|
||||||
|
{
|
||||||
|
static const std::array<uint32, 7> weaponEntries =
|
||||||
|
{
|
||||||
|
NPC_STAFF_OF_DISINTEGRATION,
|
||||||
|
NPC_COSMIC_INFUSER,
|
||||||
|
NPC_INFINITY_BLADES,
|
||||||
|
NPC_WARP_SLICER,
|
||||||
|
NPC_PHASESHIFT_BULWARK,
|
||||||
|
NPC_NETHERSTRAND_LONGBOW,
|
||||||
|
NPC_DEVASTATION
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr float searchRadius = 100.0f;
|
||||||
|
|
||||||
|
for (uint32 entry : weaponEntries)
|
||||||
|
{
|
||||||
|
Creature* weapon = bot->FindNearestCreature(entry, searchRadius, false);
|
||||||
|
|
||||||
|
if (weapon && !weapon->IsAlive())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasEquippableItemForSlot(Player* bot, uint8 slot)
|
||||||
|
{
|
||||||
|
for (uint8 i = 0; i < 5; ++i)
|
||||||
|
{
|
||||||
|
uint8 bag = (i == 0) ? INVENTORY_SLOT_BAG_0 : (INVENTORY_SLOT_BAG_START + i - 1);
|
||||||
|
uint8 startSlot = (bag == INVENTORY_SLOT_BAG_0) ? INVENTORY_SLOT_ITEM_START : 0;
|
||||||
|
uint8 endSlot = (bag == INVENTORY_SLOT_BAG_0) ? INVENTORY_SLOT_ITEM_END :
|
||||||
|
(bot->GetBagByPos(bag) ? bot->GetBagByPos(bag)->GetBagSize() : 0);
|
||||||
|
|
||||||
|
for (uint8 bagSlot = startSlot; bagSlot < endSlot; ++bagSlot)
|
||||||
|
{
|
||||||
|
Item* item = bot->GetItemByPos(bag, bagSlot);
|
||||||
|
if (!item || !item->GetTemplate())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
uint16 dest = 0;
|
||||||
|
if (bot->CanEquipItem(slot, dest, item, false) == EQUIP_ERR_OK)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
158
src/Ai/Raid/TempestKeep/Util/RaidTempestKeepHelpers.h
Normal file
158
src/Ai/Raid/TempestKeep/Util/RaidTempestKeepHelpers.h
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
#ifndef _PLAYERBOT_RAIDTEMPESTKEEPHELPERS_H_
|
||||||
|
#define _PLAYERBOT_RAIDTEMPESTKEEPHELPERS_H_
|
||||||
|
|
||||||
|
#include <ctime>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "AiObject.h"
|
||||||
|
#include "Position.h"
|
||||||
|
#include "Unit.h"
|
||||||
|
|
||||||
|
namespace TempestKeepHelpers
|
||||||
|
{
|
||||||
|
enum TempestKeepSpells
|
||||||
|
{
|
||||||
|
// Trash
|
||||||
|
SPELL_ARCANE_FLURRY = 37268,
|
||||||
|
|
||||||
|
// Al'ar
|
||||||
|
SPELL_REBIRTH_PHASE2 = 34342,
|
||||||
|
SPELL_REBIRTH_DIVE = 35369,
|
||||||
|
SPELL_MELT_ARMOR = 35410,
|
||||||
|
|
||||||
|
// Void Reaver
|
||||||
|
SPELL_ARCANE_ORB = 34172,
|
||||||
|
|
||||||
|
// High Astromancer Solarian
|
||||||
|
SPELL_SOLARIAN_TRANSFORM = 39117,
|
||||||
|
SPELL_WRATH_OF_THE_ASTROMANCER = 42783,
|
||||||
|
|
||||||
|
// Kael'thas Sunstrider
|
||||||
|
SPELL_PERMANENT_FEIGN_DEATH = 29266,
|
||||||
|
SPELL_GRAVITY_LAPSE = 39432,
|
||||||
|
SPELL_KAEL_FULL_POWER = 36187,
|
||||||
|
SPELL_MENTAL_PROTECTION_FIELD = 36480, // Staff of Disintegration
|
||||||
|
SPELL_ARCANE_BARRIER = 36481, // Phaseshift Bulwark
|
||||||
|
SPELL_KAELTHAS_MIND_CONTROL = 36797,
|
||||||
|
SPELL_SHOCK_BARRIER = 36815,
|
||||||
|
SPELL_STAFF_FROSTBOLT = 36990,
|
||||||
|
|
||||||
|
// Hunter
|
||||||
|
SPELL_MISDIRECTION = 35079,
|
||||||
|
|
||||||
|
// Priest
|
||||||
|
SPELL_FEAR_WARD = 6346,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum TempestKeepNPCs
|
||||||
|
{
|
||||||
|
// Al'ar
|
||||||
|
NPC_EMBER_OF_ALAR = 19551,
|
||||||
|
NPC_FLAME_PATCH = 20602,
|
||||||
|
|
||||||
|
// High Astromancer Solarian
|
||||||
|
NPC_SOLARIUM_PRIEST = 18806,
|
||||||
|
|
||||||
|
// Kael'thas Sunstrider
|
||||||
|
NPC_KAELTHAS_SUNSTRIDER = 19622,
|
||||||
|
NPC_NETHERSTRAND_LONGBOW = 21268,
|
||||||
|
NPC_DEVASTATION = 21269,
|
||||||
|
NPC_COSMIC_INFUSER = 21270,
|
||||||
|
NPC_INFINITY_BLADES = 21271, // Item is singular, but NPC is plural
|
||||||
|
NPC_WARP_SLICER = 21272,
|
||||||
|
NPC_PHASESHIFT_BULWARK = 21273,
|
||||||
|
NPC_STAFF_OF_DISINTEGRATION = 21274,
|
||||||
|
// NPC_NETHER_VAPOR = 21002, // Unimplemented in AC; method needed if fixed
|
||||||
|
NPC_PHOENIX = 21362,
|
||||||
|
NPC_PHOENIX_EGG = 21364,
|
||||||
|
NPC_FLAME_STRIKE_TRIGGER = 21369,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum TempestKeepItems
|
||||||
|
{
|
||||||
|
// Kael'thas Sunstrider
|
||||||
|
ITEM_WARP_SLICER = 30311,
|
||||||
|
ITEM_INFINITY_BLADE = 30312,
|
||||||
|
ITEM_STAFF_OF_DISINTEGRATION = 30313,
|
||||||
|
ITEM_PHASESHIFT_BULWARK = 30314,
|
||||||
|
ITEM_DEVASTATION = 30316,
|
||||||
|
ITEM_COSMIC_INFUSER = 30317,
|
||||||
|
ITEM_NETHERSTRAND_LONGBOW = 30318,
|
||||||
|
ITEM_NETHER_SPIKES = 30319,
|
||||||
|
};
|
||||||
|
|
||||||
|
// General
|
||||||
|
constexpr uint32 TEMPEST_KEEP_MAP_ID = 550;
|
||||||
|
Unit* GetNearestNonTankPlayerInRadius(PlayerbotAI* botAI, Player* bot, float radius);
|
||||||
|
std::vector<Unit*> GetAllHazardTriggers(Player* bot, uint32 npcEntry, float searchRadius);
|
||||||
|
Position FindSafestNearbyPosition(Player* bot, const std::vector<Unit*>& hazards,
|
||||||
|
float hazardRadius, const Position* center = nullptr);
|
||||||
|
bool IsPathSafeFromHazards(
|
||||||
|
const Position& start, const Position& end, const std::vector<Unit*>& hazards,
|
||||||
|
float hazardRadius);
|
||||||
|
|
||||||
|
// Al'ar <Phoenix God>
|
||||||
|
enum AlarLocationIndex
|
||||||
|
{
|
||||||
|
PLATFORM_0_IDX,
|
||||||
|
PLATFORM_1_IDX,
|
||||||
|
PLATFORM_2_IDX,
|
||||||
|
PLATFORM_3_IDX,
|
||||||
|
POINT_QUILL_OR_DIVE_IDX,
|
||||||
|
POINT_MIDDLE_IDX,
|
||||||
|
LOCATION_NONE = -1
|
||||||
|
};
|
||||||
|
constexpr float ALAR_BALCONY_Z = 17.0f;
|
||||||
|
extern const Position ALAR_PLATFORM_0;
|
||||||
|
extern const Position ALAR_PLATFORM_1;
|
||||||
|
extern const Position ALAR_PLATFORM_2;
|
||||||
|
extern const Position ALAR_PLATFORM_3;
|
||||||
|
extern const std::array<Position, 4> PLATFORM_POSITIONS;
|
||||||
|
extern const Position ALAR_GROUND_0;
|
||||||
|
extern const Position ALAR_GROUND_1;
|
||||||
|
extern const Position ALAR_GROUND_2;
|
||||||
|
extern const Position ALAR_GROUND_3;
|
||||||
|
extern const std::array<Position, 4> GROUND_POSITIONS;
|
||||||
|
extern const Position ALAR_ROOM_CENTER;
|
||||||
|
extern const Position ALAR_POINT_QUILL_OR_DIVE;
|
||||||
|
extern const Position ALAR_POINT_MIDDLE;
|
||||||
|
extern const Position ALAR_SE_RAMP_BASE;
|
||||||
|
extern const Position ALAR_SW_RAMP_BASE;
|
||||||
|
extern const Position ALAR_ROOM_S_CENTER;
|
||||||
|
constexpr uint8 TOTAL_ALAR_LOCATIONS = 6;
|
||||||
|
extern std::unordered_map<uint32, bool> lastRebirthState;
|
||||||
|
extern std::unordered_map<uint32, bool> isAlarInPhase2;
|
||||||
|
int8 GetAlarDestinationLocationIndex(Unit* alar, Position& dest);
|
||||||
|
int8 GetAlarCurrentLocationIndex(Unit* alar);
|
||||||
|
void GetClosestPlatformAndGround(
|
||||||
|
const Position& botPos, int8& closestPlatform, Position& ground);
|
||||||
|
std::pair<Unit*, Unit*> GetFirstTwoEmbersOfAlar(PlayerbotAI* botAI);
|
||||||
|
Player* GetSecondEmberTank(PlayerbotAI* botAI);
|
||||||
|
|
||||||
|
// Void Reaver
|
||||||
|
extern const Position VOID_REAVER_TANK_POSITION;
|
||||||
|
extern std::unordered_map<ObjectGuid, bool> hasReachedVoidReaverPosition;
|
||||||
|
struct ArcaneOrbData
|
||||||
|
{
|
||||||
|
Position destination;
|
||||||
|
uint32 castTime;
|
||||||
|
};
|
||||||
|
extern std::unordered_map<uint32, std::vector<ArcaneOrbData>> voidReaverArcaneOrbs;
|
||||||
|
|
||||||
|
// Kael'thas Sunstrider <Lord of the Blood Elves>
|
||||||
|
extern const Position SANGUINAR_TANK_POSITION;
|
||||||
|
extern const Position SANGUINAR_WAITING_POSITION;
|
||||||
|
extern const Position TELONICUS_TANK_POSITION;
|
||||||
|
extern const Position TELONICUS_WAITING_POSITION;
|
||||||
|
extern const Position CAPERNIAN_WAITING_POSITION;
|
||||||
|
extern const Position ADVISOR_HEAL_POSITION;
|
||||||
|
extern const Position KAELTHAS_TANK_POSITION;
|
||||||
|
extern std::unordered_map<uint32, time_t> advisorDpsWaitTimer;
|
||||||
|
Player* GetCapernianTank(Player* bot);
|
||||||
|
bool IsDebuffHunter(Player* bot);
|
||||||
|
bool IsAnyLegendaryWeaponDead(Player* bot);
|
||||||
|
bool HasEquippableItemForSlot(Player* bot, uint8 slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
54
src/Ai/Raid/TempestKeep/Util/RaidTempestKeepKaelthasBossAI.h
Normal file
54
src/Ai/Raid/TempestKeep/Util/RaidTempestKeepKaelthasBossAI.h
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#ifndef _PLAYERBOT_RAIDTEMPESTKEEPKAELTHASBOSSAI_H_
|
||||||
|
#define _PLAYERBOT_RAIDTEMPESTKEEPKAELTHASBOSSAI_H_
|
||||||
|
|
||||||
|
#include "ScriptedCreature.h"
|
||||||
|
|
||||||
|
enum KTYells
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
enum KTPhases
|
||||||
|
{
|
||||||
|
PHASE_NONE = 0,
|
||||||
|
PHASE_SINGLE_ADVISOR = 1,
|
||||||
|
PHASE_WEAPONS = 2,
|
||||||
|
PHASE_TRANSITION = 3,
|
||||||
|
PHASE_ALL_ADVISORS = 4,
|
||||||
|
PHASE_FINAL = 5
|
||||||
|
};
|
||||||
|
|
||||||
|
enum KTActions
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
struct boss_kaelthas : public BossAI
|
||||||
|
{
|
||||||
|
boss_kaelthas(Creature* creature);
|
||||||
|
|
||||||
|
void PrepareAdvisors();
|
||||||
|
void SetRoomState(GOState state);
|
||||||
|
void Reset() override;
|
||||||
|
void AttackStart(Unit* who) override;
|
||||||
|
void MoveInLineOfSight(Unit* who) override;
|
||||||
|
void KilledUnit(Unit* victim) override;
|
||||||
|
void JustSummoned(Creature* summon) override;
|
||||||
|
void SpellHit(Unit* caster, SpellInfo const* spell) override;
|
||||||
|
void MovementInform(uint32 type, uint32 point) override;
|
||||||
|
void ExecuteMiddleEvent();
|
||||||
|
void IntroduceNewAdvisor(KTYells talkIntroduction, KTActions kaelAction);
|
||||||
|
void PhaseEnchantedWeaponsExecute();
|
||||||
|
void PhaseAllAdvisorsExecute();
|
||||||
|
void PhaseKaelExecute();
|
||||||
|
void UpdateAI(uint32 diff) override;
|
||||||
|
bool CheckEvadeIfOutOfCombatArea() const override;
|
||||||
|
void JustDied(Unit* killer) override;
|
||||||
|
|
||||||
|
uint32 GetPhase() const { return _phase; } // This is the only addition to the existing class
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32 _phase;
|
||||||
|
uint8 _advisorsAlive;
|
||||||
|
bool _transitionSceneReached = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
47
src/Ai/Raid/TempestKeep/Util/RaidTempestKeepScripts.cpp
Normal file
47
src/Ai/Raid/TempestKeep/Util/RaidTempestKeepScripts.cpp
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
#include "RaidTempestKeepHelpers.h"
|
||||||
|
#include "ObjectAccessor.h"
|
||||||
|
#include "Player.h"
|
||||||
|
#include "ScriptMgr.h"
|
||||||
|
#include "Spell.h"
|
||||||
|
#include "Timer.h"
|
||||||
|
|
||||||
|
using namespace TempestKeepHelpers;
|
||||||
|
|
||||||
|
class BossListenerScript : public AllSpellScript
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BossListenerScript() : AllSpellScript("BossListenerScript") { }
|
||||||
|
|
||||||
|
void OnSpellCast(Spell* spell, Unit* caster, SpellInfo const* spellInfo, bool /*skipCheck*/) override
|
||||||
|
{
|
||||||
|
if (spellInfo->Id != SPELL_ARCANE_ORB)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::list<TargetInfo> const& targets = *spell->GetUniqueTargetInfo();
|
||||||
|
if (targets.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
Player* target = ObjectAccessor::GetPlayer(*caster, targets.front().targetGUID);
|
||||||
|
if (!target)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto& orbs = voidReaverArcaneOrbs[caster->GetMap()->GetInstanceId()];
|
||||||
|
uint32 currentTime = getMSTime();
|
||||||
|
|
||||||
|
ArcaneOrbData orbData;
|
||||||
|
orbData.destination = target->GetPosition();
|
||||||
|
orbData.castTime = currentTime;
|
||||||
|
|
||||||
|
orbs.push_back(orbData);
|
||||||
|
|
||||||
|
orbs.erase(std::remove_if(orbs.begin(), orbs.end(),
|
||||||
|
[currentTime](const ArcaneOrbData& orb) {
|
||||||
|
return getMSTimeDiff(orb.castTime, currentTime) > 5000;
|
||||||
|
}), orbs.end());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void AddSC_TempestKeepBotScripts()
|
||||||
|
{
|
||||||
|
new BossListenerScript();
|
||||||
|
}
|
||||||
@@ -37,12 +37,14 @@
|
|||||||
#include "Ai/Raid/BlackwingLair/RaidBwlTriggerContext.h"
|
#include "Ai/Raid/BlackwingLair/RaidBwlTriggerContext.h"
|
||||||
#include "Ai/Raid/Karazhan/RaidKarazhanActionContext.h"
|
#include "Ai/Raid/Karazhan/RaidKarazhanActionContext.h"
|
||||||
#include "Ai/Raid/Karazhan/RaidKarazhanTriggerContext.h"
|
#include "Ai/Raid/Karazhan/RaidKarazhanTriggerContext.h"
|
||||||
#include "Ai/Raid/Magtheridon/RaidMagtheridonActionContext.h"
|
|
||||||
#include "Ai/Raid/Magtheridon/RaidMagtheridonTriggerContext.h"
|
|
||||||
#include "Ai/Raid/GruulsLair/RaidGruulsLairActionContext.h"
|
#include "Ai/Raid/GruulsLair/RaidGruulsLairActionContext.h"
|
||||||
#include "Ai/Raid/GruulsLair/RaidGruulsLairTriggerContext.h"
|
#include "Ai/Raid/GruulsLair/RaidGruulsLairTriggerContext.h"
|
||||||
|
#include "Ai/Raid/Magtheridon/RaidMagtheridonActionContext.h"
|
||||||
|
#include "Ai/Raid/Magtheridon/RaidMagtheridonTriggerContext.h"
|
||||||
#include "Ai/Raid/SerpentshrineCavern/RaidSSCActionContext.h"
|
#include "Ai/Raid/SerpentshrineCavern/RaidSSCActionContext.h"
|
||||||
#include "Ai/Raid/SerpentshrineCavern/RaidSSCTriggerContext.h"
|
#include "Ai/Raid/SerpentshrineCavern/RaidSSCTriggerContext.h"
|
||||||
|
#include "Ai/Raid/TempestKeep/RaidTempestKeepActionContext.h"
|
||||||
|
#include "Ai/Raid/TempestKeep/RaidTempestKeepTriggerContext.h"
|
||||||
#include "Ai/Raid/EyeOfEternity/RaidEoEActionContext.h"
|
#include "Ai/Raid/EyeOfEternity/RaidEoEActionContext.h"
|
||||||
#include "Ai/Raid/EyeOfEternity/RaidEoETriggerContext.h"
|
#include "Ai/Raid/EyeOfEternity/RaidEoETriggerContext.h"
|
||||||
#include "Ai/Raid/VaultOfArchavon/RaidVoAActionContext.h"
|
#include "Ai/Raid/VaultOfArchavon/RaidVoAActionContext.h"
|
||||||
@@ -115,9 +117,10 @@ void AiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList<Act
|
|||||||
actionContexts.Add(new RaidMcActionContext());
|
actionContexts.Add(new RaidMcActionContext());
|
||||||
actionContexts.Add(new RaidBwlActionContext());
|
actionContexts.Add(new RaidBwlActionContext());
|
||||||
actionContexts.Add(new RaidKarazhanActionContext());
|
actionContexts.Add(new RaidKarazhanActionContext());
|
||||||
actionContexts.Add(new RaidMagtheridonActionContext());
|
|
||||||
actionContexts.Add(new RaidGruulsLairActionContext());
|
actionContexts.Add(new RaidGruulsLairActionContext());
|
||||||
|
actionContexts.Add(new RaidMagtheridonActionContext());
|
||||||
actionContexts.Add(new RaidSSCActionContext());
|
actionContexts.Add(new RaidSSCActionContext());
|
||||||
|
actionContexts.Add(new RaidTempestKeepActionContext());
|
||||||
actionContexts.Add(new RaidOsActionContext());
|
actionContexts.Add(new RaidOsActionContext());
|
||||||
actionContexts.Add(new RaidEoEActionContext());
|
actionContexts.Add(new RaidEoEActionContext());
|
||||||
actionContexts.Add(new RaidVoAActionContext());
|
actionContexts.Add(new RaidVoAActionContext());
|
||||||
@@ -150,9 +153,10 @@ void AiObjectContext::BuildSharedTriggerContexts(SharedNamedObjectContextList<Tr
|
|||||||
triggerContexts.Add(new RaidMcTriggerContext());
|
triggerContexts.Add(new RaidMcTriggerContext());
|
||||||
triggerContexts.Add(new RaidBwlTriggerContext());
|
triggerContexts.Add(new RaidBwlTriggerContext());
|
||||||
triggerContexts.Add(new RaidKarazhanTriggerContext());
|
triggerContexts.Add(new RaidKarazhanTriggerContext());
|
||||||
triggerContexts.Add(new RaidMagtheridonTriggerContext());
|
|
||||||
triggerContexts.Add(new RaidGruulsLairTriggerContext());
|
triggerContexts.Add(new RaidGruulsLairTriggerContext());
|
||||||
|
triggerContexts.Add(new RaidMagtheridonTriggerContext());
|
||||||
triggerContexts.Add(new RaidSSCTriggerContext());
|
triggerContexts.Add(new RaidSSCTriggerContext());
|
||||||
|
triggerContexts.Add(new RaidTempestKeepTriggerContext());
|
||||||
triggerContexts.Add(new RaidOsTriggerContext());
|
triggerContexts.Add(new RaidOsTriggerContext());
|
||||||
triggerContexts.Add(new RaidEoETriggerContext());
|
triggerContexts.Add(new RaidEoETriggerContext());
|
||||||
triggerContexts.Add(new RaidVoATriggerContext());
|
triggerContexts.Add(new RaidVoATriggerContext());
|
||||||
|
|||||||
@@ -1556,6 +1556,9 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
|
|||||||
case 548:
|
case 548:
|
||||||
strategyName = "ssc"; // Serpentshrine Cavern
|
strategyName = "ssc"; // Serpentshrine Cavern
|
||||||
break;
|
break;
|
||||||
|
case 550:
|
||||||
|
strategyName = "tempestkeep"; // Tempest Keep
|
||||||
|
break;
|
||||||
case 565:
|
case 565:
|
||||||
strategyName = "gruulslair"; // Gruul's Lair
|
strategyName = "gruulslair"; // Gruul's Lair
|
||||||
break;
|
break;
|
||||||
@@ -1797,98 +1800,104 @@ bool PlayerbotAI::IsCombo(Player* player)
|
|||||||
|
|
||||||
bool PlayerbotAI::IsRangedDps(Player* player, bool bySpec) { return IsRanged(player, bySpec) && IsDps(player, bySpec); }
|
bool PlayerbotAI::IsRangedDps(Player* player, bool bySpec) { return IsRanged(player, bySpec) && IsDps(player, bySpec); }
|
||||||
|
|
||||||
bool PlayerbotAI::IsAssistHealOfIndex(Player* player, int index, bool ignoreDeadPlayers)
|
bool PlayerbotAI::IsAssistHealOfIndex(Player* player, uint8 index, bool ignoreDeadPlayers)
|
||||||
{
|
{
|
||||||
|
if (!IsHeal(player))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (ignoreDeadPlayers && !player->IsAlive())
|
||||||
|
return false;
|
||||||
|
|
||||||
Group* group = player->GetGroup();
|
Group* group = player->GetGroup();
|
||||||
if (!group)
|
if (!group)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
int counter = 0;
|
uint8 totalAssistants = 0;
|
||||||
|
uint8 assistantsBeforePlayer = 0;
|
||||||
|
uint8 nonAssistantsBeforePlayer = 0;
|
||||||
|
bool playerFound = false;
|
||||||
|
|
||||||
// First, assistants
|
|
||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
{
|
{
|
||||||
Player* member = ref->GetSource();
|
Player* member = ref->GetSource();
|
||||||
if (!member)
|
if (!member || (ignoreDeadPlayers && !member->IsAlive()) || !IsHeal(member))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (ignoreDeadPlayers && !member->IsAlive())
|
bool isAssistant = group->IsAssistant(member->GetGUID());
|
||||||
continue;
|
|
||||||
|
|
||||||
if (group->IsAssistant(member->GetGUID()) && IsHeal(member))
|
if (isAssistant)
|
||||||
|
totalAssistants++;
|
||||||
|
|
||||||
|
if (member == player)
|
||||||
|
playerFound = true;
|
||||||
|
else if (!playerFound)
|
||||||
{
|
{
|
||||||
if (index == counter)
|
if (isAssistant)
|
||||||
return player == member;
|
assistantsBeforePlayer++;
|
||||||
counter++;
|
else
|
||||||
|
nonAssistantsBeforePlayer++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not enough assistants, get non-assistants
|
if (!playerFound)
|
||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
return false;
|
||||||
{
|
|
||||||
Player* member = ref->GetSource();
|
|
||||||
if (!member)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (ignoreDeadPlayers && !member->IsAlive())
|
// If the player is an assistant, their index is just the number of assistants before them.
|
||||||
continue;
|
// If they are a non-assistant, their index is shifted by the total number of assistants.
|
||||||
|
uint8 playerIndex = group->IsAssistant(player->GetGUID())
|
||||||
|
? assistantsBeforePlayer : (totalAssistants + nonAssistantsBeforePlayer);
|
||||||
|
|
||||||
if (!group->IsAssistant(member->GetGUID()) && IsHeal(member))
|
return playerIndex == index;
|
||||||
{
|
|
||||||
if (index == counter)
|
|
||||||
return player == member;
|
|
||||||
counter++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PlayerbotAI::IsAssistRangedDpsOfIndex(Player* player, int index, bool ignoreDeadPlayers)
|
bool PlayerbotAI::IsAssistRangedDpsOfIndex(Player* player, uint8 index, bool ignoreDeadPlayers)
|
||||||
{
|
{
|
||||||
|
if (!IsRangedDps(player))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (ignoreDeadPlayers && !player->IsAlive())
|
||||||
|
return false;
|
||||||
|
|
||||||
Group* group = player->GetGroup();
|
Group* group = player->GetGroup();
|
||||||
if (!group)
|
if (!group)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
int counter = 0;
|
uint8 totalAssistants = 0;
|
||||||
|
uint8 assistantsBeforePlayer = 0;
|
||||||
|
uint8 nonAssistantsBeforePlayer = 0;
|
||||||
|
bool playerFound = false;
|
||||||
|
|
||||||
// First, assistants
|
|
||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
{
|
{
|
||||||
Player* member = ref->GetSource();
|
Player* member = ref->GetSource();
|
||||||
if (!member)
|
if (!member || (ignoreDeadPlayers && !member->IsAlive()) || !IsRangedDps(member))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (ignoreDeadPlayers && !member->IsAlive())
|
bool isAssistant = group->IsAssistant(member->GetGUID());
|
||||||
continue;
|
|
||||||
|
|
||||||
if (group->IsAssistant(member->GetGUID()) && IsRangedDps(member))
|
if (isAssistant)
|
||||||
|
totalAssistants++;
|
||||||
|
|
||||||
|
if (member == player)
|
||||||
|
playerFound = true;
|
||||||
|
else if (!playerFound)
|
||||||
{
|
{
|
||||||
if (index == counter)
|
if (isAssistant)
|
||||||
return player == member;
|
assistantsBeforePlayer++;
|
||||||
counter++;
|
else
|
||||||
|
nonAssistantsBeforePlayer++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not enough assistants, get non-assistants
|
if (!playerFound)
|
||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
return false;
|
||||||
{
|
|
||||||
Player* member = ref->GetSource();
|
|
||||||
if (!member)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (ignoreDeadPlayers && !member->IsAlive())
|
// If the player is an assistant, their index is just the number of assistants before them.
|
||||||
continue;
|
// If they are a non-assistant, their index is shifted by the total number of assistants.
|
||||||
|
uint8 playerIndex = group->IsAssistant(player->GetGUID())
|
||||||
|
? assistantsBeforePlayer : (totalAssistants + nonAssistantsBeforePlayer);
|
||||||
|
|
||||||
if (!group->IsAssistant(member->GetGUID()) && IsRangedDps(member))
|
return playerIndex == index;
|
||||||
{
|
|
||||||
if (index == counter)
|
|
||||||
return player == member;
|
|
||||||
counter++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PlayerbotAI::HasAggro(Unit* unit)
|
bool PlayerbotAI::HasAggro(Unit* unit)
|
||||||
@@ -2226,43 +2235,44 @@ bool PlayerbotAI::IsDps(Player* player, bool bySpec)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PlayerbotAI::IsMainTank(Player* player)
|
bool PlayerbotAI::IsMainTank(Player* player, bool ignoreMemberFlag)
|
||||||
{
|
{
|
||||||
Group* group = player->GetGroup();
|
Group* group = player->GetGroup();
|
||||||
if (!group)
|
if (!group)
|
||||||
{
|
|
||||||
return IsTank(player);
|
return IsTank(player);
|
||||||
}
|
|
||||||
|
|
||||||
ObjectGuid mainTank = ObjectGuid();
|
ObjectGuid mainTank = ObjectGuid();
|
||||||
Group::MemberSlotList const& slots = group->GetMemberSlots();
|
|
||||||
|
|
||||||
for (Group::member_citerator itr = slots.begin(); itr != slots.end(); ++itr)
|
// (1) Check for main tank flag (any class or spec)
|
||||||
|
if (!ignoreMemberFlag)
|
||||||
{
|
{
|
||||||
if (itr->flags & MEMBER_FLAG_MAINTANK)
|
Group::MemberSlotList const& slots = group->GetMemberSlots();
|
||||||
|
|
||||||
|
for (Group::member_citerator itr = slots.begin(); itr != slots.end(); ++itr)
|
||||||
{
|
{
|
||||||
mainTank = itr->guid;
|
if (itr->flags & MEMBER_FLAG_MAINTANK)
|
||||||
break;
|
{
|
||||||
|
mainTank = itr->guid;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mainTank != ObjectGuid::Empty)
|
||||||
|
return player->GetGUID() == mainTank;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mainTank != ObjectGuid::Empty)
|
// (2) If no main tank flag, return the first tank
|
||||||
{
|
if (!IsTank(player) || !player->IsAlive())
|
||||||
return player->GetGUID() == mainTank;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
{
|
{
|
||||||
Player* member = ref->GetSource();
|
Player* member = ref->GetSource();
|
||||||
if (!member)
|
if (!member)
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
if (IsTank(member) && member->IsAlive())
|
if (IsTank(member) && member->IsAlive())
|
||||||
{
|
|
||||||
return player->GetGUID() == member->GetGUID();
|
return player->GetGUID() == member->GetGUID();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -2281,47 +2291,31 @@ bool PlayerbotAI::IsBotMainTank(Player* player)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (IsMainTank(player))
|
if (IsMainTank(player))
|
||||||
{
|
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
Group* group = player->GetGroup();
|
Group* group = player->GetGroup();
|
||||||
if (!group)
|
if (!group)
|
||||||
{
|
return true;
|
||||||
return true; // If no group, consider the bot as main tank
|
|
||||||
}
|
|
||||||
|
|
||||||
int32 botAssistTankIndex = GetAssistTankIndex(player);
|
int32 botAssistTankIndex = GetAssistTankIndex(player);
|
||||||
if (botAssistTankIndex == -1)
|
if (botAssistTankIndex == -1)
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
|
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
|
||||||
{
|
{
|
||||||
Player* member = gref->GetSource();
|
Player* member = gref->GetSource();
|
||||||
if (!member)
|
if (!member)
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
int32 memberAssistTankIndex = GetAssistTankIndex(member);
|
int32 memberAssistTankIndex = GetAssistTankIndex(member);
|
||||||
if (memberAssistTankIndex == -1)
|
if (memberAssistTankIndex == -1)
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
if (memberAssistTankIndex == botAssistTankIndex && player == member)
|
if (memberAssistTankIndex == botAssistTankIndex && player == member)
|
||||||
{
|
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
if (memberAssistTankIndex < botAssistTankIndex && member->GetSession()->IsBot())
|
if (memberAssistTankIndex < botAssistTankIndex && member->GetSession()->IsBot())
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -2331,73 +2325,76 @@ uint32 PlayerbotAI::GetGroupTankNum(Player* player)
|
|||||||
{
|
{
|
||||||
Group* group = player->GetGroup();
|
Group* group = player->GetGroup();
|
||||||
if (!group)
|
if (!group)
|
||||||
{
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
uint32 result = 0;
|
uint32 result = 0;
|
||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
{
|
{
|
||||||
Player* member = ref->GetSource();
|
Player* member = ref->GetSource();
|
||||||
|
|
||||||
if (!member)
|
if (!member)
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
if (IsTank(member) && member->IsAlive())
|
if (IsTank(member) && member->IsAlive())
|
||||||
{
|
|
||||||
result++;
|
result++;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PlayerbotAI::IsAssistTank(Player* player) { return IsTank(player) && !IsMainTank(player); }
|
bool PlayerbotAI::IsAssistTank(Player* player)
|
||||||
|
|
||||||
bool PlayerbotAI::IsAssistTankOfIndex(Player* player, int index, bool ignoreDeadPlayers)
|
|
||||||
{
|
{
|
||||||
|
return IsTank(player) && !IsMainTank(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PlayerbotAI::IsAssistTankOfIndex(Player* player, uint8 index, bool ignoreDeadPlayers)
|
||||||
|
{
|
||||||
|
if (!IsAssistTank(player))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (ignoreDeadPlayers && !player->IsAlive())
|
||||||
|
return false;
|
||||||
|
|
||||||
Group* group = player->GetGroup();
|
Group* group = player->GetGroup();
|
||||||
if (!group)
|
if (!group)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
int counter = 0;
|
uint8 totalAssistants = 0;
|
||||||
|
uint8 assistantsBeforePlayer = 0;
|
||||||
|
uint8 nonAssistantsBeforePlayer = 0;
|
||||||
|
bool playerFound = false;
|
||||||
|
|
||||||
// First, assistants
|
|
||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
{
|
{
|
||||||
Player* member = ref->GetSource();
|
Player* member = ref->GetSource();
|
||||||
if (!member)
|
if (!member || (ignoreDeadPlayers && !member->IsAlive()) || !IsAssistTank(member))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (ignoreDeadPlayers && !member->IsAlive())
|
bool isAssistant = group->IsAssistant(member->GetGUID());
|
||||||
continue;
|
|
||||||
|
|
||||||
if (group->IsAssistant(member->GetGUID()) && IsAssistTank(member))
|
if (isAssistant)
|
||||||
|
totalAssistants++;
|
||||||
|
|
||||||
|
if (member == player)
|
||||||
|
playerFound = true;
|
||||||
|
else if (!playerFound)
|
||||||
{
|
{
|
||||||
if (index == counter)
|
if (isAssistant)
|
||||||
return player == member;
|
assistantsBeforePlayer++;
|
||||||
counter++;
|
else
|
||||||
|
nonAssistantsBeforePlayer++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not enough assistants, get non-assistants
|
if (!playerFound)
|
||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
return false;
|
||||||
{
|
|
||||||
Player* member = ref->GetSource();
|
|
||||||
if (!member)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (ignoreDeadPlayers && !member->IsAlive())
|
// If the player is an assistant, their index is just the number of assistants before them.
|
||||||
continue;
|
// If they are a non-assistant, their index is shifted by the total number of assistants.
|
||||||
|
uint8 playerIndex = group->IsAssistant(player->GetGUID())
|
||||||
|
? assistantsBeforePlayer : (totalAssistants + nonAssistantsBeforePlayer);
|
||||||
|
|
||||||
if (!group->IsAssistant(member->GetGUID()) && IsAssistTank(member))
|
return playerIndex == index;
|
||||||
{
|
|
||||||
if (index == counter)
|
|
||||||
return player == member;
|
|
||||||
counter++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace acore
|
namespace acore
|
||||||
|
|||||||
@@ -423,12 +423,12 @@ public:
|
|||||||
static bool IsRangedDps(Player* player, bool bySpec = false);
|
static bool IsRangedDps(Player* player, bool bySpec = false);
|
||||||
static bool IsCombo(Player* player);
|
static bool IsCombo(Player* player);
|
||||||
static bool IsBotMainTank(Player* player);
|
static bool IsBotMainTank(Player* player);
|
||||||
static bool IsMainTank(Player* player);
|
static bool IsMainTank(Player* player, bool ignoreMemberFlag = false);
|
||||||
static uint32 GetGroupTankNum(Player* player);
|
static uint32 GetGroupTankNum(Player* player);
|
||||||
static bool IsAssistTank(Player* player);
|
static bool IsAssistTank(Player* player);
|
||||||
static bool IsAssistTankOfIndex(Player* player, int index, bool ignoreDeadPlayers = false);
|
static bool IsAssistTankOfIndex(Player* player, uint8 index, bool ignoreDeadPlayers = false);
|
||||||
static bool IsAssistHealOfIndex(Player* player, int index, bool ignoreDeadPlayers = false);
|
static bool IsAssistHealOfIndex(Player* player, uint8 index, bool ignoreDeadPlayers = false);
|
||||||
static bool IsAssistRangedDpsOfIndex(Player* player, int index, bool ignoreDeadPlayers = false);
|
static bool IsAssistRangedDpsOfIndex(Player* player, uint8 index, bool ignoreDeadPlayers = false);
|
||||||
bool HasAggro(Unit* unit);
|
bool HasAggro(Unit* unit);
|
||||||
static int32 GetAssistTankIndex(Player* player);
|
static int32 GetAssistTankIndex(Player* player);
|
||||||
int32 GetGroupSlotIndex(Player* player);
|
int32 GetGroupSlotIndex(Player* player);
|
||||||
|
|||||||
@@ -520,6 +520,8 @@ public:
|
|||||||
|
|
||||||
void AddPlayerbotsSecureLoginScripts();
|
void AddPlayerbotsSecureLoginScripts();
|
||||||
|
|
||||||
|
void AddSC_TempestKeepBotScripts();
|
||||||
|
|
||||||
void AddPlayerbotsScripts()
|
void AddPlayerbotsScripts()
|
||||||
{
|
{
|
||||||
new PlayerbotsDatabaseScript();
|
new PlayerbotsDatabaseScript();
|
||||||
@@ -532,4 +534,5 @@ void AddPlayerbotsScripts()
|
|||||||
AddPlayerbotsSecureLoginScripts();
|
AddPlayerbotsSecureLoginScripts();
|
||||||
AddPlayerbotsCommandscripts();
|
AddPlayerbotsCommandscripts();
|
||||||
PlayerBotsGuildValidationScript();
|
PlayerBotsGuildValidationScript();
|
||||||
|
AddSC_TempestKeepBotScripts();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user