diff --git a/src/Ai/Raid/RaidStrategyContext.h b/src/Ai/Raid/RaidStrategyContext.h index d117c459e..970ecf4aa 100644 --- a/src/Ai/Raid/RaidStrategyContext.h +++ b/src/Ai/Raid/RaidStrategyContext.h @@ -11,6 +11,7 @@ #include "RaidNaxxStrategy.h" #include "RaidSSCStrategy.h" #include "RaidTempestKeepStrategy.h" +#include "RaidZulAmanStrategy.h" #include "RaidOsStrategy.h" #include "RaidEoEStrategy.h" #include "RaidVoAStrategy.h" @@ -32,6 +33,7 @@ public: creators["naxx"] = &RaidStrategyContext::naxx; creators["ssc"] = &RaidStrategyContext::ssc; creators["tempestkeep"] = &RaidStrategyContext::tempestkeep; + creators["zulaman"] = &RaidStrategyContext::zulaman; creators["wotlk-os"] = &RaidStrategyContext::wotlk_os; creators["wotlk-eoe"] = &RaidStrategyContext::wotlk_eoe; creators["voa"] = &RaidStrategyContext::voa; @@ -50,6 +52,7 @@ private: static Strategy* naxx(PlayerbotAI* botAI) { return new RaidNaxxStrategy(botAI); } static Strategy* ssc(PlayerbotAI* botAI) { return new RaidSSCStrategy(botAI); } static Strategy* tempestkeep(PlayerbotAI* botAI) { return new RaidTempestKeepStrategy(botAI); } + static Strategy* zulaman(PlayerbotAI* botAI) { return new RaidZulAmanStrategy(botAI); } static Strategy* wotlk_os(PlayerbotAI* botAI) { return new RaidOsStrategy(botAI); } static Strategy* wotlk_eoe(PlayerbotAI* botAI) { return new RaidEoEStrategy(botAI); } static Strategy* voa(PlayerbotAI* botAI) { return new RaidVoAStrategy(botAI); } diff --git a/src/Ai/Raid/TempestKeep/RaidTempestKeepActionContext.h b/src/Ai/Raid/TempestKeep/RaidTempestKeepActionContext.h index 50b2de1f4..125b27c0f 100644 --- a/src/Ai/Raid/TempestKeep/RaidTempestKeepActionContext.h +++ b/src/Ai/Raid/TempestKeep/RaidTempestKeepActionContext.h @@ -116,8 +116,8 @@ public: 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 move devastation away"] = + &RaidTempestKeepActionContext::kaelthas_sunstrider_move_devastation_away; creators["kael'thas sunstrider loot legendary weapons"] = &RaidTempestKeepActionContext::kaelthas_sunstrider_loot_legendary_weapons; @@ -255,7 +255,7 @@ private: static Action* kaelthas_sunstrider_assign_legendary_weapon_dps_priority( PlayerbotAI* botAI) { return new KaelthasSunstriderAssignLegendaryWeaponDpsPriorityAction(botAI); } - static Action* kaelthas_sunstrider_main_tank_move_devastation_away( + static Action* kaelthas_sunstrider_move_devastation_away( PlayerbotAI* botAI) { return new KaelthasSunstriderMoveDevastationAwayAction(botAI); } static Action* kaelthas_sunstrider_loot_legendary_weapons( diff --git a/src/Ai/Raid/TempestKeep/Strategy/RaidTempestKeepStrategy.cpp b/src/Ai/Raid/TempestKeep/Strategy/RaidTempestKeepStrategy.cpp index 74950c3ad..627d3d7ab 100644 --- a/src/Ai/Raid/TempestKeep/Strategy/RaidTempestKeepStrategy.cpp +++ b/src/Ai/Raid/TempestKeep/Strategy/RaidTempestKeepStrategy.cpp @@ -105,7 +105,7 @@ void RaidTempestKeepStrategy::InitTriggers(std::vector& triggers) 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) })); + NextAction("kael'thas sunstrider 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) })); diff --git a/src/Ai/Raid/ZulAman/Action/RaidZulAmanActions.cpp b/src/Ai/Raid/ZulAman/Action/RaidZulAmanActions.cpp new file mode 100644 index 000000000..7ff3d64f2 --- /dev/null +++ b/src/Ai/Raid/ZulAman/Action/RaidZulAmanActions.cpp @@ -0,0 +1,745 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include "RaidZulAmanActions.h" +#include "RaidZulAmanHelpers.h" +#include "Playerbots.h" +#include "RaidBossHelpers.h" + +using namespace ZulAmanHelpers; + +// Trash + +bool AmanishiMedicineManMarkWardAction::Execute(Event /*event*/) +{ + if (Unit* protectiveWard = GetFirstAliveUnitByEntry( + botAI, static_cast(ZulAmanNPCs::NPC_AMANI_PROTECTIVE_WARD))) + { + MarkTargetWithSkull(bot, protectiveWard); + } + else if (Unit* healingWard = GetFirstAliveUnitByEntry( + botAI, static_cast(ZulAmanNPCs::NPC_AMANI_HEALING_WARD))) + { + MarkTargetWithSkull(bot, healingWard); + } + + return false; +} + +// Akil'zon + +bool AkilzonMisdirectBossToMainTankAction::Execute(Event /*event*/) +{ + Unit* akilzon = AI_VALUE2(Unit*, "find target", "akil'zon"); + if (!akilzon) + return false; + + Player* mainTank = GetGroupMainTank(botAI, bot); + if (!mainTank) + return false; + + if (botAI->CanCastSpell("misdirection", mainTank)) + return botAI->CastSpell("misdirection", mainTank); + + if (bot->HasAura(static_cast(ZulAmanSpells::SPELL_MISDIRECTION)) && + botAI->CanCastSpell("steady shot", akilzon)) + return botAI->CastSpell("steady shot", akilzon); + + return false; +} + +bool AkilzonTanksPositionBossAction::Execute(Event /*event*/) +{ + Unit* akilzon = AI_VALUE2(Unit*, "find target", "akil'zon"); + if (!akilzon) + return false; + + if (bot->GetVictim() != akilzon) + return Attack(akilzon); + + if (akilzon->GetVictim() == bot) + { + const Position& position = AKILZON_TANK_POSITION; + float distToPosition = + bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()); + + if (distToPosition > 2.0f) + { + float dX = position.GetPositionX() - bot->GetPositionX(); + float dY = position.GetPositionY() - bot->GetPositionY(); + float moveDist = std::min(10.0f, distToPosition); + float moveX = bot->GetPositionX() + (dX / distToPosition) * moveDist; + float moveY = bot->GetPositionY() + (dY / distToPosition) * moveDist; + + return MoveTo(ZULAMAN_MAP_ID, moveX, moveY, bot->GetPositionZ(), false, false, + false, false, MovementPriority::MOVEMENT_COMBAT, true, false); + } + } + + return false; +} + +bool AkilzonSpreadRangedAction::Execute(Event /*event*/) +{ + constexpr float minDistance = 13.0f; + constexpr uint32 minInterval = 1000; + if (Unit* nearestPlayer = GetNearestPlayerInRadius(bot, minDistance)) + return FleePosition(nearestPlayer->GetPosition(), minDistance, minInterval); + + return false; +} + +bool AkilzonMoveToEyeOfTheStormAction::Execute(Event /*event*/) +{ + Player* target = GetElectricalStormTarget(bot); + if (!target && !botAI->IsMainTank(bot)) + target = GetGroupMainTank(botAI, bot); + + if (target && bot->GetExactDist2d(target) > 2.0f) + { + botAI->Reset(); + return MoveTo(ZULAMAN_MAP_ID, target->GetPositionX(), target->GetPositionY(), + bot->GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_FORCED, true, false); + } + + return false; +} + +bool AkilzonManageElectricalStormTimerAction::Execute(Event /*event*/) +{ + const time_t now = std::time(nullptr); + const uint32 instanceId = bot->GetMap()->GetInstanceId(); + + Unit* akilzon = AI_VALUE2(Unit*, "find target", "akil'zon"); + if (akilzon) + { + auto [it, inserted] = akilzonStormTimer.try_emplace(instanceId, now); + return inserted; + } + else if (!bot->IsInCombat() && !akilzon && akilzonStormTimer.erase(instanceId) > 0) + { + return true; + } + + return false; +} + +// Nalorakk + +bool NalorakkMisdirectBossToMainTankAction::Execute(Event /*event*/) +{ + Unit* nalorakk = AI_VALUE2(Unit*, "find target", "nalorakk"); + if (!nalorakk) + return false; + + Player* mainTank = GetGroupMainTank(botAI, bot); + if (!mainTank) + return false; + + if (botAI->CanCastSpell("misdirection", mainTank)) + return botAI->CastSpell("misdirection", mainTank); + + if (bot->HasAura(static_cast(ZulAmanSpells::SPELL_MISDIRECTION)) && + botAI->CanCastSpell("steady shot", nalorakk)) + return botAI->CastSpell("steady shot", nalorakk); + + return false; +} + +bool NalorakkTanksPositionBossAction::Execute(Event /*event*/) +{ + if (!botAI->IsMainTank(bot) && !botAI->IsAssistTankOfIndex(bot, 0, true)) + return false; + + Unit* nalorakk = AI_VALUE2(Unit*, "find target", "nalorakk"); + if (!nalorakk) + return false; + + if (botAI->IsMainTank(bot)) + return MainTankPositionTrollForm(nalorakk); + else + return FirstAssistTankPositionBearForm(nalorakk); +} + +bool NalorakkTanksPositionBossAction::MainTankPositionTrollForm(Unit* nalorakk) +{ + if (!nalorakk->HasAura(static_cast(ZulAmanSpells::SPELL_BEARFORM))) + { + if (bot->GetVictim() != nalorakk) + return Attack(nalorakk); + + if (nalorakk->GetVictim() != bot) + return botAI->DoSpecificAction("taunt spell", Event(), true); + } + + const Position& position = NALORAKK_TANK_POSITION; + float distToPosition = + bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()); + + if (distToPosition > 2.0f) + { + float dX = position.GetPositionX() - bot->GetPositionX(); + float dY = position.GetPositionY() - bot->GetPositionY(); + float moveDist = std::min(10.0f, distToPosition); + float moveX = bot->GetPositionX() + (dX / distToPosition) * moveDist; + float moveY = bot->GetPositionY() + (dY / distToPosition) * moveDist; + + return MoveTo(ZULAMAN_MAP_ID, moveX, moveY, bot->GetPositionZ(), false, false, + false, false, MovementPriority::MOVEMENT_COMBAT, true, false); + } + + return false; +} + +bool NalorakkTanksPositionBossAction::FirstAssistTankPositionBearForm(Unit* nalorakk) +{ + if (nalorakk->HasAura(static_cast(ZulAmanSpells::SPELL_BEARFORM))) + { + if (bot->GetVictim() != nalorakk) + return Attack(nalorakk); + + if (nalorakk->GetVictim() != bot) + return botAI->DoSpecificAction("taunt spell", Event(), true); + } + + const Position& position = NALORAKK_TANK_POSITION; + float distToPosition = + bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()); + + if (distToPosition > 2.0f) + { + float dX = position.GetPositionX() - bot->GetPositionX(); + float dY = position.GetPositionY() - bot->GetPositionY(); + float moveDist = std::min(10.0f, distToPosition); + float moveX = bot->GetPositionX() + (dX / distToPosition) * moveDist; + float moveY = bot->GetPositionY() + (dY / distToPosition) * moveDist; + + return MoveTo(ZULAMAN_MAP_ID, moveX, moveY, bot->GetPositionZ(), false, false, + false, false, MovementPriority::MOVEMENT_COMBAT, true, false); + } + + return false; +} + +bool NalorakkSpreadRangedAction::Execute(Event /*event*/) +{ + constexpr float minDistance = 11.0f; + constexpr uint32 minInterval = 1000; + if (Unit* nearestPlayer = GetNearestPlayerInRadius(bot, minDistance)) + return FleePosition(nearestPlayer->GetPosition(), minDistance, minInterval); + + return false; +} + +// Jan'alai + +bool JanalaiMisdirectBossToMainTankAction::Execute(Event /*event*/) +{ + Unit* janalai = AI_VALUE2(Unit*, "find target", "jan'alai"); + if (!janalai) + return false; + + Player* mainTank = GetGroupMainTank(botAI, bot); + if (!mainTank) + return false; + + if (botAI->CanCastSpell("misdirection", mainTank)) + return botAI->CastSpell("misdirection", mainTank); + + if (bot->HasAura(static_cast(ZulAmanSpells::SPELL_MISDIRECTION)) && + botAI->CanCastSpell("steady shot", janalai)) + return botAI->CastSpell("steady shot", janalai); + + return false; +} + +bool JanalaiTanksPositionBossAction::Execute(Event /*event*/) +{ + Unit* janalai = AI_VALUE2(Unit*, "find target", "jan'alai"); + if (!janalai) + return false; + + if (bot->GetVictim() != janalai) + return Attack(janalai); + + if (janalai->GetVictim() == bot) + { + const Position& position = JANALAI_TANK_POSITION; + float distToPosition = + bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()); + + if (distToPosition > 2.0f) + { + float dX = position.GetPositionX() - bot->GetPositionX(); + float dY = position.GetPositionY() - bot->GetPositionY(); + float moveDist = std::min(10.0f, distToPosition); + float moveX = bot->GetPositionX() + (dX / distToPosition) * moveDist; + float moveY = bot->GetPositionY() + (dY / distToPosition) * moveDist; + + return MoveTo(ZULAMAN_MAP_ID, moveX, moveY, bot->GetPositionZ(), false, false, + false, false, MovementPriority::MOVEMENT_COMBAT, true, false); + } + } + + return false; +} + +bool JanalaiSpreadRangedInCircleAction::Execute(Event /*event*/) +{ + Group* group = bot->GetGroup(); + if (!group) + return false; + + std::vector rangedMembers; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !botAI->IsRanged(member)) + continue; + + rangedMembers.push_back(member); + } + + if (rangedMembers.empty()) + return false; + + auto findIt = std::find(rangedMembers.begin(), rangedMembers.end(), bot); + size_t botIndex = + (findIt != rangedMembers.end()) ? std::distance(rangedMembers.begin(), findIt) : 0; + size_t count = rangedMembers.size(); + if (count == 0) + return false; + + constexpr float radius = 15.0f; + float angle = (count == 1) ? 0.0f : + (2.0f * M_PI * static_cast(botIndex) / static_cast(count)); + + float targetX = JANALAI_TANK_POSITION.GetPositionX() + radius * std::cos(angle); + float targetY = JANALAI_TANK_POSITION.GetPositionY() + radius * std::sin(angle); + + if (bot->GetExactDist2d(targetX, targetY) > 2.0f) + { + return MoveTo(ZULAMAN_MAP_ID, targetX, targetY, bot->GetPositionZ(), false, false, + false, false, MovementPriority::MOVEMENT_COMBAT, true, false); + } + + return false; +} + +bool JanalaiAvoidFireBombsAction::Execute(Event /*event*/) +{ + auto const& bombs = GetAllHazardTriggers( + bot, static_cast(ZulAmanNPCs::NPC_FIRE_BOMB), 50.0f); + + if (bombs.empty()) + return false; + + constexpr float hazardRadius = 5.0f; + bool inDanger = false; + for (Unit* bomb : bombs) + { + if (bot->GetDistance2d(bomb) < hazardRadius) + { + inDanger = true; + break; + } + } + + if (!inDanger) + return false; + + const Position& janalaiCenter = JANALAI_TANK_POSITION; + constexpr float safeZoneRadius = 17.0f; + + Position safestPos = + FindSafestNearbyPosition(bot, bombs, janalaiCenter, safeZoneRadius, hazardRadius, false); + + bot->AttackStop(); + bot->InterruptNonMeleeSpells(true); + return MoveTo(ZULAMAN_MAP_ID, safestPos.GetPositionX(), safestPos.GetPositionY(), + bot->GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_FORCED, true, false); +} + +bool JanalaiMarkAmanishiHatchersAction::Execute(Event /*event*/) +{ + auto [hatcherLow, hatcherHigh] = GetAmanishiHatcherPair(botAI); + + if (hatcherLow && hatcherHigh && hatcherHigh != hatcherLow) + { + MarkTargetWithSkull(bot, hatcherLow); + MarkTargetWithMoon(bot, hatcherHigh); + SetRtiTarget(botAI, "skull", hatcherLow); + } + + return false; +} + +// Halazzi + +bool HalazziMisdirectBossToMainTankAction::Execute(Event /*event*/) +{ + Unit* halazzi = AI_VALUE2(Unit*, "find target", "halazzi"); + if (!halazzi) + return false; + + Player* mainTank = GetGroupMainTank(botAI, bot); + if (!mainTank) + return false; + + if (botAI->CanCastSpell("misdirection", mainTank)) + return botAI->CastSpell("misdirection", mainTank); + + if (bot->HasAura(static_cast(ZulAmanSpells::SPELL_MISDIRECTION)) && + botAI->CanCastSpell("steady shot", halazzi)) + return botAI->CastSpell("steady shot", halazzi); + + return false; +} + +bool HalazziMainTankPositionBossAction::Execute(Event /*event*/) +{ + Unit* halazzi = AI_VALUE2(Unit*, "find target", "halazzi"); + if (!halazzi) + return false; + + MarkTargetWithStar(bot, halazzi); + SetRtiTarget(botAI, "star", halazzi); + + if (bot->GetVictim() != halazzi) + return Attack(halazzi); + + if (halazzi->GetVictim() == bot) + { + const Position& position = HALAZZI_TANK_POSITION; + float distToPosition = + bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()); + + if (distToPosition > 2.0f) + { + float dX = position.GetPositionX() - bot->GetPositionX(); + float dY = position.GetPositionY() - bot->GetPositionY(); + float moveDist = std::min(10.0f, distToPosition); + float moveX = bot->GetPositionX() + (dX / distToPosition) * moveDist; + float moveY = bot->GetPositionY() + (dY / distToPosition) * moveDist; + + return MoveTo(ZULAMAN_MAP_ID, moveX, moveY, bot->GetPositionZ(), false, false, + false, false, MovementPriority::MOVEMENT_COMBAT, true, false); + } + } + + return false; +} + +bool HalazziFirstAssistTankAttackSpiritLynxAction::Execute(Event /*event*/) +{ + bool targetFound = false; + + if (Unit* lynx = AI_VALUE2(Unit*, "find target", "spirit of the lynx")) + { + MarkTargetWithCircle(bot, lynx); + SetRtiTarget(botAI, "circle", lynx); + + if (bot->GetVictim() != lynx) + return Attack(lynx); + + if (lynx->GetVictim() != bot) + return botAI->DoSpecificAction("taunt spell", Event(), true); + + targetFound = true; + } + else if (Unit* halazzi = AI_VALUE2(Unit*, "find target", "halazzi")) + { + SetRtiTarget(botAI, "star", halazzi); + + if (bot->GetVictim() != halazzi) + return Attack(halazzi); + + targetFound = true; + } + + if (!targetFound) + return false; + + const Position& position = HALAZZI_TANK_POSITION; + float distToPosition = + bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()); + + if (distToPosition > 2.0f) + { + float dX = position.GetPositionX() - bot->GetPositionX(); + float dY = position.GetPositionY() - bot->GetPositionY(); + float moveDist = std::min(10.0f, distToPosition); + float moveX = bot->GetPositionX() + (dX / distToPosition) * moveDist; + float moveY = bot->GetPositionY() + (dY / distToPosition) * moveDist; + + return MoveTo(ZULAMAN_MAP_ID, moveX, moveY, bot->GetPositionZ(), false, false, + false, false, MovementPriority::MOVEMENT_COMBAT, true, false); + } + + return false; +} + +bool HalazziAssignDpsPriorityAction::Execute(Event /*event*/) +{ + // Target priority 1: Corrupted Lightning Totems + if (Unit* totem = GetFirstAliveUnitByEntry( + botAI, static_cast(ZulAmanNPCs::NPC_CORRUPTED_LIGHTNING_TOTEM))) + { + MarkTargetWithSkull(bot, totem); + SetRtiTarget(botAI, "skull", totem); + + if (bot->GetTarget() != totem->GetGUID()) + return Attack(totem); + + return false; + } + + // Target priority 2: Halazzi + if (Unit* halazzi = AI_VALUE2(Unit*, "find target", "halazzi")) + { + SetRtiTarget(botAI, "star", halazzi); + + if (bot->GetTarget() != halazzi->GetGUID()) + return Attack(halazzi); + } + + // Don't attack the Lynx + return false; +} + +// Hex Lord Malacrass + +bool HexLordMalacrassMisdirectBossToMainTankAction::Execute(Event /*event*/) +{ + Unit* malacrass = AI_VALUE2(Unit*, "find target", "hex lord malacrass"); + if (!malacrass) + return false; + + Player* mainTank = GetGroupMainTank(botAI, bot); + if (!mainTank) + return false; + + if (botAI->CanCastSpell("misdirection", mainTank)) + return botAI->CastSpell("misdirection", mainTank); + + if (bot->HasAura(static_cast(ZulAmanSpells::SPELL_MISDIRECTION)) && + botAI->CanCastSpell("steady shot", malacrass)) + return botAI->CastSpell("steady shot", malacrass); + + return false; +} + +bool HexLordMalacrassAssignDpsPriorityAction::Execute(Event /*event*/) +{ + static constexpr uint32 priorityEntries[] = + { + static_cast(ZulAmanNPCs::NPC_LORD_RAADAN), + static_cast(ZulAmanNPCs::NPC_ALYSON_ANTILLE), + static_cast(ZulAmanNPCs::NPC_KORAGG), + static_cast(ZulAmanNPCs::NPC_DARKHEART), + static_cast(ZulAmanNPCs::NPC_FENSTALKER), + static_cast(ZulAmanNPCs::NPC_GAZAKROTH), + static_cast(ZulAmanNPCs::NPC_THURG), + static_cast(ZulAmanNPCs::NPC_SLITHER), + static_cast(ZulAmanNPCs::NPC_HEX_LORD_MALACRASS) + }; + + auto const& targets = + botAI->GetAiObjectContext()->GetValue("possible targets no los")->Get(); + + Unit* priorityTarget = nullptr; + + for (uint32 entry : priorityEntries) + { + for (auto const& guid : targets) + { + Unit* unit = botAI->GetUnit(guid); + if (unit && unit->IsAlive() && unit->GetEntry() == entry) + { + priorityTarget = unit; + break; + } + } + + if (priorityTarget) + break; + } + + if (priorityTarget) + { + MarkTargetWithSkull(bot, priorityTarget); + SetRtiTarget(botAI, "skull", priorityTarget); + } + + return false; +} + +bool HexLordMalacrassRunAwayFromWhirlwindAction::Execute(Event /*event*/) +{ + if (Unit* malacrass = AI_VALUE2(Unit*, "find target", "hex lord malacrass")) + { + float currentDistance = bot->GetDistance2d(malacrass); + constexpr float safeDistance = 9.0f; + if (currentDistance < safeDistance) + { + bot->AttackStop(); + bot->InterruptNonMeleeSpells(true); + return MoveAway(malacrass, safeDistance - currentDistance); + } + } + + return false; +} + +bool HexLordMalacrassCastersStopAttackingAction::Execute(Event /*event*/) +{ + Unit* malacrass = AI_VALUE2(Unit*, "find target", "hex lord malacrass"); + if (!malacrass || + !malacrass->HasAura(static_cast(ZulAmanSpells::SPELL_HEX_LORD_SPELL_REFLECTION))) + return false; + + if (bot->GetVictim() == malacrass) + { + bot->AttackStop(); + bot->InterruptNonMeleeSpells(true); + return true; + } + + return false; +} + +bool HexLordMalacrassMoveAwayFromFreezingTrapAction::Execute(Event /*event*/) +{ + GameObject* trapGo = bot->FindNearestGameObject( + static_cast(ZulAmanObjects::GO_FREEZING_TRAP), 20.0f, true); + + if (!trapGo) + return false; + + float currentDistance = bot->GetDistance2d(trapGo); + constexpr float safeDistance = 6.0f; + constexpr uint32 minInterval = 0; + if (currentDistance < safeDistance) + return FleePosition(trapGo->GetPosition(), safeDistance, minInterval); + + return false; +} + +// Zul'jin + +bool ZuljinMisdirectBossToMainTankAction::Execute(Event /*event*/) +{ + Unit* zuljin = AI_VALUE2(Unit*, "find target", "zul'jin"); + if (!zuljin) + return false; + + Player* mainTank = GetGroupMainTank(botAI, bot); + if (!mainTank) + return false; + + if (botAI->CanCastSpell("misdirection", mainTank)) + return botAI->CastSpell("misdirection", mainTank); + + if (bot->HasAura(static_cast(ZulAmanSpells::SPELL_MISDIRECTION)) && + botAI->CanCastSpell("steady shot", zuljin)) + return botAI->CastSpell("steady shot", zuljin); + + return false; +} + +bool ZuljinTanksPositionBossAction::Execute(Event /*event*/) +{ + Unit* zuljin = AI_VALUE2(Unit*, "find target", "zul'jin"); + if (!zuljin) + return false; + + if (bot->GetVictim() != zuljin) + return Attack(zuljin); + + if (zuljin->GetVictim() == bot) + { + const Position& position = ZULJIN_TANK_POSITION; + float distToPosition = + bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()); + + if (distToPosition > 2.0f) + { + float dX = position.GetPositionX() - bot->GetPositionX(); + float dY = position.GetPositionY() - bot->GetPositionY(); + float moveDist = std::min(10.0f, distToPosition); + float moveX = bot->GetPositionX() + (dX / distToPosition) * moveDist; + float moveY = bot->GetPositionY() + (dY / distToPosition) * moveDist; + + return MoveTo(ZULAMAN_MAP_ID, moveX, moveY, bot->GetPositionZ(), false, false, + false, false, MovementPriority::MOVEMENT_COMBAT, true, true); + } + } + + return false; +} + +bool ZuljinRunAwayFromWhirlwindAction::Execute(Event /*event*/) +{ + if (Unit* zuljin = AI_VALUE2(Unit*, "find target", "zul'jin")) + { + float currentDistance = bot->GetExactDist2d(zuljin); + constexpr float safeDistance = 10.0f; + if (currentDistance < safeDistance) + { + bot->AttackStop(); + bot->InterruptNonMeleeSpells(true); + return MoveAway(zuljin, safeDistance - currentDistance); + } + } + + return false; +} + +bool ZuljinAvoidCyclonesAction::Execute(Event /*event*/) +{ + auto const& cyclones = GetAllHazardTriggers( + bot, static_cast(ZulAmanNPCs::NPC_FEATHER_VORTEX), 50.0f); + + if (cyclones.empty()) + return false; + + constexpr float hazardRadius = 6.0f; + bool inDanger = false; + for (Unit* cyclone : cyclones) + { + if (bot->GetDistance2d(cyclone) < hazardRadius) + { + inDanger = true; + break; + } + } + + if (!inDanger) + return false; + + const Position& zuljinCenter = ZULJIN_TANK_POSITION; + constexpr float safeZoneRadius = 30.0f; + + Position safestPos = + FindSafestNearbyPosition(bot, cyclones, zuljinCenter, safeZoneRadius, hazardRadius, true); + + bot->AttackStop(); + bot->InterruptNonMeleeSpells(true); + return MoveTo(ZULAMAN_MAP_ID, safestPos.GetPositionX(), safestPos.GetPositionY(), + bot->GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_FORCED, true, false); +} + +bool ZuljinSpreadRangedAction::Execute(Event /*event*/) +{ + constexpr float minDistance = 6.0f; + constexpr uint32 minInterval = 1000; + if (Unit* nearestPlayer = GetNearestPlayerInRadius(bot, minDistance)) + return FleePosition(nearestPlayer->GetPosition(), minDistance, minInterval); + + return false; +} diff --git a/src/Ai/Raid/ZulAman/Action/RaidZulAmanActions.h b/src/Ai/Raid/ZulAman/Action/RaidZulAmanActions.h new file mode 100644 index 000000000..5542e8f38 --- /dev/null +++ b/src/Ai/Raid/ZulAman/Action/RaidZulAmanActions.h @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_RAIDZULAMANACTIONS_H +#define _PLAYERBOT_RAIDZULAMANACTIONS_H + +#include "Action.h" +#include "AttackAction.h" +#include "MovementActions.h" + +// Trash + +class AmanishiMedicineManMarkWardAction : public Action +{ +public: + AmanishiMedicineManMarkWardAction( + PlayerbotAI* botAI, std::string const name = "amani'shi medicine man mark ward") : Action(botAI, name) {} + bool Execute(Event event) override; +}; + +// Akil'zon + +class AkilzonMisdirectBossToMainTankAction : public AttackAction +{ +public: + AkilzonMisdirectBossToMainTankAction( + PlayerbotAI* botAI, std::string const name = "akil'zon misdirect boss to main tank") : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class AkilzonTanksPositionBossAction : public AttackAction +{ +public: + AkilzonTanksPositionBossAction( + PlayerbotAI* botAI, std::string const name = "akil'zon tanks position boss") : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class AkilzonSpreadRangedAction : public MovementAction +{ +public: + AkilzonSpreadRangedAction( + PlayerbotAI* botAI, std::string const name = "akil'zon spread ranged") : MovementAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class AkilzonMoveToEyeOfTheStormAction : public MovementAction +{ +public: + AkilzonMoveToEyeOfTheStormAction( + PlayerbotAI* botAI, std::string const name = "akil'zon move to eye of the storm") : MovementAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class AkilzonManageElectricalStormTimerAction : public Action +{ +public: + AkilzonManageElectricalStormTimerAction( + PlayerbotAI* botAI, std::string const name = "akil'zon manage electrical storm timer") : Action(botAI, name) {} + bool Execute(Event event) override; +}; + +// Nalorakk + +class NalorakkMisdirectBossToMainTankAction : public AttackAction +{ +public: + NalorakkMisdirectBossToMainTankAction( + PlayerbotAI* botAI, std::string const name = "nalorakk misdirect boss to main tank") : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class NalorakkTanksPositionBossAction : public AttackAction +{ +public: + NalorakkTanksPositionBossAction( + PlayerbotAI* botAI, std::string const name = "nalorakk tanks position boss") : AttackAction(botAI, name) {} + bool Execute(Event event) override; + +private: + bool MainTankPositionTrollForm(Unit* nalorakk); + bool FirstAssistTankPositionBearForm(Unit* nalorakk); +}; + +class NalorakkSpreadRangedAction : public MovementAction +{ +public: + NalorakkSpreadRangedAction( + PlayerbotAI* botAI, std::string const name = "nalorakk spread ranged") : MovementAction(botAI, name) {} + bool Execute(Event event) override; +}; + +// Jan'alai + +class JanalaiMisdirectBossToMainTankAction : public AttackAction +{ +public: + JanalaiMisdirectBossToMainTankAction( + PlayerbotAI* botAI, std::string const name = "jan'alai misdirect boss to main tank") : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class JanalaiTanksPositionBossAction : public AttackAction +{ +public: + JanalaiTanksPositionBossAction( + PlayerbotAI* botAI, std::string const name = "jan'alai tanks position boss") : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class JanalaiSpreadRangedInCircleAction : public MovementAction +{ +public: + JanalaiSpreadRangedInCircleAction( + PlayerbotAI* botAI, std::string const name = "jan'alai spread ranged in circle") : MovementAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class JanalaiAvoidFireBombsAction : public MovementAction +{ +public: + JanalaiAvoidFireBombsAction(PlayerbotAI* botAI, std::string const name = "jan'alai avoid fire bombs") : MovementAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class JanalaiMarkAmanishiHatchersAction : public Action +{ +public: + JanalaiMarkAmanishiHatchersAction( + PlayerbotAI* botAI, std::string const name = "jan'alai mark amani'shi hatchers") : Action(botAI, name) {} + bool Execute(Event event) override; +}; + +// Halazzi + +class HalazziMisdirectBossToMainTankAction : public AttackAction +{ +public: + HalazziMisdirectBossToMainTankAction( + PlayerbotAI* botAI, std::string const name = "halazzi misdirect boss to main tank") : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class HalazziMainTankPositionBossAction : public AttackAction +{ +public: + HalazziMainTankPositionBossAction( + PlayerbotAI* botAI, std::string const name = "halazzi main tank position boss") : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class HalazziFirstAssistTankAttackSpiritLynxAction : public AttackAction +{ +public: + HalazziFirstAssistTankAttackSpiritLynxAction( + PlayerbotAI* botAI, std::string const name = "halazzi first assist tank attack spirit lynx") : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class HalazziAssignDpsPriorityAction : public AttackAction +{ +public: + HalazziAssignDpsPriorityAction( + PlayerbotAI* botAI, std::string const name = "halazzi assign dps priority") : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +// Hex Lord Malacrass + +class HexLordMalacrassMisdirectBossToMainTankAction : public AttackAction +{ +public: + HexLordMalacrassMisdirectBossToMainTankAction( + PlayerbotAI* botAI, std::string const name = "hex lord malacrass misdirect boss to main tank") : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class HexLordMalacrassAssignDpsPriorityAction : public AttackAction +{ +public: + HexLordMalacrassAssignDpsPriorityAction( + PlayerbotAI* botAI, std::string const name = "hex lord malacrass assign dps priority") : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class HexLordMalacrassRunAwayFromWhirlwindAction : public MovementAction +{ +public: + HexLordMalacrassRunAwayFromWhirlwindAction( + PlayerbotAI* botAI, std::string const name = "hex lord malacrass run away from whirlwind") : MovementAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class HexLordMalacrassCastersStopAttackingAction : public Action +{ +public: + HexLordMalacrassCastersStopAttackingAction( + PlayerbotAI* botAI, std::string const name = "hex lord malacrass casters stop attacking") : Action(botAI, name) {} + bool Execute(Event event) override; +}; + +class HexLordMalacrassMoveAwayFromFreezingTrapAction : public MovementAction +{ +public: + HexLordMalacrassMoveAwayFromFreezingTrapAction( + PlayerbotAI* botAI, std::string const name = "hex lord malacrass move away from freezing trap") : MovementAction(botAI, name) {} + bool Execute(Event event) override; +}; + +// Zul'jin + +class ZuljinMisdirectBossToMainTankAction : public AttackAction +{ +public: + ZuljinMisdirectBossToMainTankAction( + PlayerbotAI* botAI, std::string const name = "zul'jin misdirect boss to main tank") : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class ZuljinTanksPositionBossAction : public AttackAction +{ +public: + ZuljinTanksPositionBossAction( + PlayerbotAI* botAI, std::string const name = "zul'jin tanks position boss") : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class ZuljinRunAwayFromWhirlwindAction : public MovementAction +{ +public: + ZuljinRunAwayFromWhirlwindAction( + PlayerbotAI* botAI, std::string const name = "zul'jin run away from whirlwind") : MovementAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class ZuljinAvoidCyclonesAction : public MovementAction +{ +public: + ZuljinAvoidCyclonesAction(PlayerbotAI* botAI, std::string const name = "zul'jin avoid cyclones") : MovementAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class ZuljinSpreadRangedAction : public MovementAction +{ +public: + ZuljinSpreadRangedAction( + PlayerbotAI* botAI, std::string const name = "zul'jin spread ranged") : MovementAction(botAI, name) {} + bool Execute(Event event) override; +}; + +#endif diff --git a/src/Ai/Raid/ZulAman/Multiplier/RaidZulAmanMultipliers.cpp b/src/Ai/Raid/ZulAman/Multiplier/RaidZulAmanMultipliers.cpp new file mode 100644 index 000000000..e7d16920f --- /dev/null +++ b/src/Ai/Raid/ZulAman/Multiplier/RaidZulAmanMultipliers.cpp @@ -0,0 +1,382 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include "RaidZulAmanMultipliers.h" +#include "RaidZulAmanActions.h" +#include "RaidZulAmanHelpers.h" +#include "ChooseTargetActions.h" +#include "DKActions.h" +#include "DruidBearActions.h" +#include "FollowActions.h" +#include "GenericSpellActions.h" +#include "HunterActions.h" +#include "MageActions.h" +#include "PaladinActions.h" +#include "Playerbots.h" +#include "PriestActions.h" +#include "RaidBossHelpers.h" +#include "ReachTargetActions.h" +#include "RogueActions.h" +#include "ShamanActions.h" +#include "WarlockActions.h" +#include "WarriorActions.h" + +using namespace ZulAmanHelpers; + +// Akil'zon + +float AkilzonDisableCombatFormationMoveMultiplier::GetValue(Action* action) +{ + if (!AI_VALUE2(Unit*, "find target", "akil'zon")) + return 1.0f; + + if (dynamic_cast(action) && + !dynamic_cast(action)) + return 0.0f; + + return 1.0f; +} + +float AkilzonStayInEyeOfTheStormMultiplier::GetValue(Action* action) +{ + if (!AI_VALUE2(Unit*, "find target", "akil'zon") /* || + !GetElectricalStormTarget(bot)*/) + return 1.0f; + + auto it = akilzonStormTimer.find(bot->GetMap()->GetInstanceId()); + if (it == akilzonStormTimer.end() || + !IsInStormWindow(it->second, std::time(nullptr))) + return 1.0f; + + if (dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action)) + return 0.0f; + + return 1.0f; +} + +// Nalorakk + +float NalorakkDisableTankActionsMultiplier::GetValue(Action* action) +{ + if (!botAI->IsTank(bot)) + return 1.0f; + + Unit* nalorakk = AI_VALUE2(Unit*, "find target", "nalorakk"); + if (!nalorakk) + return 1.0f; + + if (dynamic_cast(action)) + return 0.0f; + + if (bot->GetVictim() == nullptr) + return 1.0f; + + bool shouldTankBoss = false; + + if (botAI->IsMainTank(bot) && + !nalorakk->HasAura(static_cast(ZulAmanSpells::SPELL_BEARFORM))) + shouldTankBoss = true; + + if (botAI->IsAssistTankOfIndex(bot, 0, true) && + nalorakk->HasAura(static_cast(ZulAmanSpells::SPELL_BEARFORM))) + shouldTankBoss = true; + + if (!shouldTankBoss && + (dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action))) + return 0.0f; + + return 1.0f; +} + +float NalorakkControlMisdirectionMultiplier::GetValue(Action* action) +{ + if (bot->getClass() != CLASS_HUNTER || + !AI_VALUE2(Unit*, "find target", "nalorakk")) + return 1.0f; + + if (dynamic_cast(action)) + return 0.0f; + + return 1.0f; +} + +// Jan'alai + +float JanalaiDisableTankActionsMultiplier::GetValue(Action* action) +{ + if (!botAI->IsTank(bot) || + !AI_VALUE2(Unit*, "find target", "jan'alai")) + return 1.0f; + + if (dynamic_cast(action)) + return 0.0f; + + if (bot->GetVictim() == nullptr) + return 1.0f; + + if (botAI->IsMainTank(bot) && + dynamic_cast(action)) + return 0.0f; + + if (botAI->IsAssistTank(bot) && + !GetFirstAliveUnitByEntry( + botAI, static_cast(ZulAmanNPCs::NPC_AMANI_DRAGONHAWK_HATCHLING)) && + dynamic_cast(action)) + return 0.0f; + + return 1.0f; +} + +float JanalaiDisableCombatFormationMoveMultiplier::GetValue(Action* action) +{ + if (!AI_VALUE2(Unit*, "find target", "jan'alai")) + return 1.0f; + + if (dynamic_cast(action) && + !dynamic_cast(action)) + return 0.0f; + + return 1.0f; +} + +float JanalaiStayAwayFromFireBombsMultiplier::GetValue(Action* action) +{ + if (!AI_VALUE2(Unit*, "find target", "jan'alai")) + return 1.0f; + + if (!HasFireBombNearby(botAI, bot)) + return 1.0f; + + if (dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action)) + return 0.0f; + + return 1.0f; +} + +float JanalaiDoNotCrowdControlHatchersMultiplier::GetValue(Action* action) +{ + if (!AI_VALUE2(Unit*, "find target", "amani'shi hatcher")) + return 1.0f; + + if (dynamic_cast(action) || + dynamic_cast(action)) + return 0.0f; + + return 1.0f; +} + +float JanalaiDelayBloodlustAndHeroismMultiplier::GetValue(Action* action) +{ + if (bot->getClass() != CLASS_SHAMAN) + return 1.0f; + + if (!AI_VALUE2(Unit*, "find target", "jan'alai")) + return 1.0f; + + if (AI_VALUE2(Unit*, "find target", "amani dragonhawk hatchling")) + return 1.0f; + + if (dynamic_cast(action) || + dynamic_cast(action)) + return 0.0f; + + return 1.0f; +} + +// Halazzi + +float HalazziDisableTankActionsMultiplier::GetValue(Action* action) +{ + if (!botAI->IsTank(bot) || + !AI_VALUE2(Unit*, "find target", "halazzi")) + return 1.0f; + + if (dynamic_cast(action)) + return 0.0f; + + if (bot->GetVictim() != nullptr && + dynamic_cast(action)) + return 0.0f; + + return 1.0f; +} + +float HalazziControlMisdirectionMultiplier::GetValue(Action* action) +{ + if (bot->getClass() != CLASS_HUNTER || + !AI_VALUE2(Unit*, "find target", "halazzi")) + return 1.0f; + + if (dynamic_cast(action)) + return 0.0f; + + return 1.0f; +} + +// Hex Lord Malacrass + +float HexLordMalacrassAvoidWhirlwindMultiplier::GetValue(Action* action) +{ + if (botAI->IsMainTank(bot)) + return 1.0f; + + Unit* malacrass = AI_VALUE2(Unit*, "find target", "hex lord malacrass"); + if (!malacrass || + !malacrass->HasAura(static_cast(ZulAmanSpells::SPELL_HEX_LORD_WHIRLWIND))) + return 1.0f; + + if (dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action)) + return 0.0f; + + return 1.0f; +} + +float HexLordMalacrassStopAttackingDuringSpellReflectionMultiplier::GetValue(Action* action) +{ + if (!botAI->IsCaster(bot)) + return 1.0f; + + Unit* malacrass = AI_VALUE2(Unit*, "find target", "hex lord malacrass"); + if (!malacrass || + !malacrass->HasAura(static_cast(ZulAmanSpells::SPELL_HEX_LORD_SPELL_REFLECTION))) + return 1.0f; + + auto castSpellAction = dynamic_cast(action); + if (!castSpellAction) + return 1.0f; + + if (castSpellAction->getThreatType() == Action::ActionThreatType::Aoe || + (bot->GetVictim() == malacrass && + castSpellAction->getThreatType() == Action::ActionThreatType::Single)) + return 0.0f; + + return 1.0f; +} + +float HexLordMalacrassDoNotDispelUnstableAfflictionMultiplier::GetValue(Action* action) +{ + if (bot->getClass() != CLASS_PRIEST && + bot->getClass() != CLASS_PALADIN && + bot->getClass() != CLASS_WARLOCK) + return 1.0f; + + if (!AI_VALUE2(Unit*, "find target", "hex lord malacrass")) + return 1.0f; + + Group* group = bot->GetGroup(); + if (!group) + return 1.0f; + + bool hasUnstableAffliction = false; + for (GroupReference* ref = bot->GetGroup()->GetFirstMember(); ref != nullptr; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive()) + continue; + + if (member->HasAura(static_cast(ZulAmanSpells::SPELL_UNSTABLE_AFFLICTION))) + { + hasUnstableAffliction = true; + break; + } + } + + if (!hasUnstableAffliction) + return 1.0f; + + if (dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action)) + return 0.0f; + + return 1.0f; +} + +// Zul'jin + +float ZuljinDisableTankFaceMultiplier::GetValue(Action* action) +{ + if (!botAI->IsTank(bot)) + return 1.0f; + + Unit* zuljin = AI_VALUE2(Unit*, "find target", "zul'jin"); + if (!zuljin || + zuljin->HasAura(static_cast(ZulAmanSpells::SPELL_SHAPE_OF_THE_DRAGONHAWK))) + return 1.0f; + + if (dynamic_cast(action)) + return 0.0f; + + return 1.0f; +} + +float ZuljinAvoidWhirlwindMultiplier::GetValue(Action* action) +{ + if (botAI->IsMainTank(bot)) + return 1.0f; + + Unit* zuljin = AI_VALUE2(Unit*, "find target", "zul'jin"); + if (!zuljin || + !zuljin->HasAura(static_cast(ZulAmanSpells::SPELL_ZULJIN_WHIRLWIND))) + return 1.0f; + + if (dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action)) + return 0.0f; + + return 1.0f; +} + +float ZuljinDisableAvoidAoeMultiplier::GetValue(Action* action) +{ + Unit* zuljin = AI_VALUE2(Unit*, "find target", "zul'jin"); + if (!zuljin || + !zuljin->HasAura(static_cast(ZulAmanSpells::SPELL_SHAPE_OF_THE_EAGLE))) + return 1.0f; + + if (dynamic_cast(action)) + return 0.0f; + + return 1.0f; +} + +float ZuljinDelayBloodlustAndHeroismMultiplier::GetValue(Action* action) +{ + if (bot->getClass() != CLASS_SHAMAN) + return 1.0f; + + Unit* zuljin = AI_VALUE2(Unit*, "find target", "zul'jin"); + if (!zuljin || + zuljin->HasAura(static_cast(ZulAmanSpells::SPELL_SHAPE_OF_THE_EAGLE))) + return 1.0f; + + if (dynamic_cast(action) || + dynamic_cast(action)) + return 0.0f; + + return 1.0f; +} diff --git a/src/Ai/Raid/ZulAman/Multiplier/RaidZulAmanMultipliers.h b/src/Ai/Raid/ZulAman/Multiplier/RaidZulAmanMultipliers.h new file mode 100644 index 000000000..c3bdffbe1 --- /dev/null +++ b/src/Ai/Raid/ZulAman/Multiplier/RaidZulAmanMultipliers.h @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_RAIDZULAMANMULTIPLIERS_H +#define _PLAYERBOT_RAIDZULAMANMULTIPLIERS_H + +#include "Multiplier.h" + +// Akil'zon + +class AkilzonDisableCombatFormationMoveMultiplier : public Multiplier +{ +public: + AkilzonDisableCombatFormationMoveMultiplier(PlayerbotAI* botAI) : Multiplier( + botAI, "akil'zon disable combat formation move") {} + virtual float GetValue(Action* action); +}; + +class AkilzonStayInEyeOfTheStormMultiplier : public Multiplier +{ +public: + AkilzonStayInEyeOfTheStormMultiplier(PlayerbotAI* botAI) : Multiplier( + botAI, "akil'zon stay in eye of the storm") {} + virtual float GetValue(Action* action); +}; + +// Nalorakk + +class NalorakkDisableTankActionsMultiplier : public Multiplier +{ +public: + NalorakkDisableTankActionsMultiplier(PlayerbotAI* botAI) : Multiplier( + botAI, "nalorakk disable tank actions") {} + virtual float GetValue(Action* action); +}; + +class NalorakkControlMisdirectionMultiplier : public Multiplier +{ +public: + NalorakkControlMisdirectionMultiplier(PlayerbotAI* botAI) : Multiplier( + botAI, "nalorakk control misdirection") {} + virtual float GetValue(Action* action); +}; + +// Jan'alai + +class JanalaiDisableTankActionsMultiplier : public Multiplier +{ +public: + JanalaiDisableTankActionsMultiplier(PlayerbotAI* botAI) : Multiplier( + botAI, "jan'alai disable tank actions") {} + virtual float GetValue(Action* action); +}; + +class JanalaiDisableCombatFormationMoveMultiplier : public Multiplier +{ +public: + JanalaiDisableCombatFormationMoveMultiplier(PlayerbotAI* botAI) : Multiplier( + botAI, "jan'alai disable combat formation move") {} + virtual float GetValue(Action* action); +}; + +class JanalaiStayAwayFromFireBombsMultiplier : public Multiplier +{ +public: + JanalaiStayAwayFromFireBombsMultiplier(PlayerbotAI* botAI) : Multiplier( + botAI, "jan'alai stay away from fire bombs") {} + virtual float GetValue(Action* action); +}; + +class JanalaiDoNotCrowdControlHatchersMultiplier : public Multiplier +{ +public: + JanalaiDoNotCrowdControlHatchersMultiplier(PlayerbotAI* botAI) : Multiplier( + botAI, "jan'alai do not crowd control hatchers") {} + virtual float GetValue(Action* action); +}; + +class JanalaiDelayBloodlustAndHeroismMultiplier : public Multiplier +{ +public: + JanalaiDelayBloodlustAndHeroismMultiplier(PlayerbotAI* botAI) : Multiplier( + botAI, "jan'alai delay bloodlust and heroism") {} + virtual float GetValue(Action* action); +}; + +// Halazzi + +class HalazziDisableTankActionsMultiplier : public Multiplier +{ +public: + HalazziDisableTankActionsMultiplier(PlayerbotAI* botAI) : Multiplier( + botAI, "halazzi disable tank actions") {} + virtual float GetValue(Action* action); +}; + +class HalazziControlMisdirectionMultiplier : public Multiplier +{ +public: + HalazziControlMisdirectionMultiplier(PlayerbotAI* botAI) : Multiplier( + botAI, "halazzi control misdirection") {} + virtual float GetValue(Action* action); +}; + +// Hex Lord Malacrass + +class HexLordMalacrassAvoidWhirlwindMultiplier : public Multiplier +{ +public: + HexLordMalacrassAvoidWhirlwindMultiplier(PlayerbotAI* botAI) : Multiplier( + botAI, "hex lord malacrass avoid whirlwind") {} + virtual float GetValue(Action* action); +}; + +class HexLordMalacrassDoNotDispelUnstableAfflictionMultiplier : public Multiplier +{ +public: + HexLordMalacrassDoNotDispelUnstableAfflictionMultiplier(PlayerbotAI* botAI) : Multiplier( + botAI, "hex lord malacrass do not dispel unstable affliction") {} + virtual float GetValue(Action* action); +}; + +class HexLordMalacrassStopAttackingDuringSpellReflectionMultiplier : public Multiplier +{ +public: + HexLordMalacrassStopAttackingDuringSpellReflectionMultiplier(PlayerbotAI* botAI) : Multiplier( + botAI, "hex lord malacrass stop attacking during spell reflection") {} + virtual float GetValue(Action* action); +}; + +// Zul'jin + +class ZuljinDisableTankFaceMultiplier : public Multiplier +{ +public: + ZuljinDisableTankFaceMultiplier(PlayerbotAI* botAI) : Multiplier( + botAI, "zul'jin disable tank face") {} + virtual float GetValue(Action* action); +}; + +class ZuljinAvoidWhirlwindMultiplier : public Multiplier +{ +public: + ZuljinAvoidWhirlwindMultiplier(PlayerbotAI* botAI) : Multiplier( + botAI, "zul'jin avoid whirlwind") {} + virtual float GetValue(Action* action); +}; + +class ZuljinDisableAvoidAoeMultiplier : public Multiplier +{ +public: + ZuljinDisableAvoidAoeMultiplier(PlayerbotAI* botAI) : Multiplier( + botAI, "zul'jin disable avoid aoe") {} + virtual float GetValue(Action* action); +}; + +class ZuljinDelayBloodlustAndHeroismMultiplier : public Multiplier +{ +public: + ZuljinDelayBloodlustAndHeroismMultiplier(PlayerbotAI* botAI) : Multiplier( + botAI, "zul'jin delay bloodlust and heroism") {} + virtual float GetValue(Action* action); +}; + +#endif diff --git a/src/Ai/Raid/ZulAman/RaidZulAmanActionContext.h b/src/Ai/Raid/ZulAman/RaidZulAmanActionContext.h new file mode 100644 index 000000000..852a180b3 --- /dev/null +++ b/src/Ai/Raid/ZulAman/RaidZulAmanActionContext.h @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_RAIDZULAMANACTIONCONTEXT_H +#define _PLAYERBOT_RAIDZULAMANACTIONCONTEXT_H + +#include "RaidZulAmanActions.h" +#include "NamedObjectContext.h" + +class RaidZulAmanActionContext : public NamedObjectContext +{ +public: + RaidZulAmanActionContext() + { + // Trash + creators["amani'shi medicine man mark ward"] = + &RaidZulAmanActionContext::amanishi_medicine_man_mark_ward; + + // Akil'zon + creators["akil'zon misdirect boss to main tank"] = + &RaidZulAmanActionContext::akilzon_misdirect_boss_to_main_tank; + + creators["akil'zon tanks position boss"] = + &RaidZulAmanActionContext::akilzon_tanks_position_boss; + + creators["akil'zon spread ranged"] = + &RaidZulAmanActionContext::akilzon_spread_ranged; + + creators["akil'zon move to eye of the storm"] = + &RaidZulAmanActionContext::akilzon_move_to_eye_of_the_storm; + + creators["akil'zon manage electrical storm timer"] = + &RaidZulAmanActionContext::akilzon_manage_electrical_storm_timer; + + // Nalorakk + creators["nalorakk misdirect boss to main tank"] = + &RaidZulAmanActionContext::nalorakk_misdirect_boss_to_main_tank; + + creators["nalorakk tanks position boss"] = + &RaidZulAmanActionContext::nalorakk_tanks_position_boss; + + creators["nalorakk spread ranged"] = + &RaidZulAmanActionContext::nalorakk_spread_ranged; + + // Jan'alai + creators["jan'alai misdirect boss to main tank"] = + &RaidZulAmanActionContext::janalai_misdirect_boss_to_main_tank; + + creators["jan'alai tanks position boss"] = + &RaidZulAmanActionContext::janalai_tanks_position_boss; + + creators["jan'alai spread ranged in circle"] = + &RaidZulAmanActionContext::janalai_spread_ranged_in_circle; + + creators["jan'alai avoid fire bombs"] = + &RaidZulAmanActionContext::janalai_avoid_fire_bombs; + + creators["jan'alai mark amani'shi hatchers"] = + &RaidZulAmanActionContext::janalai_mark_amanishi_hatchers; + + // Halazzi + creators["halazzi misdirect boss to main tank"] = + &RaidZulAmanActionContext::halazzi_misdirect_boss_to_main_tank; + + creators["halazzi main tank position boss"] = + &RaidZulAmanActionContext::halazzi_main_tank_position_boss; + + creators["halazzi first assist tank attack spirit lynx"] = + &RaidZulAmanActionContext::halazzi_first_assist_tank_attack_spirit_lynx; + + creators["halazzi assign dps priority"] = + &RaidZulAmanActionContext::halazzi_assign_dps_priority; + + // Hex Lord Malacrass + creators["hex lord malacrass misdirect boss to main tank"] = + &RaidZulAmanActionContext::hex_lord_malacrass_misdirect_boss_to_main_tank; + + creators["hex lord malacrass assign dps priority"] = + &RaidZulAmanActionContext::hex_lord_malacrass_assign_dps_priority; + + creators["hex lord malacrass run away from whirlwind"] = + &RaidZulAmanActionContext::hex_lord_malacrass_run_away_from_whirlwind; + + creators["hex lord malacrass casters stop attacking"] = + &RaidZulAmanActionContext::hex_lord_malacrass_casters_stop_attacking; + + creators["hex lord malacrass move away from freezing trap"] = + &RaidZulAmanActionContext::hex_lord_malacrass_move_away_from_freezing_trap; + + // Zul'jin + creators["zul'jin misdirect boss to main tank"] = + &RaidZulAmanActionContext::zuljin_misdirect_boss_to_main_tank; + + creators["zul'jin tanks position boss"] = + &RaidZulAmanActionContext::zuljin_tanks_position_boss; + + creators["zul'jin run away from whirlwind"] = + &RaidZulAmanActionContext::zuljin_run_away_from_whirlwind; + + creators["zul'jin avoid cyclones"] = + &RaidZulAmanActionContext::zuljin_avoid_cyclones; + + creators["zul'jin spread ranged"] = + &RaidZulAmanActionContext::zuljin_spread_ranged; + } + +private: + // Trash + static Action* amanishi_medicine_man_mark_ward( + PlayerbotAI* botAI) { return new AmanishiMedicineManMarkWardAction(botAI); } + + // Akil'zon + static Action* akilzon_misdirect_boss_to_main_tank( + PlayerbotAI* botAI) { return new AkilzonMisdirectBossToMainTankAction(botAI); } + + static Action* akilzon_tanks_position_boss( + PlayerbotAI* botAI) { return new AkilzonTanksPositionBossAction(botAI); } + + static Action* akilzon_spread_ranged( + PlayerbotAI* botAI) { return new AkilzonSpreadRangedAction(botAI); } + + static Action* akilzon_move_to_eye_of_the_storm( + PlayerbotAI* botAI) { return new AkilzonMoveToEyeOfTheStormAction(botAI); } + + static Action* akilzon_manage_electrical_storm_timer( + PlayerbotAI* botAI) { return new AkilzonManageElectricalStormTimerAction(botAI); } + + // Nalorakk + static Action* nalorakk_misdirect_boss_to_main_tank( + PlayerbotAI* botAI) { return new NalorakkMisdirectBossToMainTankAction(botAI); } + + static Action* nalorakk_tanks_position_boss( + PlayerbotAI* botAI) { return new NalorakkTanksPositionBossAction(botAI); } + + static Action* nalorakk_spread_ranged( + PlayerbotAI* botAI) { return new NalorakkSpreadRangedAction(botAI); } + + // Jan'alai + static Action* janalai_misdirect_boss_to_main_tank( + PlayerbotAI* botAI) { return new JanalaiMisdirectBossToMainTankAction(botAI); } + + static Action* janalai_tanks_position_boss( + PlayerbotAI* botAI) { return new JanalaiTanksPositionBossAction(botAI); } + + static Action* janalai_spread_ranged_in_circle( + PlayerbotAI* botAI) { return new JanalaiSpreadRangedInCircleAction(botAI); } + + static Action* janalai_avoid_fire_bombs( + PlayerbotAI* botAI) { return new JanalaiAvoidFireBombsAction(botAI); } + + static Action* janalai_mark_amanishi_hatchers( + PlayerbotAI* botAI) { return new JanalaiMarkAmanishiHatchersAction(botAI); } + + // Halazzi + static Action* halazzi_misdirect_boss_to_main_tank( + PlayerbotAI* botAI) { return new HalazziMisdirectBossToMainTankAction(botAI); } + + static Action* halazzi_main_tank_position_boss( + PlayerbotAI* botAI) { return new HalazziMainTankPositionBossAction(botAI); } + + static Action* halazzi_first_assist_tank_attack_spirit_lynx( + PlayerbotAI* botAI) { return new HalazziFirstAssistTankAttackSpiritLynxAction(botAI); } + + static Action* halazzi_assign_dps_priority( + PlayerbotAI* botAI) { return new HalazziAssignDpsPriorityAction(botAI); } + + // Hex Lord Malacrass + static Action* hex_lord_malacrass_misdirect_boss_to_main_tank( + PlayerbotAI* botAI) { return new HexLordMalacrassMisdirectBossToMainTankAction(botAI); } + + static Action* hex_lord_malacrass_assign_dps_priority( + PlayerbotAI* botAI) { return new HexLordMalacrassAssignDpsPriorityAction(botAI); } + + static Action* hex_lord_malacrass_run_away_from_whirlwind( + PlayerbotAI* botAI) { return new HexLordMalacrassRunAwayFromWhirlwindAction(botAI); } + + static Action* hex_lord_malacrass_casters_stop_attacking( + PlayerbotAI* botAI) { return new HexLordMalacrassCastersStopAttackingAction(botAI); } + + static Action* hex_lord_malacrass_move_away_from_freezing_trap( + PlayerbotAI* botAI) { return new HexLordMalacrassMoveAwayFromFreezingTrapAction(botAI); } + + // Zul'jin + static Action* zuljin_misdirect_boss_to_main_tank( + PlayerbotAI* botAI) { return new ZuljinMisdirectBossToMainTankAction(botAI); } + + static Action* zuljin_tanks_position_boss( + PlayerbotAI* botAI) { return new ZuljinTanksPositionBossAction(botAI); } + + static Action* zuljin_run_away_from_whirlwind( + PlayerbotAI* botAI) { return new ZuljinRunAwayFromWhirlwindAction(botAI); } + + static Action* zuljin_avoid_cyclones( + PlayerbotAI* botAI) { return new ZuljinAvoidCyclonesAction(botAI); } + + static Action* zuljin_spread_ranged( + PlayerbotAI* botAI) { return new ZuljinSpreadRangedAction(botAI); } +}; + +#endif diff --git a/src/Ai/Raid/ZulAman/RaidZulAmanTriggerContext.h b/src/Ai/Raid/ZulAman/RaidZulAmanTriggerContext.h new file mode 100644 index 000000000..5be8bad7f --- /dev/null +++ b/src/Ai/Raid/ZulAman/RaidZulAmanTriggerContext.h @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_RAIDZULAMANTRIGGERCONTEXT_H +#define _PLAYERBOT_RAIDZULAMANTRIGGERCONTEXT_H + +#include "RaidZulAmanTriggers.h" +#include "AiObjectContext.h" + +class RaidZulAmanTriggerContext : public NamedObjectContext +{ +public: + RaidZulAmanTriggerContext() + { + // Trash + creators["amani'shi medicine man summoned ward"] = + &RaidZulAmanTriggerContext::amanishi_medicine_man_summoned_ward; + + // Akil'zon + creators["akil'zon pulling boss"] = + &RaidZulAmanTriggerContext::akilzon_pulling_boss; + + creators["akil'zon boss engaged by tanks"] = + &RaidZulAmanTriggerContext::akilzon_boss_engaged_by_tanks; + + creators["akil'zon boss casts static disruption"] = + &RaidZulAmanTriggerContext::akilzon_boss_casts_static_disruption; + + creators["akil'zon electrical storm incoming"] = + &RaidZulAmanTriggerContext::akilzon_electrical_storm_incoming; + + creators["akil'zon bots need to prepare for electrical storm"] = + &RaidZulAmanTriggerContext::akilzon_bots_need_to_prepare_for_electrical_storm; + + // Nalorakk + creators["nalorakk pulling boss"] = + &RaidZulAmanTriggerContext::nalorakk_pulling_boss; + + creators["nalorakk boss casts surge"] = + &RaidZulAmanTriggerContext::nalorakk_boss_casts_surge; + + creators["nalorakk boss switches forms"] = + &RaidZulAmanTriggerContext::nalorakk_boss_switches_forms; + + // Jan'alai + creators["jan'alai pulling boss"] = + &RaidZulAmanTriggerContext::janalai_pulling_boss; + + creators["jan'alai boss engaged by tanks"] = + &RaidZulAmanTriggerContext::janalai_boss_engaged_by_tanks; + + creators["jan'alai boss casts flame breath"] = + &RaidZulAmanTriggerContext::janalai_boss_casts_flame_breath; + + creators["jan'alai boss summoning fire bombs"] = + &RaidZulAmanTriggerContext::janalai_boss_summoning_fire_bombs; + + creators["jan'alai amani'shi hatchers spawned"] = + &RaidZulAmanTriggerContext::janalai_amanishi_hatchers_spawned; + + // Halazzi + creators["halazzi pulling boss"] = + &RaidZulAmanTriggerContext::halazzi_pulling_boss; + + creators["halazzi boss engaged by main tank"] = + &RaidZulAmanTriggerContext::halazzi_boss_engaged_by_main_tank; + + creators["halazzi boss summons spirit lynx"] = + &RaidZulAmanTriggerContext::halazzi_boss_summons_spirit_lynx; + + creators["halazzi determining dps target"] = + &RaidZulAmanTriggerContext::halazzi_determining_dps_target; + + // Hex Lord Malacrass + + creators["hex lord malacrass pulling boss"] = + &RaidZulAmanTriggerContext::hex_lord_malacrass_pulling_boss; + + creators["hex lord malacrass determining kill order"] = + &RaidZulAmanTriggerContext::hex_lord_malacrass_determining_kill_order; + + creators["hex lord malacrass boss is channeling whirlwind"] = + &RaidZulAmanTriggerContext::hex_lord_malacrass_boss_is_channeling_whirlwind; + + creators["hex lord malacrass boss has spell reflection"] = + &RaidZulAmanTriggerContext::hex_lord_malacrass_boss_has_spell_reflection; + + creators["hex lord malacrass boss placed freezing trap"] = + &RaidZulAmanTriggerContext::hex_lord_malacrass_boss_placed_freezing_trap; + + // Zul'jin + + creators["zul'jin main tank needs aggro upon pull or phase change"] = + &RaidZulAmanTriggerContext::zuljin_main_tank_needs_aggro_upon_pull_or_phase_change; + + creators["zul'jin boss engaged by tanks"] = + &RaidZulAmanTriggerContext::zuljin_boss_engaged_by_tanks; + + creators["zul'jin boss is channeling whirlwind in troll form"] = + &RaidZulAmanTriggerContext::zuljin_boss_is_channeling_whirlwind_in_troll_form; + + creators["zul'jin boss is summoning cyclones in eagle form"] = + &RaidZulAmanTriggerContext::zuljin_boss_is_summoning_cyclones_in_eagle_form; + + creators["zul'jin boss casts aoe abilities in dragonhawk form"] = + &RaidZulAmanTriggerContext::zuljin_boss_casts_aoe_abilities_in_dragonhawk_form; + } + +private: + // Trash + static Trigger* amanishi_medicine_man_summoned_ward( + PlayerbotAI* botAI) { return new AmanishiMedicineManSummonedWardTrigger(botAI); } + + // Akil'zon + static Trigger* akilzon_pulling_boss( + PlayerbotAI* botAI) { return new AkilzonPullingBossTrigger(botAI); } + + static Trigger* akilzon_boss_engaged_by_tanks( + PlayerbotAI* botAI) { return new AkilzonBossEngagedByTanksTrigger(botAI); } + + static Trigger* akilzon_boss_casts_static_disruption( + PlayerbotAI* botAI) { return new AkilzonBossCastsStaticDisruptionTrigger(botAI); } + + static Trigger* akilzon_electrical_storm_incoming( + PlayerbotAI* botAI) { return new AkilzonElectricalStormIncomingTrigger(botAI); } + + static Trigger* akilzon_bots_need_to_prepare_for_electrical_storm( + PlayerbotAI* botAI) { return new AkilzonBotsNeedToPrepareForElectricalStormTrigger(botAI); } + + // Nalorakk + static Trigger* nalorakk_pulling_boss( + PlayerbotAI* botAI) { return new NalorakkPullingBossTrigger(botAI); } + + static Trigger* nalorakk_boss_casts_surge( + PlayerbotAI* botAI) { return new NalorakkBossCastsSurgeTrigger(botAI); } + + static Trigger* nalorakk_boss_switches_forms( + PlayerbotAI* botAI) { return new NalorakkBossSwitchesFormsTrigger(botAI); } + + // Jan'alai + static Trigger* janalai_pulling_boss( + PlayerbotAI* botAI) { return new JanalaiPullingBossTrigger(botAI); } + + static Trigger* janalai_boss_engaged_by_tanks( + PlayerbotAI* botAI) { return new JanalaiBossEngagedByTanksTrigger(botAI); } + + static Trigger* janalai_boss_casts_flame_breath( + PlayerbotAI* botAI) { return new JanalaiBossCastsFlameBreathTrigger(botAI); } + + static Trigger* janalai_boss_summoning_fire_bombs( + PlayerbotAI* botAI) { return new JanalaiBossSummoningFireBombsTrigger(botAI); } + + static Trigger* janalai_amanishi_hatchers_spawned( + PlayerbotAI* botAI) { return new JanalaiAmanishiHatchersSpawnedTrigger(botAI); } + + // Halazzi + static Trigger* halazzi_pulling_boss( + PlayerbotAI* botAI) { return new HalazziPullingBossTrigger(botAI); } + + static Trigger* halazzi_boss_engaged_by_main_tank( + PlayerbotAI* botAI) { return new HalazziBossEngagedByMainTankTrigger(botAI); } + + static Trigger* halazzi_boss_summons_spirit_lynx( + PlayerbotAI* botAI) { return new HalazziBossSummonsSpiritLynxTrigger(botAI); } + + static Trigger* halazzi_determining_dps_target( + PlayerbotAI* botAI) { return new HalazziDeterminingDpsTargetTrigger(botAI); } + + // Hex Lord Malacrass + + static Trigger* hex_lord_malacrass_pulling_boss( + PlayerbotAI* botAI) { return new HexLordMalacrassPullingBossTrigger(botAI); } + + static Trigger* hex_lord_malacrass_determining_kill_order( + PlayerbotAI* botAI) { return new HexLordMalacrassDeterminingKillOrderTrigger(botAI); } + + static Trigger* hex_lord_malacrass_boss_is_channeling_whirlwind( + PlayerbotAI* botAI) { return new HexLordMalacrassBossIsChannelingWhirlwindTrigger(botAI); } + + static Trigger* hex_lord_malacrass_boss_has_spell_reflection( + PlayerbotAI* botAI) { return new HexLordMalacrassBossHasSpellReflectionTrigger(botAI); } + + static Trigger* hex_lord_malacrass_boss_placed_freezing_trap( + PlayerbotAI* botAI) { return new HexLordMalacrassBossPlacedFreezingTrapTrigger(botAI); } + + // Zul'jin + + static Trigger* zuljin_boss_engaged_by_tanks( + PlayerbotAI* botAI) { return new ZuljinBossEngagedByTanksTrigger(botAI); } + + static Trigger* zuljin_main_tank_needs_aggro_upon_pull_or_phase_change( + PlayerbotAI* botAI) { return new ZuljinMainTankNeedsAggroUponPullOrPhaseChangeTrigger(botAI); } + + static Trigger* zuljin_boss_is_channeling_whirlwind_in_troll_form( + PlayerbotAI* botAI) { return new ZuljinBossIsChannelingWhirlwindInTrollFormTrigger(botAI); } + + static Trigger* zuljin_boss_is_summoning_cyclones_in_eagle_form( + PlayerbotAI* botAI) { return new ZuljinBossIsSummoningCyclonesInEagleFormTrigger(botAI); } + + static Trigger* zuljin_boss_casts_aoe_abilities_in_dragonhawk_form( + PlayerbotAI* botAI) { return new ZuljinBossCastsAoeAbilitiesInDragonhawkFormTrigger(botAI); } +}; + +#endif diff --git a/src/Ai/Raid/ZulAman/Strategy/RaidZulAmanStrategy.cpp b/src/Ai/Raid/ZulAman/Strategy/RaidZulAmanStrategy.cpp new file mode 100644 index 000000000..4ba01a2a1 --- /dev/null +++ b/src/Ai/Raid/ZulAman/Strategy/RaidZulAmanStrategy.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include "RaidZulAmanStrategy.h" +#include "RaidZulAmanMultipliers.h" + +void RaidZulAmanStrategy::InitTriggers(std::vector& triggers) +{ + // Trash + triggers.push_back(new TriggerNode("amani'shi medicine man summoned ward", { + NextAction("amani'shi medicine man mark ward", ACTION_RAID + 1) })); + + // Akil'zon + triggers.push_back(new TriggerNode("akil'zon pulling boss", { + NextAction("akil'zon misdirect boss to main tank", ACTION_RAID + 2) })); + + triggers.push_back(new TriggerNode("akil'zon boss engaged by main tank", { + NextAction("akil'zon main tank position boss", ACTION_RAID + 1) })); + + triggers.push_back(new TriggerNode("akil'zon boss casts static disruption", { + NextAction("akil'zon spread ranged", ACTION_RAID + 1) })); + + triggers.push_back(new TriggerNode("akil'zon electrical storm incoming", { + NextAction("akil'zon move to eye of the storm", ACTION_EMERGENCY + 6) })); + + triggers.push_back(new TriggerNode("akil'zon bots need to prepare for electrical storm", { + NextAction("akil'zon manage electrical storm timer", ACTION_EMERGENCY + 10) })); + + // Nalorakk + triggers.push_back(new TriggerNode("nalorakk pulling boss", { + NextAction("nalorakk misdirect boss to main tank", ACTION_RAID + 1) })); + + triggers.push_back(new TriggerNode("nalorakk boss switches forms", { + NextAction("nalorakk tanks position boss", ACTION_EMERGENCY + 1) })); + + triggers.push_back(new TriggerNode("nalorakk boss casts surge", { + NextAction("nalorakk spread ranged", ACTION_RAID + 1) })); + + // Jan'alai + triggers.push_back(new TriggerNode("jan'alai pulling boss", { + NextAction("jan'alai misdirect boss to main tank", ACTION_RAID + 2) })); + + triggers.push_back(new TriggerNode("jan'alai boss engaged by main tank", { + NextAction("jan'alai main tank position boss", ACTION_RAID + 1) })); + + triggers.push_back(new TriggerNode("jan'alai boss casts flame breath", { + NextAction("jan'alai spread ranged in circle", ACTION_RAID + 1) })); + + triggers.push_back(new TriggerNode("jan'alai boss summoning fire bombs", { + NextAction("jan'alai avoid fire bombs", ACTION_EMERGENCY + 6) })); + + triggers.push_back(new TriggerNode("jan'alai amani'shi hatchers spawned", { + NextAction("jan'alai mark amani'shi hatchers", ACTION_RAID + 2) })); + + // Halazzi + triggers.push_back(new TriggerNode("halazzi pulling boss", { + NextAction("halazzi misdirect boss to main tank", ACTION_RAID + 2) })); + + triggers.push_back(new TriggerNode("halazzi boss engaged by main tank", { + NextAction("halazzi main tank position boss", ACTION_RAID + 1) })); + + triggers.push_back(new TriggerNode("halazzi boss summons spirit lynx", { + NextAction("halazzi first assist tank attack spirit lynx", ACTION_RAID + 1) })); + + triggers.push_back(new TriggerNode("halazzi determining dps target", { + NextAction("halazzi assign dps priority", ACTION_RAID + 1) })); + + // Hex Lord Malacrass + triggers.push_back(new TriggerNode("hex lord malacrass pulling boss", { + NextAction("hex lord malacrass misdirect boss to main tank", ACTION_RAID + 2) })); + + triggers.push_back(new TriggerNode("hex lord malacrass determining kill order", { + NextAction("hex lord malacrass assign dps priority", ACTION_RAID + 1) })); + + triggers.push_back(new TriggerNode("hex lord malacrass boss is channeling whirlwind", { + NextAction("hex lord malacrass run away from whirlwind", ACTION_EMERGENCY + 6) })); + + triggers.push_back(new TriggerNode("hex lord malacrass boss has spell reflection", { + NextAction("hex lord malacrass casters stop attacking", ACTION_EMERGENCY + 6) })); + + triggers.push_back(new TriggerNode("hex lord malacrass boss placed freezing trap", { + NextAction("hex lord malacrass move away from freezing trap", ACTION_EMERGENCY + 1) })); + + // Zul'jin + triggers.push_back(new TriggerNode("zul'jin main tank needs aggro upon pull or phase change", { + NextAction("zul'jin misdirect boss to main tank", ACTION_RAID + 2) })); + + triggers.push_back(new TriggerNode("zul'jin boss engaged by main tank", { + NextAction("zul'jin main tank position boss", ACTION_RAID + 1) })); + + triggers.push_back(new TriggerNode("zul'jin boss is channeling whirlwind in troll form", { + NextAction("zul'jin run away from whirlwind", ACTION_EMERGENCY + 6) })); + + triggers.push_back(new TriggerNode("zul'jin boss is summoning cyclones in eagle form", { + NextAction("zul'jin avoid cyclones", ACTION_EMERGENCY + 1) })); + + triggers.push_back(new TriggerNode("zul'jin boss casts aoe abilities in dragonhawk form", { + NextAction("zul'jin spread ranged", ACTION_RAID + 1) })); +} + +void RaidZulAmanStrategy::InitMultipliers(std::vector& multipliers) +{ + // Akil'zon + multipliers.push_back(new AkilzonDisableCombatFormationMoveMultiplier(botAI)); + multipliers.push_back(new AkilzonStayInEyeOfTheStormMultiplier(botAI)); + + // Nalorakk + multipliers.push_back(new NalorakkDisableTankActionsMultiplier(botAI)); + multipliers.push_back(new NalorakkControlMisdirectionMultiplier(botAI)); + + // Jan'alai + multipliers.push_back(new JanalaiDisableTankActionsMultiplier(botAI)); + multipliers.push_back(new JanalaiDisableCombatFormationMoveMultiplier(botAI)); + multipliers.push_back(new JanalaiStayAwayFromFireBombsMultiplier(botAI)); + multipliers.push_back(new JanalaiDoNotCrowdControlHatchersMultiplier(botAI)); + multipliers.push_back(new JanalaiDelayBloodlustAndHeroismMultiplier(botAI)); + + // Halazzi + multipliers.push_back(new HalazziDisableTankActionsMultiplier(botAI)); + multipliers.push_back(new HalazziControlMisdirectionMultiplier(botAI)); + + // Hex Lord Malacrass + multipliers.push_back(new HexLordMalacrassAvoidWhirlwindMultiplier(botAI)); + multipliers.push_back(new HexLordMalacrassStopAttackingDuringSpellReflectionMultiplier(botAI)); + multipliers.push_back(new HexLordMalacrassDoNotDispelUnstableAfflictionMultiplier(botAI)); + + // Zul'jin + multipliers.push_back(new ZuljinDisableTankFaceMultiplier(botAI)); + multipliers.push_back(new ZuljinAvoidWhirlwindMultiplier(botAI)); + multipliers.push_back(new ZuljinDisableAvoidAoeMultiplier(botAI)); + multipliers.push_back(new ZuljinDelayBloodlustAndHeroismMultiplier(botAI)); +} diff --git a/src/Ai/Raid/ZulAman/Strategy/RaidZulAmanStrategy.h b/src/Ai/Raid/ZulAman/Strategy/RaidZulAmanStrategy.h new file mode 100644 index 000000000..c49e08888 --- /dev/null +++ b/src/Ai/Raid/ZulAman/Strategy/RaidZulAmanStrategy.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_RAIDZULAMANSTRATEGY_H_ +#define _PLAYERBOT_RAIDZULAMANSTRATEGY_H_ + +#include "Strategy.h" +#include "Multiplier.h" + +class RaidZulAmanStrategy : public Strategy +{ +public: + RaidZulAmanStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} + + std::string const getName() override { return "zulaman"; } + + void InitTriggers(std::vector& triggers) override; + void InitMultipliers(std::vector& multipliers) override; +}; + +#endif diff --git a/src/Ai/Raid/ZulAman/Trigger/RaidZulAmanTriggers.cpp b/src/Ai/Raid/ZulAman/Trigger/RaidZulAmanTriggers.cpp new file mode 100644 index 000000000..ad338a5a7 --- /dev/null +++ b/src/Ai/Raid/ZulAman/Trigger/RaidZulAmanTriggers.cpp @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include "RaidZulAmanTriggers.h" +#include "RaidZulAmanHelpers.h" +#include "RaidZulAmanActions.h" +#include "Playerbots.h" +#include "RaidBossHelpers.h" + +using namespace ZulAmanHelpers; + +// Trash + +bool AmanishiMedicineManSummonedWardTrigger::IsActive() +{ + return AI_VALUE2(Unit*, "find target", "amani'shi medicine man"); +} + +// Akil'zon + +bool AkilzonPullingBossTrigger::IsActive() +{ + if (bot->getClass() != CLASS_HUNTER) + return false; + + Unit* akilzon = AI_VALUE2(Unit*, "find target", "akil'zon"); + return akilzon && akilzon->GetHealthPct() > 95.0f; +} + +bool AkilzonBossEngagedByTanksTrigger::IsActive() +{ + if (!botAI->IsTank(bot) || + !AI_VALUE2(Unit*, "find target", "akil'zon")) + return false; + + return !GetElectricalStormTarget(bot); +} + +bool AkilzonBossCastsStaticDisruptionTrigger::IsActive() +{ + if (!botAI->IsRanged(bot) || + !AI_VALUE2(Unit*, "find target", "akil'zon")) + return false; + + auto it = akilzonStormTimer.find(bot->GetMap()->GetInstanceId()); + if (it == akilzonStormTimer.end()) + return true; + + return !IsInStormWindow(it->second, std::time(nullptr)); +} + +bool AkilzonElectricalStormIncomingTrigger::IsActive() +{ + if (!AI_VALUE2(Unit*, "find target", "akil'zon")) + return false; + + auto it = akilzonStormTimer.find(bot->GetMap()->GetInstanceId()); + if (it == akilzonStormTimer.end()) + return false; + + return IsInStormWindow(it->second, std::time(nullptr)); +} + +bool AkilzonBotsNeedToPrepareForElectricalStormTrigger::IsActive() +{ + return IsMechanicTrackerBot(botAI, bot, ZULAMAN_MAP_ID); +} + +// Nalorakk + +bool NalorakkPullingBossTrigger::IsActive() +{ + if (bot->getClass() != CLASS_HUNTER) + return false; + + Unit* nalorakk = AI_VALUE2(Unit*, "find target", "nalorakk"); + return nalorakk && nalorakk->GetHealthPct() > 95.0f; +} + +bool NalorakkBossSwitchesFormsTrigger::IsActive() +{ + return (botAI->IsMainTank(bot) || botAI->IsAssistTankOfIndex(bot, 0, true)) && + AI_VALUE2(Unit*, "find target", "nalorakk"); +} + +bool NalorakkBossCastsSurgeTrigger::IsActive() +{ + return botAI->IsRanged(bot) && + AI_VALUE2(Unit*, "find target", "nalorakk"); +} + +// Jan'alai + +bool JanalaiPullingBossTrigger::IsActive() +{ + if (bot->getClass() != CLASS_HUNTER) + return false; + + Unit* janalai = AI_VALUE2(Unit*, "find target", "jan'alai"); + return janalai && janalai->GetHealthPct() > 95.0f; +} + +bool JanalaiBossEngagedByTanksTrigger::IsActive() +{ + if (!botAI->IsTank(bot) || + !AI_VALUE2(Unit*, "find target", "jan'alai")) + return false; + + return !HasFireBombNearby(botAI, bot); +} + +bool JanalaiBossCastsFlameBreathTrigger::IsActive() +{ + if (!botAI->IsRanged(bot) || + !AI_VALUE2(Unit*, "find target", "jan'alai") || + AI_VALUE2(Unit*, "find target", "amani dragonhawk hatchling")) + return false; + + return !HasFireBombNearby(botAI, bot); +} + +bool JanalaiBossSummoningFireBombsTrigger::IsActive() +{ + return AI_VALUE2(Unit*, "find target", "jan'alai") && + HasFireBombNearby(botAI, bot); +} + +bool JanalaiAmanishiHatchersSpawnedTrigger::IsActive() +{ + if (!botAI->IsRangedDps(bot) || + !AI_VALUE2(Unit*, "find target", "jan'alai")) + return false; + + return bot->FindNearestCreature( + static_cast(ZulAmanNPCs::NPC_AMANISHI_HATCHER), 40.0f); +} + +// Halazzi + +bool HalazziPullingBossTrigger::IsActive() +{ + if (bot->getClass() != CLASS_HUNTER) + return false; + + Unit* halazzi = AI_VALUE2(Unit*, "find target", "halazzi"); + return halazzi && halazzi->GetHealthPct() > 95.0f; +} + +bool HalazziBossEngagedByMainTankTrigger::IsActive() +{ + return botAI->IsMainTank(bot) && + AI_VALUE2(Unit*, "find target", "halazzi"); +} + +bool HalazziBossSummonsSpiritLynxTrigger::IsActive() +{ + return botAI->IsAssistTankOfIndex(bot, 0, true) && + AI_VALUE2(Unit*, "find target", "halazzi"); +} + +bool HalazziDeterminingDpsTargetTrigger::IsActive() +{ + return botAI->IsDps(bot) && + AI_VALUE2(Unit*, "find target", "halazzi"); +} + +// Hex Lord Malacrass + +bool HexLordMalacrassPullingBossTrigger::IsActive() +{ + if (bot->getClass() != CLASS_HUNTER) + return false; + + Unit* malacrass = AI_VALUE2(Unit*, "find target", "hex lord malacrass"); + return malacrass && malacrass->GetHealthPct() > 95.0f; +} + +bool HexLordMalacrassDeterminingKillOrderTrigger::IsActive() +{ + return botAI->IsDps(bot) && + AI_VALUE2(Unit*, "find target", "hex lord malacrass"); +} + +bool HexLordMalacrassBossIsChannelingWhirlwindTrigger::IsActive() +{ + Unit* malacrass = AI_VALUE2(Unit*, "find target", "hex lord malacrass"); + if (!malacrass || + !malacrass->HasAura(static_cast(ZulAmanSpells::SPELL_HEX_LORD_WHIRLWIND))) + return false; + + return !(botAI->IsTank(bot) && malacrass->GetVictim() == bot); +} + +bool HexLordMalacrassBossHasSpellReflectionTrigger::IsActive() +{ + if (!botAI->IsCaster(bot)) + return false; + + Unit* malacrass = AI_VALUE2(Unit*, "find target", "hex lord malacrass"); + return malacrass && + malacrass->HasAura(static_cast(ZulAmanSpells::SPELL_HEX_LORD_SPELL_REFLECTION)); +} + +bool HexLordMalacrassBossPlacedFreezingTrapTrigger::IsActive() +{ + return AI_VALUE2(Unit*, "find target", "hex lord malacrass") && + bot->FindNearestGameObject( + static_cast(ZulAmanObjects::GO_FREEZING_TRAP), 20.0f, true); +} + +// Zul'jin + +bool ZuljinMainTankNeedsAggroUponPullOrPhaseChangeTrigger::IsActive() +{ + if (bot->getClass() != CLASS_HUNTER) + return false; + + Unit* zuljin = AI_VALUE2(Unit*, "find target", "zul'jin"); + if (!zuljin) + return false; + + float hp = zuljin->GetHealthPct(); + + return (hp <= 100.0f && hp > 95.0f) || + (hp <= 80.0f && hp > 75.0f && + zuljin->HasAura(static_cast(ZulAmanSpells::SPELL_SHAPE_OF_THE_BEAR))) || + (hp <= 40.0f && hp > 35.0f && + zuljin->HasAura(static_cast(ZulAmanSpells::SPELL_SHAPE_OF_THE_LYNX))) || + (hp <= 20.0f && hp > 15.0f && + zuljin->HasAura(static_cast(ZulAmanSpells::SPELL_SHAPE_OF_THE_DRAGONHAWK))); +} + +bool ZuljinBossEngagedByTanksTrigger::IsActive() +{ + if (!botAI->IsTank(bot)) + return false; + + Unit* zuljin = AI_VALUE2(Unit*, "find target", "zul'jin"); + return zuljin && + !zuljin->HasAura(static_cast(ZulAmanSpells::SPELL_SHAPE_OF_THE_EAGLE)) && + !zuljin->HasAura(static_cast(ZulAmanSpells::SPELL_SHAPE_OF_THE_DRAGONHAWK)); +} + +bool ZuljinBossIsChannelingWhirlwindInTrollFormTrigger::IsActive() +{ + Unit* zuljin = AI_VALUE2(Unit*, "find target", "zul'jin"); + if (!zuljin || + !zuljin->HasAura(static_cast(ZulAmanSpells::SPELL_ZULJIN_WHIRLWIND))) + return false; + + return !(botAI->IsTank(bot) && zuljin->GetVictim() == bot); +} + +bool ZuljinBossIsSummoningCyclonesInEagleFormTrigger::IsActive() +{ + Unit* zuljin = AI_VALUE2(Unit*, "find target", "zul'jin"); + return zuljin && + zuljin->HasAura(static_cast(ZulAmanSpells::SPELL_SHAPE_OF_THE_EAGLE)); +} + +bool ZuljinBossCastsAoeAbilitiesInDragonhawkFormTrigger::IsActive() +{ + if (!botAI->IsRanged(bot)) + return false; + + Unit* zuljin = AI_VALUE2(Unit*, "find target", "zul'jin"); + return zuljin && + zuljin->HasAura(static_cast(ZulAmanSpells::SPELL_SHAPE_OF_THE_DRAGONHAWK)); +} diff --git a/src/Ai/Raid/ZulAman/Trigger/RaidZulAmanTriggers.h b/src/Ai/Raid/ZulAman/Trigger/RaidZulAmanTriggers.h new file mode 100644 index 000000000..8d0cc1e54 --- /dev/null +++ b/src/Ai/Raid/ZulAman/Trigger/RaidZulAmanTriggers.h @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_RAIDZULAMANTRIGGERS_H +#define _PLAYERBOT_RAIDZULAMANTRIGGERS_H + +#include "Trigger.h" + +// Trash + +class AmanishiMedicineManSummonedWardTrigger : public Trigger +{ +public: + AmanishiMedicineManSummonedWardTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "amani'shi medicine man summoned ward") {} + bool IsActive() override; +}; + +// Akil'zon + +class AkilzonPullingBossTrigger : public Trigger +{ +public: + AkilzonPullingBossTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "akil'zon pulling boss") {} + bool IsActive() override; +}; + +class AkilzonBossEngagedByTanksTrigger : public Trigger +{ +public: + AkilzonBossEngagedByTanksTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "akil'zon boss engaged by tanks") {} + bool IsActive() override; +}; + +class AkilzonBossCastsStaticDisruptionTrigger : public Trigger +{ +public: + AkilzonBossCastsStaticDisruptionTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "akil'zon boss casts static disruption") {} + bool IsActive() override; +}; + +class AkilzonElectricalStormIncomingTrigger : public Trigger +{ +public: + AkilzonElectricalStormIncomingTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "akil'zon electrical storm incoming") {} + bool IsActive() override; +}; + +class AkilzonBotsNeedToPrepareForElectricalStormTrigger : public Trigger +{ +public: + AkilzonBotsNeedToPrepareForElectricalStormTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "akil'zon bots need to prepare for electrical storm") {} + bool IsActive() override; +}; + +// Nalorakk + +class NalorakkPullingBossTrigger : public Trigger +{ +public: + NalorakkPullingBossTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "nalorakk pulling boss") {} + bool IsActive() override; +}; + +class NalorakkBossSwitchesFormsTrigger : public Trigger +{ +public: + NalorakkBossSwitchesFormsTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "nalorakk boss switches forms") {} + bool IsActive() override; +}; + +class NalorakkBossCastsSurgeTrigger : public Trigger +{ +public: + NalorakkBossCastsSurgeTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "nalorakk boss casts surge") {} + bool IsActive() override; +}; + +// Jan'alai + +class JanalaiPullingBossTrigger : public Trigger +{ +public: + JanalaiPullingBossTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "jan'alai pulling boss") {} + bool IsActive() override; +}; + +class JanalaiBossEngagedByTanksTrigger : public Trigger +{ +public: + JanalaiBossEngagedByTanksTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "jan'alai boss engaged by tanks") {} + bool IsActive() override; +}; + +class JanalaiBossCastsFlameBreathTrigger : public Trigger +{ +public: + JanalaiBossCastsFlameBreathTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "jan'alai boss casts flame breath") {} + bool IsActive() override; +}; + +class JanalaiBossSummoningFireBombsTrigger : public Trigger +{ +public: + JanalaiBossSummoningFireBombsTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "jan'alai boss summoning fire bombs") {} + bool IsActive() override; +}; + +class JanalaiAmanishiHatchersSpawnedTrigger : public Trigger +{ +public: + JanalaiAmanishiHatchersSpawnedTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "jan'alai amani'shi hatchers spawned") {} + bool IsActive() override; +}; + +// Halazzi + +class HalazziPullingBossTrigger : public Trigger +{ +public: + HalazziPullingBossTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "halazzi pulling boss") {} + bool IsActive() override; +}; + +class HalazziBossEngagedByMainTankTrigger : public Trigger +{ +public: + HalazziBossEngagedByMainTankTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "halazzi boss engaged by main tank") {} + bool IsActive() override; +}; + +class HalazziBossSummonsSpiritLynxTrigger : public Trigger +{ +public: + HalazziBossSummonsSpiritLynxTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "halazzi boss summons spirit lynx") {} + bool IsActive() override; +}; + +class HalazziDeterminingDpsTargetTrigger : public Trigger +{ +public: + HalazziDeterminingDpsTargetTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "halazzi determining dps target") {} + bool IsActive() override; +}; + +// Hex Lord Malacrass + +class HexLordMalacrassPullingBossTrigger : public Trigger +{ +public: + HexLordMalacrassPullingBossTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "hex lord malacrass pulling boss") {} + bool IsActive() override; +}; + +class HexLordMalacrassDeterminingKillOrderTrigger : public Trigger +{ +public: + HexLordMalacrassDeterminingKillOrderTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "hex lord malacrass determining kill order") {} + bool IsActive() override; +}; + +class HexLordMalacrassBossIsChannelingWhirlwindTrigger : public Trigger +{ +public: + HexLordMalacrassBossIsChannelingWhirlwindTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "hex lord malacrass boss is channeling whirlwind") {} + bool IsActive() override; +}; + +class HexLordMalacrassBossHasSpellReflectionTrigger : public Trigger +{ +public: + HexLordMalacrassBossHasSpellReflectionTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "hex lord malacrass boss has spell reflection") {} + bool IsActive() override; +}; + +class HexLordMalacrassBossPlacedFreezingTrapTrigger : public Trigger +{ +public: + HexLordMalacrassBossPlacedFreezingTrapTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "hex lord malacrass boss placed freezing trap") {} + bool IsActive() override; +}; + +// Zul'jin + +class ZuljinMainTankNeedsAggroUponPullOrPhaseChangeTrigger : public Trigger +{ +public: + ZuljinMainTankNeedsAggroUponPullOrPhaseChangeTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "zul'jin main tank needs aggro upon pull or phase change") {} + bool IsActive() override; +}; + +class ZuljinBossEngagedByTanksTrigger : public Trigger +{ +public: + ZuljinBossEngagedByTanksTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "zul'jin boss engaged by tanks") {} + bool IsActive() override; +}; + +class ZuljinBossIsChannelingWhirlwindInTrollFormTrigger : public Trigger +{ +public: + ZuljinBossIsChannelingWhirlwindInTrollFormTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "zul'jin boss is channeling whirlwind in troll form") {} + bool IsActive() override; +}; + +class ZuljinBossIsSummoningCyclonesInEagleFormTrigger : public Trigger +{ +public: + ZuljinBossIsSummoningCyclonesInEagleFormTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "zul'jin boss is summoning cyclones in eagle form") {} + bool IsActive() override; +}; + +class ZuljinBossCastsAoeAbilitiesInDragonhawkFormTrigger : public Trigger +{ +public: +ZuljinBossCastsAoeAbilitiesInDragonhawkFormTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "zul'jin boss casts aoe abilities in dragonhawk form") {} + bool IsActive() override; +}; + +#endif diff --git a/src/Ai/Raid/ZulAman/Util/RaidZulAmanHelpers.cpp b/src/Ai/Raid/ZulAman/Util/RaidZulAmanHelpers.cpp new file mode 100644 index 000000000..77c268817 --- /dev/null +++ b/src/Ai/Raid/ZulAman/Util/RaidZulAmanHelpers.cpp @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include "RaidZulAmanHelpers.h" +#include "Group.h" +#include "Playerbots.h" + +namespace ZulAmanHelpers +{ + // General + Position FindSafestNearbyPosition(Player* bot, + const std::vector& hazards, const Position& safeZoneCenter, + float safeZoneRadius, float hazardRadius, bool requireSafePath) + { + constexpr float searchStep = M_PI / 8.0f; + constexpr float distanceStep = 1.0f; + + Position bestPos; + float minMoveDistance = std::numeric_limits::max(); + bool foundSafe = false; + + for (float distance = 0.0f; + distance <= safeZoneRadius; distance += distanceStep) + { + for (float angle = 0.0f; angle < 2 * M_PI; angle += searchStep) + { + float x = bot->GetPositionX() + distance * std::cos(angle); + float y = bot->GetPositionY() + distance * std::sin(angle); + + if (safeZoneCenter.GetExactDist2d(x, y) > safeZoneRadius) + continue; + + if (!IsPositionSafeFromHazards(x, y, hazards, hazardRadius)) + continue; + + Position testPos(x, y, bot->GetPositionZ()); + + bool pathSafe = true; + if (requireSafePath) + { + pathSafe = + IsPathSafeFromHazards(bot->GetPosition(), testPos, hazards, hazardRadius); + if (!pathSafe) + continue; + } + + float moveDistance = bot->GetExactDist2d(x, y); + if (!foundSafe || moveDistance < minMoveDistance) + { + bestPos = testPos; + minMoveDistance = moveDistance; + foundSafe = pathSafe; + } + } + + if (foundSafe) + break; + } + + return bestPos; + } + + bool IsPathSafeFromHazards(const Position& start, const Position& end, + const std::vector& 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(i) / numChecks; + float checkX = start.GetPositionX() + dx * ratio; + float checkY = start.GetPositionY() + dy * ratio; + + if (!IsPositionSafeFromHazards(checkX, checkY, hazards, hazardRadius)) + return false; + } + + return true; + } + + bool IsPositionSafeFromHazards( + float x, float y, const std::vector& hazards, float hazardRadius) + { + for (Unit* hazard : hazards) + { + if (hazard->GetDistance2d(x, y) < hazardRadius) + return false; + } + + return true; + } + + std::vector GetAllHazardTriggers(Player* bot, uint32 entry, float searchRadius) + { + std::vector triggers; + std::list creatureList; + bot->GetCreatureListWithEntryInGrid(creatureList, entry, searchRadius); + + for (Creature* creature : creatureList) + { + if (creature && creature->IsAlive()) + triggers.push_back(creature); + } + + return triggers; + } + + // Akil'zon + const Position AKILZON_TANK_POSITION = { 378.369f, 1407.718f, 74.797f }; + std::unordered_map akilzonStormTimer; + + bool IsInStormWindow(time_t start, time_t now) + { + time_t elapsed = now - start; + uint32 seconds = elapsed % 60; + return elapsed >= 55 && (seconds >= 55 || seconds < 10); + } + + Player* GetElectricalStormTarget(Player* bot) + { + Group* group = bot->GetGroup(); + if (!group) + return nullptr; + + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (member && + member->HasAura(static_cast(ZulAmanSpells::SPELL_ELECTRICAL_STORM))) + return member; + } + + return nullptr; + } + + // Nalorakk + const Position NALORAKK_TANK_POSITION = { -80.208f, 1324.530f, 40.942f }; + + // Jan'alai + const Position JANALAI_TANK_POSITION = { -33.873f, 1149.571f, 19.146f }; + + bool HasFireBombNearby(PlayerbotAI* botAI, Player* bot) + { + constexpr float searchRadius = 30.0f; + std::list creatureList; + bot->GetCreatureListWithEntryInGrid( + creatureList, static_cast(ZulAmanNPCs::NPC_FIRE_BOMB), searchRadius); + + for (Creature* creature : creatureList) + { + if (creature && creature->IsAlive()) + return true; + } + + return false; + } + + std::pair GetAmanishiHatcherPair(PlayerbotAI* botAI) + { + Unit* lowest = nullptr; + Unit* highest = nullptr; + + for (auto const& guid : + botAI->GetAiObjectContext()->GetValue("possible targets no los")->Get()) + { + Unit* unit = botAI->GetUnit(guid); + if (unit && + unit->GetEntry() == static_cast(ZulAmanNPCs::NPC_AMANISHI_HATCHER)) + { + if (!lowest || unit->GetGUID().GetCounter() < lowest->GetGUID().GetCounter()) + lowest = unit; + + if (!highest || unit->GetGUID().GetCounter() > highest->GetGUID().GetCounter()) + highest = unit; + } + } + + return {lowest, highest}; + } + + // Halazzi + const Position HALAZZI_TANK_POSITION = { 370.733f, 1131.202f, 6.516f }; + + // Zul'jin + const Position ZULJIN_TANK_POSITION = { 120.210f, 705.564f, 45.111f }; +} diff --git a/src/Ai/Raid/ZulAman/Util/RaidZulAmanHelpers.h b/src/Ai/Raid/ZulAman/Util/RaidZulAmanHelpers.h new file mode 100644 index 000000000..4c27f0238 --- /dev/null +++ b/src/Ai/Raid/ZulAman/Util/RaidZulAmanHelpers.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_RAIDZULAMANHELPERS_H_ +#define _PLAYERBOT_RAIDZULAMANHELPERS_H_ + +#include + +#include "AiObject.h" +#include "Position.h" +#include "Unit.h" + +namespace ZulAmanHelpers +{ + enum class ZulAmanSpells : uint32 + { + // Akil'zon + SPELL_ELECTRICAL_STORM = 43648, + + // Nalorakk + SPELL_BEARFORM = 42377, + + // Hex Lord Malacrass + SPELL_HEX_LORD_WHIRLWIND = 43442, + SPELL_HEX_LORD_SPELL_REFLECTION = 43443, + SPELL_UNSTABLE_AFFLICTION = 43522, + + // Zul'jin + SPELL_ZULJIN_WHIRLWIND = 17207, + SPELL_SHAPE_OF_THE_BEAR = 42594, + SPELL_SHAPE_OF_THE_EAGLE = 42606, + SPELL_SHAPE_OF_THE_LYNX = 42607, + SPELL_SHAPE_OF_THE_DRAGONHAWK = 42608, + // SPELL_CLAW_RAGE = 43149, // Would require getting Zul'jin's bossai + + // Hunter + SPELL_MISDIRECTION = 35079, + }; + + enum class ZulAmanNPCs : uint32 + { + // Trash + NPC_AMANI_HEALING_WARD = 23757, + NPC_AMANI_PROTECTIVE_WARD = 23822, + + // Jan'alai + NPC_AMANI_DRAGONHAWK_HATCHLING = 23598, + NPC_AMANISHI_HATCHER = 23818, + NPC_FIRE_BOMB = 23920, + + // Halazzi + NPC_CORRUPTED_LIGHTNING_TOTEM = 24224, + + // Hex Lord Malacrass + NPC_HEX_LORD_MALACRASS = 24239, + NPC_ALYSON_ANTILLE = 24240, + NPC_THURG = 24241, + NPC_SLITHER = 24242, + NPC_LORD_RAADAN = 24243, + NPC_GAZAKROTH = 24244, + NPC_FENSTALKER = 24245, + NPC_DARKHEART = 24246, + NPC_KORAGG = 24247, + + // Zul'jin + NPC_FEATHER_VORTEX = 24136, + }; + + enum class ZulAmanObjects : uint32 + { + GO_FREEZING_TRAP = 186669, + }; + + // General + constexpr uint32 ZULAMAN_MAP_ID = 568; + Position FindSafestNearbyPosition( + Player* bot, const std::vector& hazards, const Position& center, + float safeZoneRadius, float hazardRadius, bool requireSafePath); + bool IsPathSafeFromHazards( + const Position& start, const Position& end, + const std::vector& hazards, float hazardRadius); + bool IsPositionSafeFromHazards( + float x, float y, const std::vector& hazards, float hazardRadius); + std::vector GetAllHazardTriggers( + Player* bot, uint32 entry, float searchRadius); + + // Akil'zon + extern const Position AKILZON_TANK_POSITION; + extern std::unordered_map akilzonStormTimer; + bool IsInStormWindow(time_t start, time_t now); + Player* GetElectricalStormTarget(Player* bot); + + // Nalorakk + extern const Position NALORAKK_TANK_POSITION; + + // Jan'alai + extern const Position JANALAI_TANK_POSITION; + bool HasFireBombNearby(PlayerbotAI* botAI, Player* bot); + std::pair GetAmanishiHatcherPair(PlayerbotAI* botAI); + + // Halazzi + extern const Position HALAZZI_TANK_POSITION; + + // Zul'jin + extern const Position ZULJIN_TANK_POSITION; +} + +#endif diff --git a/src/Bot/Engine/BuildSharedActionContexts.cpp b/src/Bot/Engine/BuildSharedActionContexts.cpp index dc4ccdfe8..8fbb6c135 100644 --- a/src/Bot/Engine/BuildSharedActionContexts.cpp +++ b/src/Bot/Engine/BuildSharedActionContexts.cpp @@ -11,6 +11,7 @@ #include "Ai/Raid/Magtheridon/RaidMagtheridonActionContext.h" #include "Ai/Raid/SerpentshrineCavern/RaidSSCActionContext.h" #include "Ai/Raid/TempestKeep/RaidTempestKeepActionContext.h" +#include "Ai/Raid/ZulAman/RaidZulAmanActionContext.h" #include "Ai/Raid/ObsidianSanctum/RaidOsActionContext.h" #include "Ai/Raid/EyeOfEternity/RaidEoEActionContext.h" #include "Ai/Raid/VaultOfArchavon/RaidVoAActionContext.h" @@ -32,6 +33,7 @@ void AiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList