diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 7975807f..2ef6860a 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -1459,7 +1459,7 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster) strategyName = "onyxia"; // Onyxia's Lair break; case 409: - strategyName = "mc"; // Molten Core + strategyName = "moltencore"; // Molten Core break; case 469: strategyName = "bwl"; // Blackwing Lair @@ -2247,7 +2247,7 @@ uint32 PlayerbotAI::GetGroupTankNum(Player* player) bool PlayerbotAI::IsAssistTank(Player* player) { return IsTank(player) && !IsMainTank(player); } -bool PlayerbotAI::IsAssistTankOfIndex(Player* player, int index) +bool PlayerbotAI::IsAssistTankOfIndex(Player* player, int index, bool ignoreDeadPlayers) { Group* group = player->GetGroup(); if (!group) @@ -2264,6 +2264,9 @@ bool PlayerbotAI::IsAssistTankOfIndex(Player* player, int index) continue; } + if (ignoreDeadPlayers && !member->IsAlive()) + continue; + if (group->IsAssistant(member->GetGUID()) && IsAssistTank(member)) { if (index == counter) @@ -2283,6 +2286,9 @@ bool PlayerbotAI::IsAssistTankOfIndex(Player* player, int index) continue; } + if (ignoreDeadPlayers && !member->IsAlive()) + continue; + if (!group->IsAssistant(member->GetGUID()) && IsAssistTank(member)) { if (index == counter) diff --git a/src/PlayerbotAI.h b/src/PlayerbotAI.h index 7252cd77..3b9eaab1 100644 --- a/src/PlayerbotAI.h +++ b/src/PlayerbotAI.h @@ -428,7 +428,7 @@ public: static bool IsMainTank(Player* player); static uint32 GetGroupTankNum(Player* player); static bool IsAssistTank(Player* player); - static bool IsAssistTankOfIndex(Player* player, int index); + static bool IsAssistTankOfIndex(Player* player, int index, bool ignoreDeadPlayers = false); static bool IsHealAssistantOfIndex(Player* player, int index); static bool IsRangedDpsAssistantOfIndex(Player* player, int index); bool HasAggro(Unit* unit); diff --git a/src/strategy/raids/RaidStrategyContext.h b/src/strategy/raids/RaidStrategyContext.h index f16fa725..ee2bac32 100644 --- a/src/strategy/raids/RaidStrategyContext.h +++ b/src/strategy/raids/RaidStrategyContext.h @@ -22,7 +22,7 @@ public: RaidStrategyContext() : NamedObjectContext(false, true) { creators["aq20"] = &RaidStrategyContext::aq20; - creators["mc"] = &RaidStrategyContext::mc; + creators["moltencore"] = &RaidStrategyContext::moltencore; creators["bwl"] = &RaidStrategyContext::bwl; creators["karazhan"] = &RaidStrategyContext::karazhan; creators["magtheridon"] = &RaidStrategyContext::magtheridon; @@ -38,7 +38,7 @@ public: private: static Strategy* aq20(PlayerbotAI* botAI) { return new RaidAq20Strategy(botAI); } - static Strategy* mc(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* karazhan(PlayerbotAI* botAI) { return new RaidKarazhanStrategy(botAI); } static Strategy* magtheridon(PlayerbotAI* botAI) { return new RaidMagtheridonStrategy(botAI); } diff --git a/src/strategy/raids/moltencore/RaidMcActionContext.h b/src/strategy/raids/moltencore/RaidMcActionContext.h index 45e6e056..79a4a95a 100644 --- a/src/strategy/raids/moltencore/RaidMcActionContext.h +++ b/src/strategy/raids/moltencore/RaidMcActionContext.h @@ -10,13 +10,39 @@ class RaidMcActionContext : public NamedObjectContext public: RaidMcActionContext() { - creators["mc check should move from group"] = &RaidMcActionContext::check_should_move_from_group; + creators["mc lucifron shadow resistance"] = &RaidMcActionContext::lucifron_shadow_resistance; + creators["mc magmadar fire resistance"] = &RaidMcActionContext::magmadar_fire_resistance; + creators["mc gehennas shadow resistance"] = &RaidMcActionContext::gehennas_shadow_resistance; + creators["mc garr fire resistance"] = &RaidMcActionContext::garr_fire_resistance; + creators["mc baron geddon fire resistance"] = &RaidMcActionContext::baron_geddon_fire_resistance; + creators["mc move from group"] = &RaidMcActionContext::check_should_move_from_group; creators["mc move from baron geddon"] = &RaidMcActionContext::move_from_baron_geddon; + creators["mc shazzrah move away"] = &RaidMcActionContext::shazzrah_move_away; + creators["mc sulfuron harbinger fire resistance"] = &RaidMcActionContext::sulfuron_harbinger_fire_resistance; + creators["mc golemagg fire resistance"] = &RaidMcActionContext::golemagg_fire_resistance; + creators["mc golemagg mark boss"] = &RaidMcActionContext::golemagg_mark_boss; + creators["mc golemagg main tank attack golemagg"] = &RaidMcActionContext::golemagg_main_tank_attack_golemagg; + creators["mc golemagg assist tank attack core rager"] = &RaidMcActionContext::golemagg_assist_tank_attack_core_rager; + creators["mc majordomo shadow resistance"] = &RaidMcActionContext::majordomo_shadow_resistance; + creators["mc ragnaros fire resistance"] = &RaidMcActionContext::ragnaros_fire_resistance; } private: - static Action* check_should_move_from_group(PlayerbotAI* ai) { return new McCheckShouldMoveFromGroupAction(ai); } - static Action* move_from_baron_geddon(PlayerbotAI* ai) { return new McMoveFromBaronGeddonAction(ai); } + static Action* lucifron_shadow_resistance(PlayerbotAI* botAI) { return new BossShadowResistanceAction(botAI, "lucifron"); } + static Action* magmadar_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceAction(botAI, "magmadar"); } + static Action* gehennas_shadow_resistance(PlayerbotAI* botAI) { return new BossShadowResistanceAction(botAI, "gehennas"); } + static Action* garr_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceAction(botAI, "garr"); } + static Action* baron_geddon_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceAction(botAI, "baron geddon"); } + static Action* check_should_move_from_group(PlayerbotAI* botAI) { return new McMoveFromGroupAction(botAI); } + static Action* move_from_baron_geddon(PlayerbotAI* botAI) { return new McMoveFromBaronGeddonAction(botAI); } + static Action* shazzrah_move_away(PlayerbotAI* botAI) { return new McShazzrahMoveAwayAction(botAI); } + static Action* sulfuron_harbinger_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceAction(botAI, "sulfuron harbinger"); } + static Action* golemagg_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceAction(botAI, "golemagg the incinerator"); } + static Action* golemagg_mark_boss(PlayerbotAI* botAI) { return new McGolemaggMarkBossAction(botAI); } + static Action* golemagg_main_tank_attack_golemagg(PlayerbotAI* botAI) { return new McGolemaggMainTankAttackGolemaggAction(botAI); } + static Action* golemagg_assist_tank_attack_core_rager(PlayerbotAI* botAI) { return new McGolemaggAssistTankAttackCoreRagerAction(botAI); } + static Action* majordomo_shadow_resistance(PlayerbotAI* botAI) { return new BossShadowResistanceAction(botAI, "majordomo executus"); } + static Action* ragnaros_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceAction(botAI, "ragnaros"); } }; #endif diff --git a/src/strategy/raids/moltencore/RaidMcActions.cpp b/src/strategy/raids/moltencore/RaidMcActions.cpp index b82a073c..b18c8b85 100644 --- a/src/strategy/raids/moltencore/RaidMcActions.cpp +++ b/src/strategy/raids/moltencore/RaidMcActions.cpp @@ -1,43 +1,215 @@ #include "RaidMcActions.h" #include "Playerbots.h" +#include "RtiTargetValue.h" +#include "RaidMcTriggers.h" +#include "RaidMcHelpers.h" -bool McCheckShouldMoveFromGroupAction::Execute(Event event) +static constexpr float LIVING_BOMB_DISTANCE = 20.0f; +static constexpr float INFERNO_DISTANCE = 20.0f; + +// don't get hit by Arcane Explosion but still be in casting range +static constexpr float ARCANE_EXPLOSION_DISTANCE = 26.0f; + +// dedicated tank positions; prevents assist tanks from positioning Core Ragers on steep walls on pull +static const Position GOLEMAGG_TANK_POSITION{795.7308, -994.8848, -207.18661}; +static const Position CORE_RAGER_TANK_POSITION{846.6453, -1019.0639, -198.9819}; + +static constexpr float GOLEMAGGS_TRUST_DISTANCE = 30.0f; +static constexpr float CORE_RAGER_STEP_DISTANCE = 5.0f; + +using namespace MoltenCoreHelpers; + +bool McMoveFromGroupAction::Execute(Event event) { - if (bot->HasAura(20475)) // barron geddon's living bomb - { - if (!botAI->HasStrategy("move from group", BotState::BOT_STATE_COMBAT)) - { - // add/remove from both for now as it will make it more obvious to - // player if this strat remains on after fight somehow - botAI->ChangeStrategy("+move from group", BOT_STATE_NON_COMBAT); - botAI->ChangeStrategy("+move from group", BOT_STATE_COMBAT); - return true; - } - } - else if (botAI->HasStrategy("move from group", BotState::BOT_STATE_COMBAT)) - { - // add/remove from both for now as it will make it more obvious to - // player if this strat remains on after fight somehow - botAI->ChangeStrategy("-move from group", BOT_STATE_NON_COMBAT); - botAI->ChangeStrategy("-move from group", BOT_STATE_COMBAT); - return true; - } - return false; + return MoveFromGroup(LIVING_BOMB_DISTANCE); } bool McMoveFromBaronGeddonAction::Execute(Event event) { - const float radius = 25.0f; // more than should be needed but bots keep trying to run back in if (Unit* boss = AI_VALUE2(Unit*, "find target", "baron geddon")) { - long distToTravel = radius - bot->GetDistance(boss); + float distToTravel = INFERNO_DISTANCE - bot->GetDistance2d(boss); if (distToTravel > 0) { - // float angle = bot->GetAngle(boss) + M_PI; - // return Move(angle, distToTravel); + // Stop current spell first + bot->AttackStop(); + bot->InterruptNonMeleeSpells(false); + return MoveAway(boss, distToTravel); } } return false; } + +bool McShazzrahMoveAwayAction::Execute(Event event) +{ + if (Unit* boss = AI_VALUE2(Unit*, "find target", "shazzrah")) + { + float distToTravel = ARCANE_EXPLOSION_DISTANCE - bot->GetDistance2d(boss); + if (distToTravel > 0) + return MoveAway(boss, distToTravel); + } + return false; +} + +bool McGolemaggMarkBossAction::Execute(Event event) +{ + if (Unit* boss = AI_VALUE2(Unit*, "find target", "golemagg the incinerator")) + { + if (Group* group = bot->GetGroup()) + { + ObjectGuid currentSkullGuid = group->GetTargetIcon(RtiTargetValue::skullIndex); + if (currentSkullGuid.IsEmpty() || currentSkullGuid != boss->GetGUID()) + { + group->SetTargetIcon(RtiTargetValue::skullIndex, bot->GetGUID(), boss->GetGUID()); + return true; + } + } + } + return false; +} + +bool McGolemaggTankAction::MoveUnitToPosition(Unit* target, const Position& tankPosition, float maxDistance, + float stepDistance) +{ + if (bot->GetVictim() != target) + return Attack(target); + if (target->GetVictim() == bot) + { + float distanceToTankPosition = bot->GetExactDist2d(tankPosition.GetPositionX(), tankPosition.GetPositionY()); + if (distanceToTankPosition > maxDistance) + { + float dX = tankPosition.GetPositionX() - bot->GetPositionX(); + float dY = tankPosition.GetPositionY() - bot->GetPositionY(); + float dist = sqrt(dX * dX + dY * dY); + float moveX = bot->GetPositionX() + (dX / dist) * stepDistance; + float moveY = bot->GetPositionY() + (dY / dist) * stepDistance; + return MoveTo(bot->GetMapId(), moveX, moveY, bot->GetPositionZ(), false, false, + false, false, MovementPriority::MOVEMENT_COMBAT, true, + true); + } + } + else if (botAI->DoSpecificAction("taunt spell", Event(), true)) + return true; + return false; +} + +bool McGolemaggTankAction::FindCoreRagers(Unit*& coreRager1, Unit*& coreRager2) const +{ + coreRager1 = coreRager2 = nullptr; + for (auto const& target : AI_VALUE(GuidVector, "possible targets no los")) + { + Unit* unit = botAI->GetUnit(target); + if (unit && unit->IsAlive() && unit->GetEntry() == NPC_CORE_RAGER) + { + if (coreRager1 == nullptr) + coreRager1 = unit; + else if (coreRager2 == nullptr) + { + coreRager2 = unit; + break; // There should be no third Core Rager. + } + } + } + return coreRager1 != nullptr && coreRager2 != nullptr; +} + +bool McGolemaggMainTankAttackGolemaggAction::Execute(Event event) +{ + // At this point, we know we are not the last living tank in the group. + if (Unit* boss = AI_VALUE2(Unit*, "find target", "golemagg the incinerator")) + { + Unit* coreRager1; + Unit* coreRager2; + if (!FindCoreRagers(coreRager1, coreRager2)) + return false; // safety check + + // We only need to move if the Core Ragers still have Golemagg's Trust + if (coreRager1->HasAura(SPELL_GOLEMAGGS_TRUST) || coreRager2->HasAura(SPELL_GOLEMAGGS_TRUST)) + return MoveUnitToPosition(boss, GOLEMAGG_TANK_POSITION, boss->GetCombatReach()); + } + return false; +} + +bool McGolemaggAssistTankAttackCoreRagerAction::Execute(Event event) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "golemagg the incinerator"); + if (!boss) + return false; + + // Step 0: Filter additional assist tanks. We only need 2. + bool isFirstAssistTank = PlayerbotAI::IsAssistTankOfIndex(bot, 0, true); + bool isSecondAssistTank = PlayerbotAI::IsAssistTankOfIndex(bot, 1, true); + if (!isFirstAssistTank && !isSecondAssistTank) + return Attack(boss); + + // Step 1: Find both Core Ragers + Unit* coreRager1; + Unit* coreRager2; + if (!FindCoreRagers(coreRager1, coreRager2)) + return false; // safety check + + // Step 2: Assign Core Rager to bot + Unit* myCoreRager = nullptr; + Unit* otherCoreRager = nullptr; + if (isFirstAssistTank) + { + myCoreRager = coreRager1; + otherCoreRager = coreRager2; + } + else // isSecondAssistTank is always true here + { + myCoreRager = coreRager2; + otherCoreRager = coreRager1; + } + + // Step 3: Select the right target + if (myCoreRager->GetVictim() != bot) + { + // Step 3.1: My Core Rager isn't attacking me. Attack until it does. + if (bot->GetVictim() != myCoreRager) + return Attack(myCoreRager); + return botAI->DoSpecificAction("taunt spell", event, true); + } + + Unit* otherCoreRagerVictim = otherCoreRager->GetVictim(); + if (otherCoreRagerVictim) // Core Rager victim can be NULL + { + // Step 3.2: Check if the other Core Rager isn't attacking its assist tank. + Player* otherCoreRagerPlayerVictim = otherCoreRagerVictim->ToPlayer(); + if (otherCoreRagerPlayerVictim && + !PlayerbotAI::IsAssistTankOfIndex(otherCoreRagerPlayerVictim, 0, true) && + !PlayerbotAI::IsAssistTankOfIndex(otherCoreRagerPlayerVictim, 1, true)) + { + // Assume we are the only assist tank or the other assist tank is dead => pick up other Core Rager! + if (bot->GetVictim() != otherCoreRager) + return Attack(otherCoreRager); + return botAI->DoSpecificAction("taunt spell", event, true); + } + } + + if (bot->GetVictim() != myCoreRager) + return Attack(myCoreRager); // Step 3.3: Attack our Core Rager in case we previously switched in 3.2. + + // Step 4: Prevent Golemagg's Trust on Core Ragers + if (myCoreRager->HasAura(SPELL_GOLEMAGGS_TRUST) || + (otherCoreRagerVictim == bot && otherCoreRager->HasAura(SPELL_GOLEMAGGS_TRUST))) + { + // Step 4.1: Move Core Ragers to dedicated tank position (only if Golemagg is far enough away from said position) + float bossDistanceToCoreRagerTankPosition = boss->GetExactDist2d( + CORE_RAGER_TANK_POSITION.GetPositionX(), CORE_RAGER_TANK_POSITION.GetPositionY()); + if (bossDistanceToCoreRagerTankPosition > GOLEMAGGS_TRUST_DISTANCE) + { + float distanceToTankPosition = bot->GetExactDist2d(CORE_RAGER_TANK_POSITION.GetPositionX(), + CORE_RAGER_TANK_POSITION.GetPositionY()); + if (distanceToTankPosition > CORE_RAGER_STEP_DISTANCE) + return MoveUnitToPosition(myCoreRager, CORE_RAGER_TANK_POSITION, CORE_RAGER_STEP_DISTANCE); + } + + // Step 4.2: if boss is too close to tank position, or we are already there, move away from Golemagg to try to out-range Golemagg's Trust + return MoveAway(boss, CORE_RAGER_STEP_DISTANCE, true); + } + + return false; +} diff --git a/src/strategy/raids/moltencore/RaidMcActions.h b/src/strategy/raids/moltencore/RaidMcActions.h index 6ff18513..680b311d 100644 --- a/src/strategy/raids/moltencore/RaidMcActions.h +++ b/src/strategy/raids/moltencore/RaidMcActions.h @@ -1,15 +1,16 @@ #ifndef _PLAYERBOT_RAIDMCACTIONS_H #define _PLAYERBOT_RAIDMCACTIONS_H +#include "AttackAction.h" #include "MovementActions.h" #include "PlayerbotAI.h" #include "Playerbots.h" -class McCheckShouldMoveFromGroupAction : public Action +class McMoveFromGroupAction : public MovementAction { public: - McCheckShouldMoveFromGroupAction(PlayerbotAI* botAI, std::string const name = "mc check should move from group") - : Action(botAI, name) {} + McMoveFromGroupAction(PlayerbotAI* botAI, std::string const name = "mc move from group") + : MovementAction(botAI, name) {} bool Execute(Event event) override; }; @@ -21,4 +22,46 @@ public: bool Execute(Event event) override; }; +class McShazzrahMoveAwayAction : public MovementAction +{ +public: + McShazzrahMoveAwayAction(PlayerbotAI* botAI, std::string const name = "mc shazzrah move away") + : MovementAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class McGolemaggMarkBossAction : public Action +{ +public: + McGolemaggMarkBossAction(PlayerbotAI* botAI, std::string const name = "mc golemagg mark boss") + : Action(botAI, name) {}; + bool Execute(Event event) override; +}; + +class McGolemaggTankAction : public AttackAction +{ +public: + McGolemaggTankAction(PlayerbotAI* botAI, std::string const name) + : AttackAction(botAI, name) {} +protected: + bool MoveUnitToPosition(Unit* target, const Position& tankPosition, float maxDistance, float stepDistance = 3.0f); + bool FindCoreRagers(Unit*& coreRager1, Unit*& coreRager2) const; +}; + +class McGolemaggMainTankAttackGolemaggAction : public McGolemaggTankAction +{ +public: + McGolemaggMainTankAttackGolemaggAction(PlayerbotAI* botAI, std::string const name = "mc golemagg main tank attack golemagg") + : McGolemaggTankAction(botAI, name) {}; + bool Execute(Event event) override; +}; + +class McGolemaggAssistTankAttackCoreRagerAction : public McGolemaggTankAction +{ +public: + McGolemaggAssistTankAttackCoreRagerAction(PlayerbotAI* botAI, std::string const name = "mc golemagg assist tank attack core rager") + : McGolemaggTankAction(botAI, name) {}; + bool Execute(Event event) override; +}; + #endif diff --git a/src/strategy/raids/moltencore/RaidMcHelpers.h b/src/strategy/raids/moltencore/RaidMcHelpers.h new file mode 100644 index 00000000..5dc82124 --- /dev/null +++ b/src/strategy/raids/moltencore/RaidMcHelpers.h @@ -0,0 +1,22 @@ +#ifndef _PLAYERBOT_RAIDMCHELPERS_H +#define _PLAYERBOT_RAIDMCHELPERS_H + +namespace MoltenCoreHelpers +{ +enum MoltenCoreNPCs +{ + // Golemagg + NPC_CORE_RAGER = 11672, +}; +enum MoltenCoreSpells +{ + // Baron Geddon + SPELL_INFERNO = 19695, + SPELL_LIVING_BOMB = 20475, + + // Golemagg + SPELL_GOLEMAGGS_TRUST = 20553, +}; +} + +#endif diff --git a/src/strategy/raids/moltencore/RaidMcMultipliers.cpp b/src/strategy/raids/moltencore/RaidMcMultipliers.cpp new file mode 100644 index 00000000..d1ee936b --- /dev/null +++ b/src/strategy/raids/moltencore/RaidMcMultipliers.cpp @@ -0,0 +1,117 @@ +#include "RaidMcMultipliers.h" + +#include "Playerbots.h" +#include "ChooseTargetActions.h" +#include "GenericSpellActions.h" +#include "DruidActions.h" +#include "HunterActions.h" +#include "PaladinActions.h" +#include "ShamanActions.h" +#include "WarriorActions.h" +#include "DKActions.h" +#include "RaidMcActions.h" +#include "RaidMcHelpers.h" + +using namespace MoltenCoreHelpers; + +static bool IsDpsBotWithAoeAction(Player* bot, Action* action) +{ + if (PlayerbotAI::IsDps(bot)) + { + if (dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action)) + return true; + + if (auto castSpellAction = dynamic_cast(action)) + { + if (castSpellAction->getThreatType() == Action::ActionThreatType::Aoe) + return true; + } + } + return false; +} + +float GarrDisableDpsAoeMultiplier::GetValue(Action* action) +{ + if (AI_VALUE2(Unit*, "find target", "garr")) + { + if (IsDpsBotWithAoeAction(bot, action)) + return 0.0f; + } + return 1.0f; +} + +static bool IsAllowedGeddonMovementAction(Action* action) +{ + if (dynamic_cast(action) && + !dynamic_cast(action) && + !dynamic_cast(action)) + return false; + + if (dynamic_cast(action)) + return false; + + return true; +} + +float BaronGeddonAbilityMultiplier::GetValue(Action* action) +{ + if (Unit* boss = AI_VALUE2(Unit*, "find target", "baron geddon")) + { + if (boss->HasAura(SPELL_INFERNO)) + { + if (!IsAllowedGeddonMovementAction(action)) + return 0.0f; + } + } + + // No check for Baron Geddon, because bots may have the bomb even after Geddon died. + if (bot->HasAura(SPELL_LIVING_BOMB)) + { + if (!IsAllowedGeddonMovementAction(action)) + return 0.0f; + } + + return 1.0f; +} + +static bool IsSingleLivingTankInGroup(Player* bot) +{ + if (Group* group = bot->GetGroup()) + { + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (!member || !member->IsAlive() || member == bot) + continue; + if (PlayerbotAI::IsTank(member)) + return false; + } + } + return true; +} + +float GolemaggMultiplier::GetValue(Action* action) +{ + if (AI_VALUE2(Unit*, "find target", "golemagg the incinerator")) + { + if (PlayerbotAI::IsTank(bot) && IsSingleLivingTankInGroup(bot)) + { + // Only one tank => Pick up Golemagg and the two Core Ragers + if (dynamic_cast(action) || + dynamic_cast(action)) + return 0.0f; + } + if (PlayerbotAI::IsAssistTank(bot)) + { + // The first two assist tanks manage the Core Ragers. The remaining assist tanks attack the boss. + if (dynamic_cast(action)) + return 0.0f; + } + if (IsDpsBotWithAoeAction(bot, action)) + return 0.0f; + } + return 1.0f; +} diff --git a/src/strategy/raids/moltencore/RaidMcMultipliers.h b/src/strategy/raids/moltencore/RaidMcMultipliers.h new file mode 100644 index 00000000..56dc7a5f --- /dev/null +++ b/src/strategy/raids/moltencore/RaidMcMultipliers.h @@ -0,0 +1,27 @@ +#ifndef _PLAYERBOT_RAIDMCMULTIPLIERS_H +#define _PLAYERBOT_RAIDMCMULTIPLIERS_H + +#include "Multiplier.h" + +class GarrDisableDpsAoeMultiplier : public Multiplier +{ +public: + GarrDisableDpsAoeMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "garr disable dps aoe multiplier") {} + float GetValue(Action* action) override; +}; + +class BaronGeddonAbilityMultiplier : public Multiplier +{ +public: + BaronGeddonAbilityMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "baron geddon ability multiplier") {} + float GetValue(Action* action) override; +}; + +class GolemaggMultiplier : public Multiplier +{ +public: + GolemaggMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "golemagg multiplier") {} + float GetValue(Action* action) override; +}; + +#endif diff --git a/src/strategy/raids/moltencore/RaidMcStrategy.cpp b/src/strategy/raids/moltencore/RaidMcStrategy.cpp index 67793de9..c6a5042e 100644 --- a/src/strategy/raids/moltencore/RaidMcStrategy.cpp +++ b/src/strategy/raids/moltencore/RaidMcStrategy.cpp @@ -1,13 +1,81 @@ #include "RaidMcStrategy.h" +#include "RaidMcMultipliers.h" #include "Strategy.h" void RaidMcStrategy::InitTriggers(std::vector& triggers) { + // Lucifron + triggers.push_back( + new TriggerNode("mc lucifron shadow resistance", + NextAction::array(0, new NextAction("mc lucifron shadow resistance", ACTION_RAID), nullptr))); + + // Magmadar + // TODO: Fear ward / tremor totem, or general anti-fear strat development. Same as King Dred (Drak'Tharon) and faction commander (Nexus). + triggers.push_back( + new TriggerNode("mc magmadar fire resistance", + NextAction::array(0, new NextAction("mc magmadar fire resistance", ACTION_RAID), nullptr))); + + // Gehennas + triggers.push_back( + new TriggerNode("mc gehennas shadow resistance", + NextAction::array(0, new NextAction("mc gehennas shadow resistance", ACTION_RAID), nullptr))); + + // Garr + triggers.push_back( + new TriggerNode("mc garr fire resistance", + NextAction::array(0, new NextAction("mc garr fire resistance", ACTION_RAID), nullptr))); + + // Baron Geddon + triggers.push_back( + new TriggerNode("mc baron geddon fire resistance", + NextAction::array(0, new NextAction("mc baron geddon fire resistance", ACTION_RAID), nullptr))); triggers.push_back( new TriggerNode("mc living bomb debuff", - NextAction::array(0, new NextAction("mc check should move from group", ACTION_RAID), nullptr))); + NextAction::array(0, new NextAction("mc move from group", ACTION_RAID), nullptr))); triggers.push_back( new TriggerNode("mc baron geddon inferno", NextAction::array(0, new NextAction("mc move from baron geddon", ACTION_RAID), nullptr))); + + // Shazzrah + triggers.push_back( + new TriggerNode("mc shazzrah ranged", + NextAction::array(0, new NextAction("mc shazzrah move away", ACTION_RAID), nullptr))); + + // Sulfuron Harbinger + // Alternatively, shadow resistance is also possible. + triggers.push_back( + new TriggerNode("mc sulfuron harbinger fire resistance", + NextAction::array(0, new NextAction("mc sulfuron harbinger fire resistance", ACTION_RAID), nullptr))); + + // Golemagg the Incinerator + triggers.push_back( + new TriggerNode("mc golemagg fire resistance", + NextAction::array(0, new NextAction("mc golemagg fire resistance", ACTION_RAID), nullptr))); + triggers.push_back( + new TriggerNode("mc golemagg mark boss", + NextAction::array(0, new NextAction("mc golemagg mark boss", ACTION_RAID), nullptr))); + triggers.push_back( + new TriggerNode("mc golemagg is main tank", + NextAction::array(0, new NextAction("mc golemagg main tank attack golemagg", ACTION_RAID), nullptr))); + triggers.push_back( + new TriggerNode("mc golemagg is assist tank", + NextAction::array(0, new NextAction("mc golemagg assist tank attack core rager", ACTION_RAID), nullptr))); + + // Majordomo Executus + triggers.push_back( + new TriggerNode("mc majordomo shadow resistance", + NextAction::array(0, new NextAction("mc majordomo shadow resistance", ACTION_RAID), nullptr))); + + // Ragnaros + triggers.push_back( + new TriggerNode("mc ragnaros fire resistance", + NextAction::array(0, new NextAction("mc ragnaros fire resistance", ACTION_RAID), nullptr))); +} + +void RaidMcStrategy::InitMultipliers(std::vector& multipliers) +{ + multipliers.push_back(new GarrDisableDpsAoeMultiplier(botAI)); + multipliers.push_back(new BaronGeddonAbilityMultiplier(botAI)); + multipliers.push_back(new GolemaggMultiplier(botAI)); } diff --git a/src/strategy/raids/moltencore/RaidMcStrategy.h b/src/strategy/raids/moltencore/RaidMcStrategy.h index 82b7d8f2..45b503e9 100644 --- a/src/strategy/raids/moltencore/RaidMcStrategy.h +++ b/src/strategy/raids/moltencore/RaidMcStrategy.h @@ -8,10 +8,10 @@ class RaidMcStrategy : public Strategy { public: - RaidMcStrategy(PlayerbotAI* ai) : Strategy(ai) {} - virtual std::string const getName() override { return "mc"; } - virtual void InitTriggers(std::vector& triggers) override; - // virtual void InitMultipliers(std::vector &multipliers) override; + RaidMcStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} + std::string const getName() override { return "moltencore"; } + void InitTriggers(std::vector& triggers) override; + void InitMultipliers(std::vector &multipliers) override; }; #endif diff --git a/src/strategy/raids/moltencore/RaidMcTriggerContext.h b/src/strategy/raids/moltencore/RaidMcTriggerContext.h index 93fe6df0..b7495891 100644 --- a/src/strategy/raids/moltencore/RaidMcTriggerContext.h +++ b/src/strategy/raids/moltencore/RaidMcTriggerContext.h @@ -10,13 +10,39 @@ class RaidMcTriggerContext : public NamedObjectContext public: RaidMcTriggerContext() { + creators["mc lucifron shadow resistance"] = &RaidMcTriggerContext::lucifron_shadow_resistance; + creators["mc magmadar fire resistance"] = &RaidMcTriggerContext::magmadar_fire_resistance; + creators["mc gehennas shadow resistance"] = &RaidMcTriggerContext::gehennas_shadow_resistance; + creators["mc garr fire resistance"] = &RaidMcTriggerContext::garr_fire_resistance; + creators["mc baron geddon fire resistance"] = &RaidMcTriggerContext::baron_geddon_fire_resistance; creators["mc living bomb debuff"] = &RaidMcTriggerContext::living_bomb_debuff; creators["mc baron geddon inferno"] = &RaidMcTriggerContext::baron_geddon_inferno; + creators["mc shazzrah ranged"] = &RaidMcTriggerContext::shazzrah_ranged; + creators["mc sulfuron harbinger fire resistance"] = &RaidMcTriggerContext::sulfuron_harbinger_fire_resistance; + creators["mc golemagg fire resistance"] = &RaidMcTriggerContext::golemagg_fire_resistance; + creators["mc golemagg mark boss"] = &RaidMcTriggerContext::golemagg_mark_boss; + creators["mc golemagg is main tank"] = &RaidMcTriggerContext::golemagg_is_main_tank; + creators["mc golemagg is assist tank"] = &RaidMcTriggerContext::golemagg_is_assist_tank; + creators["mc majordomo shadow resistance"] = &RaidMcTriggerContext::majordomo_shadow_resistance; + creators["mc ragnaros fire resistance"] = &RaidMcTriggerContext::ragnaros_fire_resistance; } private: - static Trigger* living_bomb_debuff(PlayerbotAI* ai) { return new McLivingBombDebuffTrigger(ai); } - static Trigger* baron_geddon_inferno(PlayerbotAI* ai) { return new McBaronGeddonInfernoTrigger(ai); } + static Trigger* lucifron_shadow_resistance(PlayerbotAI* botAI) { return new BossShadowResistanceTrigger(botAI, "lucifron"); } + static Trigger* magmadar_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceTrigger(botAI, "magmadar"); } + static Trigger* gehennas_shadow_resistance(PlayerbotAI* botAI) { return new BossShadowResistanceTrigger(botAI, "gehennas"); } + static Trigger* garr_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceTrigger(botAI, "garr"); } + static Trigger* baron_geddon_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceTrigger(botAI, "baron geddon"); } + static Trigger* living_bomb_debuff(PlayerbotAI* botAI) { return new McLivingBombDebuffTrigger(botAI); } + static Trigger* baron_geddon_inferno(PlayerbotAI* botAI) { return new McBaronGeddonInfernoTrigger(botAI); } + static Trigger* shazzrah_ranged(PlayerbotAI* botAI) { return new McShazzrahRangedTrigger(botAI); } + static Trigger* sulfuron_harbinger_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceTrigger(botAI, "sulfuron harbinger"); } + static Trigger* golemagg_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceTrigger(botAI, "golemagg the incinerator"); } + static Trigger* golemagg_mark_boss(PlayerbotAI* botAI) { return new McGolemaggMarkBossTrigger(botAI); } + static Trigger* golemagg_is_main_tank(PlayerbotAI* botAI) { return new McGolemaggIsMainTankTrigger(botAI); } + static Trigger* golemagg_is_assist_tank(PlayerbotAI* botAI) { return new McGolemaggIsAssistTankTrigger(botAI); } + static Trigger* majordomo_shadow_resistance(PlayerbotAI* botAI) { return new BossShadowResistanceTrigger(botAI, "majordomo executus"); } + static Trigger* ragnaros_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceTrigger(botAI, "ragnaros"); } }; #endif diff --git a/src/strategy/raids/moltencore/RaidMcTriggers.cpp b/src/strategy/raids/moltencore/RaidMcTriggers.cpp index f77b70f2..834d703d 100644 --- a/src/strategy/raids/moltencore/RaidMcTriggers.cpp +++ b/src/strategy/raids/moltencore/RaidMcTriggers.cpp @@ -1,22 +1,40 @@ #include "RaidMcTriggers.h" #include "SharedDefines.h" +#include "RaidMcHelpers.h" + +using namespace MoltenCoreHelpers; bool McLivingBombDebuffTrigger::IsActive() { - // if bot has barron geddon's living bomb, we need to add strat, otherwise we need to remove - // only do when fighting baron geddon (to avoid modifying strat set by player outside this fight) - if (Unit* boss = AI_VALUE2(Unit*, "find target", "baron geddon")) - { - if (boss->IsInCombat()) - return bot->HasAura(20475) != botAI->HasStrategy("move from group", BotState::BOT_STATE_COMBAT); - } - return false; + // No check for Baron Geddon, because bots may have the bomb even after Geddon died. + return bot->HasAura(SPELL_LIVING_BOMB); } bool McBaronGeddonInfernoTrigger::IsActive() { if (Unit* boss = AI_VALUE2(Unit*, "find target", "baron geddon")) - return boss->HasAura(19695); + return boss->HasAura(SPELL_INFERNO); return false; } + +bool McShazzrahRangedTrigger::IsActive() +{ + return AI_VALUE2(Unit*, "find target", "shazzrah") && PlayerbotAI::IsRanged(bot); +} + +bool McGolemaggMarkBossTrigger::IsActive() +{ + // any tank may mark the boss + return AI_VALUE2(Unit*, "find target", "golemagg the incinerator") && PlayerbotAI::IsTank(bot); +} + +bool McGolemaggIsMainTankTrigger::IsActive() +{ + return AI_VALUE2(Unit*, "find target", "golemagg the incinerator") && PlayerbotAI::IsMainTank(bot); +} + +bool McGolemaggIsAssistTankTrigger::IsActive() +{ + return AI_VALUE2(Unit*, "find target", "golemagg the incinerator") && PlayerbotAI::IsAssistTank(bot); +} diff --git a/src/strategy/raids/moltencore/RaidMcTriggers.h b/src/strategy/raids/moltencore/RaidMcTriggers.h index 9d2fb985..4cd84a2d 100644 --- a/src/strategy/raids/moltencore/RaidMcTriggers.h +++ b/src/strategy/raids/moltencore/RaidMcTriggers.h @@ -19,4 +19,32 @@ public: bool IsActive() override; }; +class McShazzrahRangedTrigger : public Trigger +{ +public: + McShazzrahRangedTrigger(PlayerbotAI* botAI) : Trigger(botAI, "mc shazzrah ranged") {} + bool IsActive() override; +}; + +class McGolemaggMarkBossTrigger : public Trigger +{ +public: + McGolemaggMarkBossTrigger(PlayerbotAI* botAI) : Trigger(botAI, "mc golemagg mark boss") {} + bool IsActive() override; +}; + +class McGolemaggIsMainTankTrigger : public Trigger +{ +public: + McGolemaggIsMainTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "mc golemagg is main tank") {} + bool IsActive() override; +}; + +class McGolemaggIsAssistTankTrigger : public Trigger +{ +public: + McGolemaggIsAssistTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "mc golemagg is assist tank") {} + bool IsActive() override; +}; + #endif