diff --git a/src/strategy/raids/karazhan/RaidKarazhanActions.cpp b/src/strategy/raids/karazhan/RaidKarazhanActions.cpp index 35b1e285..8ec3ea91 100644 --- a/src/strategy/raids/karazhan/RaidKarazhanActions.cpp +++ b/src/strategy/raids/karazhan/RaidKarazhanActions.cpp @@ -29,7 +29,12 @@ bool KarazhanAttumenTheHuntsmanStackBehindAction::Execute(Event event) float rx = x + cos(orientation) * distance; float ry = y + sin(orientation) * distance; - return MoveTo(bot->GetMapId(), rx, ry, z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + if (bot->GetExactDist2d(rx, ry) > 1.0f) + { + return MoveTo(bot->GetMapId(), rx, ry, z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + + return false; } bool KarazhanAttumenTheHuntsmanStackBehindAction::isUseful() @@ -83,6 +88,9 @@ bool KarazhanMaidenOfVirtuePositionBossAction::Execute(Event event) float targetY = healer->GetPositionY() + sin(angle) * 6.0f; float targetZ = healer->GetPositionZ(); { + bot->AttackStop(); + bot->InterruptNonMeleeSpells(false); + return MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, true, MovementPriority::MOVEMENT_COMBAT); } @@ -98,6 +106,9 @@ bool KarazhanMaidenOfVirtuePositionBossAction::Execute(Event event) float mX = KARAZHAN_MAIDEN_OF_VIRTUE_BOSS_POSITION.GetPositionX() + (dX / distanceToBossPosition) * maxDistance; float mY = KARAZHAN_MAIDEN_OF_VIRTUE_BOSS_POSITION.GetPositionY() + (dY / distanceToBossPosition) * maxDistance; { + bot->AttackStop(); + bot->InterruptNonMeleeSpells(false); + return MoveTo(bot->GetMapId(), mX, mY, bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); } @@ -144,6 +155,9 @@ bool KarazhanMaidenOfVirtuePositionRangedAction::Execute(Event event) const float maxDistance = 2.0f; if (distance > maxDistance) { + bot->AttackStop(); + bot->InterruptNonMeleeSpells(false); + return MoveTo(bot->GetMapId(), KARAZHAN_MAIDEN_OF_VIRTUE_RANGED_POSITION[index].GetPositionX(), KARAZHAN_MAIDEN_OF_VIRTUE_RANGED_POSITION[index].GetPositionY(), bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); @@ -206,6 +220,9 @@ bool KarazhanBigBadWolfRunAwayAction::Execute(Event event) target = KARAZHAN_BIG_BAD_WOLF_RUN_POSITION[currentIndex]; } + bot->AttackStop(); + bot->InterruptNonMeleeSpells(false); + return MoveTo(bot->GetMapId(), target.GetPositionX(), target.GetPositionY(), target.GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_FORCED); } @@ -326,6 +343,9 @@ bool KarazhanTheCuratorSpreadRangedAction::Execute(Event event) if (nearestPlayer) { + bot->AttackStop(); + bot->InterruptNonMeleeSpells(false); + return FleePosition(nearestPlayer->GetPosition(), minDistance); } @@ -368,11 +388,11 @@ bool KarazhanShadeOfAranArcaneExplosionRunAwayAction::Execute(Event event) const float safeDistance = 20.0f; const float distance = bot->GetDistance2d(boss); - bot->AttackStop(); - bot->InterruptNonMeleeSpells(false); - if (distance < safeDistance) { + bot->AttackStop(); + bot->InterruptNonMeleeSpells(false); + return MoveAway(boss, safeDistance - distance); } @@ -469,36 +489,27 @@ bool KarazhanNetherspiteBlockRedBeamAction::Execute(Event event) Unit* boss = AI_VALUE2(Unit*, "find target", "netherspite"); Unit* redPortal = bot->FindNearestCreature(NPC_RED_PORTAL, 150.0f); - Group* group = bot->GetGroup(); - if (!group) - { - return false; - } - - Player* eligibleTank = nullptr; - for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) - { - Player* member = itr->GetSource(); - if (!member || !member->IsAlive() || !botAI->IsTank(member) || !GET_PLAYERBOT_AI(member) || - member->HasAura(SPELL_NETHER_EXHAUSTION_RED)) - { - continue; - } - eligibleTank = member; - break; - } RaidKarazhanHelpers karazhanHelper(botAI); + static std::map wasBlockingRedBeam; + ObjectGuid botGuid = bot->GetGUID(); + auto [redBlocker, greenBlocker, blueBlocker] = karazhanHelper.GetCurrentBeamBlockers(); + bool isBlockingNow = (bot == redBlocker); + bool wasBlocking = wasBlockingRedBeam[botGuid]; + Position beamPos = karazhanHelper.GetPositionOnBeam(boss, redPortal, 18.0f); - if (bot == eligibleTank) + if (isBlockingNow) { - std::map ph; - ph["%player"] = bot->GetName(); - std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( - "netherspite_beam_blocking_red", "%player is moving to block the red beam!", ph); - bot->Yell(text, LANG_UNIVERSAL); + if (!wasBlocking) + { + std::map ph; + ph["%player"] = bot->GetName(); + std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( + "netherspite_beam_blocking_red", "%player is moving to block the red beam!", ph); + bot->Yell(text, LANG_UNIVERSAL); + } + wasBlockingRedBeam[botGuid] = true; - ObjectGuid botGuid = bot->GetGUID(); uint32 intervalSecs = 5; if (beamMoveTimes[botGuid] == 0) @@ -516,7 +527,6 @@ bool KarazhanNetherspiteBlockRedBeamAction::Execute(Event event) return MoveTo(bot->GetMapId(), beamPos.GetPositionX(), beamPos.GetPositionY(), beamPos.GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_FORCED); } - else { float bx = boss->GetPositionX(); @@ -544,6 +554,7 @@ bool KarazhanNetherspiteBlockRedBeamAction::Execute(Event event) } } + wasBlockingRedBeam[botGuid] = false; return false; } @@ -556,11 +567,6 @@ bool KarazhanNetherspiteBlockRedBeamAction::isUseful() static std::map lastBossBanishState; bool bossIsBanished = boss && boss->HasAura(SPELL_NETHERSPITE_BANISHED); - if (!boss || !redPortal || bossIsBanished) - { - return false; - } - if (lastBossBanishState[botGuid] != bossIsBanished) { if (!bossIsBanished) @@ -571,10 +577,10 @@ bool KarazhanNetherspiteBlockRedBeamAction::isUseful() lastBossBanishState[botGuid] = bossIsBanished; } - return true; + return boss && redPortal && !bossIsBanished; } -// Two non-Rogue/Warrior DPS bots will block the blue beam for each phase (swap at 25 debuff stacks) +// Two non-Rogue/Warrior DPS bots will block the blue beam for each phase (swap at 26 debuff stacks) // When avoiding void zones, blocking bots will move along the beam to continue blocking bool KarazhanNetherspiteBlockBlueBeamAction::Execute(Event event) { @@ -582,11 +588,10 @@ bool KarazhanNetherspiteBlockBlueBeamAction::Execute(Event event) Unit* bluePortal = bot->FindNearestCreature(NPC_BLUE_PORTAL, 150.0f); RaidKarazhanHelpers karazhanHelper(botAI); - std::vector blueBlockers = karazhanHelper.GetBlueBlockers(); static std::map wasBlockingBlueBeam; ObjectGuid botGuid = bot->GetGUID(); - Player* assignedBlueBlocker = blueBlockers.empty() ? nullptr : blueBlockers.front(); - bool isBlockingNow = (bot == assignedBlueBlocker); + auto [redBlocker, greenBlocker, blueBlocker] = karazhanHelper.GetCurrentBeamBlockers(); + bool isBlockingNow = (bot == blueBlocker); bool wasBlocking = wasBlockingBlueBeam[botGuid]; if (wasBlocking && !isBlockingNow) @@ -596,8 +601,8 @@ bool KarazhanNetherspiteBlockBlueBeamAction::Execute(Event event) std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( "netherspite_beam_leaving_blue", "%player is leaving the blue beam--next blocker up!", ph); bot->Yell(text, LANG_UNIVERSAL); - wasBlockingBlueBeam[botGuid] = false; + return false; } @@ -632,7 +637,7 @@ bool KarazhanNetherspiteBlockBlueBeamAction::Execute(Event event) float bestDist = 150.0f; Position bestPos; bool found = false; - for (float dist = 18.0f; dist <= 25.0f; dist += 0.5f) + for (float dist = 18.0f; dist <= 30.0f; dist += 0.5f) { float candidateX = bx + dx * dist; float candidateY = by + dy * dist; @@ -662,6 +667,9 @@ bool KarazhanNetherspiteBlockBlueBeamAction::Execute(Event event) } if (found) { + bot->AttackStop(); + bot->InterruptNonMeleeSpells(false); + return MoveTo(bot->GetMapId(), bestPos.GetPositionX(), bestPos.GetPositionY(), bestPos.GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_FORCED); } @@ -681,7 +689,7 @@ bool KarazhanNetherspiteBlockBlueBeamAction::isUseful() return boss && bluePortal && !boss->HasAura(SPELL_NETHERSPITE_BANISHED); } -// Two healer bots will block the green beam for each phase (swap at 25 debuff stacks) +// Two healer bots will block the green beam for each phase (swap at 26 debuff stacks) // OR one rogue or DPS warrior bot will block the green beam for an entire phase (if they begin the phase as the blocker) // When avoiding void zones, blocking bots will move along the beam to continue blocking bool KarazhanNetherspiteBlockGreenBeamAction::Execute(Event event) @@ -690,11 +698,10 @@ bool KarazhanNetherspiteBlockGreenBeamAction::Execute(Event event) Unit* greenPortal = bot->FindNearestCreature(NPC_GREEN_PORTAL, 150.0f); RaidKarazhanHelpers karazhanHelper(botAI); - std::vector greenBlockers = karazhanHelper.GetGreenBlockers(); static std::map wasBlockingGreenBeam; ObjectGuid botGuid = bot->GetGUID(); - Player* assignedGreenBlocker = greenBlockers.empty() ? nullptr : greenBlockers.front(); - bool isBlockingNow = (bot == assignedGreenBlocker); + auto [redBlocker, greenBlocker, blueBlocker] = karazhanHelper.GetCurrentBeamBlockers(); + bool isBlockingNow = (bot == greenBlocker); bool wasBlocking = wasBlockingGreenBeam[botGuid]; if (wasBlocking && !isBlockingNow) @@ -704,8 +711,8 @@ bool KarazhanNetherspiteBlockGreenBeamAction::Execute(Event event) std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( "netherspite_beam_leaving_green", "%player is leaving the green beam--next blocker up!", ph); bot->Yell(text, LANG_UNIVERSAL); - wasBlockingGreenBeam[botGuid] = false; + return false; } @@ -740,7 +747,7 @@ bool KarazhanNetherspiteBlockGreenBeamAction::Execute(Event event) float bestDist = 150.0f; Position bestPos; bool found = false; - for (float dist = 18.0f; dist <= 25.0f; dist += 0.5f) + for (float dist = 18.0f; dist <= 30.0f; dist += 0.5f) { float candidateX = bx + dx * dist; float candidateY = by + dy * dist; @@ -770,6 +777,9 @@ bool KarazhanNetherspiteBlockGreenBeamAction::Execute(Event event) } if (found) { + bot->AttackStop(); + bot->InterruptNonMeleeSpells(false); + return MoveTo(bot->GetMapId(), bestPos.GetPositionX(), bestPos.GetPositionY(), bestPos.GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_FORCED); } @@ -796,7 +806,6 @@ bool KarazhanNetherspiteAvoidBeamAndVoidZoneAction::Execute(Event event) RaidKarazhanHelpers karazhanHelper(botAI); auto [redBlocker, greenBlocker, blueBlocker] = karazhanHelper.GetCurrentBeamBlockers(); std::vector voidZones = karazhanHelper.GetAllVoidZones(); - bool nearVoidZone = false; for (Unit* vz : voidZones) { @@ -806,7 +815,6 @@ bool KarazhanNetherspiteAvoidBeamAndVoidZoneAction::Execute(Event event) break; } } - struct BeamAvoid { Unit* portal; float minDist, maxDist; }; std::vector beams; Unit* redPortal = bot->FindNearestCreature(NPC_RED_PORTAL, 150.0f); @@ -863,14 +871,14 @@ bool KarazhanNetherspiteAvoidBeamAndVoidZoneAction::Execute(Event event) return false; } - const float minMoveDist = 3.0f, maxSearchDist = 20.0f, stepAngle = M_PI/18.0f, stepDist = 0.5f; + const float minMoveDist = 2.0f, maxSearchDist = 30.0f, stepAngle = M_PI/18.0f, stepDist = 0.5f; float bossZ = boss->GetPositionZ(); Position bestCandidate; float bestDist = 0.0f; bool found = false; for (float angle = 0; angle < 2 * M_PI; angle += stepAngle) { - for (float dist = 5.0f; dist <= maxSearchDist; dist += stepDist) + for (float dist = 2.0f; dist <= maxSearchDist; dist += stepDist) { float cx = bot->GetPositionX() + cos(angle) * dist; float cy = bot->GetPositionY() + sin(angle) * dist; @@ -922,9 +930,13 @@ bool KarazhanNetherspiteAvoidBeamAndVoidZoneAction::Execute(Event event) bestCandidate.GetPositionY(), bestCandidate.GetPositionZ(), voidZones, 4.0f)) { + bot->AttackStop(); + bot->InterruptNonMeleeSpells(false); + return MoveTo(bot->GetMapId(), bestCandidate.GetPositionX(), bestCandidate.GetPositionY(), bestCandidate.GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_COMBAT); } + return false; } @@ -989,9 +1001,9 @@ bool KarazhanPrinceMalchezaarNonTankAvoidHazardAction::Execute(Event event) RaidKarazhanHelpers karazhanHelper(botAI); std::vector infernals = karazhanHelper.GetSpawnedInfernals(); - const float minSafeBossDistance = 35.0f; - const float maxSafeBossDistance = 40.0f; - const float safeInfernalDistance = 22.0f; + const float minSafeBossDistance = 34.0f; + const float maxSafeBossDistance = 60.0f; + const float safeInfernalDistance = 23.0f; const float stepSize = 0.5f; const int numAngles = 64; float bx = bot->GetPositionX(); @@ -1016,19 +1028,25 @@ bool KarazhanPrinceMalchezaarNonTankAvoidHazardAction::Execute(Event event) float x = bossX + dx * dist; float y = bossY + dy * dist; float destZ = bossZ; - if (!bot->IsWithinLOS(x, y, destZ)) + float destX = x, destY = y, destZ2 = destZ; + if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bx, by, bz, destX, destY, destZ2, true)) { continue; } - bool pathSafe = karazhanHelper.IsStraightPathSafe(Position(bx, by, bz), Position(x, y, destZ), + float distFromBoss = sqrt(pow(destX - bossX, 2) + pow(destY - bossY, 2)); + if (distFromBoss < minSafeBossDistance) + { + continue; + } + bool pathSafe = karazhanHelper.IsStraightPathSafe(Position(bx, by, bz), Position(destX, destY, destZ2), infernals, safeInfernalDistance, stepSize); - float moveDist = sqrt(pow(x - bx, 2) + pow(y - by, 2)); + float moveDist = sqrt(pow(destX - bx, 2) + pow(destY - by, 2)); if (pathSafe && moveDist < bestMoveDist) { bestMoveDist = moveDist; - bestDestX = x; - bestDestY = y; - bestDestZ = destZ; + bestDestX = destX; + bestDestY = destY; + bestDestZ = destZ2; found = true; } } @@ -1062,63 +1080,41 @@ bool KarazhanPrinceMalchezaarNonTankAvoidHazardAction::Execute(Event event) float bestMoveDist = std::numeric_limits::max(); float bestDestX = bx, bestDestY = by, bestDestZ = bz; bool found = false; - bool usedArc = false; for (int i = 0; i < numAngles; ++i) { float angle = (2 * M_PI * i) / numAngles; float dx = cos(angle); float dy = sin(angle); - for (float dist = stepSize; dist <= 35.0f; dist += stepSize) + for (float dist = stepSize; dist <= maxSafeBossDistance; dist += stepSize) { float x = bossX + dx * dist; float y = bossY + dy * dist; float destZ = bossZ; - if (!bot->IsWithinLOS(x, y, destZ)) + float destX = x, destY = y, destZ2 = destZ; + if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bossX, bossY, bossZ, destX, destY, destZ2, true)) { continue; } - bool pathSafe = karazhanHelper.IsStraightPathSafe(Position(bx, by, bz), Position(x, y, destZ), - infernals, safeInfernalDistance, stepSize); - float moveDist = sqrt(pow(x - bx, 2) + pow(y - by, 2)); - if (pathSafe && moveDist < bestMoveDist) + bool destSafe = true; + for (Unit* infernal : infernals) + { + float infernalDist = sqrt(pow(destX - infernal->GetPositionX(), 2) + pow(destY - infernal->GetPositionY(), 2)); + if (infernalDist < safeInfernalDistance) + { + destSafe = false; + break; + } + } + if (!destSafe) + continue; + float moveDist = sqrt(pow(destX - bx, 2) + pow(destY - by, 2)); + if (moveDist < bestMoveDist) { bestMoveDist = moveDist; - bestDestX = x; - bestDestY = y; - bestDestZ = destZ; + bestDestX = destX; + bestDestY = destY; + bestDestZ = destZ2; found = true; - usedArc = false; - } - if (!pathSafe) - { - Position arcPoint = karazhanHelper.CalculateArcPoint(Position(bx, by, bz), Position(x, y, destZ), - Position(bossX, bossY, bossZ)); - if (!bot->IsWithinLOS(arcPoint.GetPositionX(), arcPoint.GetPositionY(), arcPoint.GetPositionZ())) - { - continue; - } - bool arcSafe = true; - for (Unit* infernal : infernals) - { - float infernalDist = sqrt(pow(arcPoint.GetPositionX() - infernal->GetPositionX(), 2) + - pow(arcPoint.GetPositionY() - infernal->GetPositionY(), 2)); - if (infernalDist < safeInfernalDistance) - { - arcSafe = false; - break; - } - } - float arcMoveDist = sqrt(pow(arcPoint.GetPositionX() - bx, 2) + - pow(arcPoint.GetPositionY() - by, 2)); - if (arcSafe && arcMoveDist < bestMoveDist) - { - bestMoveDist = arcMoveDist; - bestDestX = arcPoint.GetPositionX(); - bestDestY = arcPoint.GetPositionY(); - bestDestZ = arcPoint.GetPositionZ(); - found = true; - usedArc = true; - } } } } @@ -1149,17 +1145,13 @@ bool KarazhanPrinceMalchezaarTankAvoidHazardAction::Execute(Event event) RaidKarazhanHelpers karazhanHelper(botAI); std::vector infernals = karazhanHelper.GetSpawnedInfernals(); - const float safeInfernalDistance = 30.0f; + const float safeInfernalDistance = 28.0f; const float stepSize = 0.5f; const int numAngles = 64; const float maxSampleDist = 60.0f; float bx = bot->GetPositionX(); float by = bot->GetPositionY(); float bz = bot->GetPositionZ(); - float bestMoveDist = std::numeric_limits::max(); - float bestDestX = bx, bestDestY = by, bestDestZ = bz; - bool found = false; - bool usedArc = false; bool nearInfernal = false; for (Unit* infernal : infernals) @@ -1172,6 +1164,10 @@ bool KarazhanPrinceMalchezaarTankAvoidHazardAction::Execute(Event event) } } + float bestMoveDist = std::numeric_limits::max(); + float bestDestX = bx, bestDestY = by, bestDestZ = bz; + bool found = false; + if (nearInfernal) { for (int i = 0; i < numAngles; ++i) @@ -1184,54 +1180,72 @@ bool KarazhanPrinceMalchezaarTankAvoidHazardAction::Execute(Event event) float x = bx + dx * dist; float y = by + dy * dist; float z = bz; - if (!bot->IsWithinLOS(x, y, z)) - { + + float destX = x, destY = y, destZ = z; + if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bx, by, bz, destX, destY, destZ, true)) continue; + + bool destSafe = true; + for (Unit* infernal : infernals) + { + float infernalDist = sqrt(pow(destX - infernal->GetPositionX(), 2) + pow(destY - infernal->GetPositionY(), 2)); + if (infernalDist < safeInfernalDistance) + { + destSafe = false; + break; + } } - bool safe = karazhanHelper.IsStraightPathSafe(Position(bx, by, bz), Position(x, y, z), - infernals, safeInfernalDistance, stepSize); - float moveDist = sqrt(pow(x - bx, 2) + pow(y - by, 2)); - if (safe && moveDist < bestMoveDist) + if (!destSafe) + continue; + + bool pathSafe = karazhanHelper.IsStraightPathSafe(Position(bx, by, bz), Position(destX, destY, destZ), + infernals, safeInfernalDistance, stepSize); + float moveDist = sqrt(pow(destX - bx, 2) + pow(destY - by, 2)); + if (pathSafe && moveDist < bestMoveDist) { bestMoveDist = moveDist; - bestDestX = x; - bestDestY = y; - bestDestZ = z; + bestDestX = destX; + bestDestY = destY; + bestDestZ = destZ; found = true; - usedArc = false; } - if (!safe) + } + } + if (!found) + { + for (int i = 0; i < numAngles; ++i) + { + float angle = (2 * M_PI * i) / numAngles; + float dx = cos(angle); + float dy = sin(angle); + for (float dist = stepSize; dist <= maxSampleDist; dist += stepSize) { - Position arcPoint = karazhanHelper.CalculateArcPoint(Position(bx, by, bz), Position(x, y, z), - Position(bx, by, bz)); - float arcX = arcPoint.GetPositionX(); - float arcY = arcPoint.GetPositionY(); - float arcZ = arcPoint.GetPositionZ(); - float arcDestX = arcX, arcDestY = arcY, arcDestZ = arcZ; - if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bx, by, bz, arcDestX, arcDestY, arcDestZ)) - { + float x = bx + dx * dist; + float y = by + dy * dist; + float z = bz; + + float destX = x, destY = y, destZ = z; + if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bx, by, bz, destX, destY, destZ, true)) continue; - } - bool arcSafe = true; + + bool destSafe = true; for (Unit* infernal : infernals) { - float infernalDist = sqrt(pow(arcPoint.GetPositionX() - infernal->GetPositionX(), 2) + - pow(arcPoint.GetPositionY() - infernal->GetPositionY(), 2)); + float infernalDist = sqrt(pow(destX - infernal->GetPositionX(), 2) + pow(destY - infernal->GetPositionY(), 2)); if (infernalDist < safeInfernalDistance) { - arcSafe = false; + destSafe = false; break; } } - float arcMoveDist = sqrt(pow(arcPoint.GetPositionX() - bx, 2) + pow(arcPoint.GetPositionY() - by, 2)); - if (arcSafe && arcMoveDist < bestMoveDist) + float moveDist = sqrt(pow(destX - bx, 2) + pow(destY - by, 2)); + if (destSafe && moveDist < bestMoveDist) { - bestMoveDist = arcMoveDist; - bestDestX = arcPoint.GetPositionX(); - bestDestY = arcPoint.GetPositionY(); - bestDestZ = arcPoint.GetPositionZ(); + bestMoveDist = moveDist; + bestDestX = destX; + bestDestY = destY; + bestDestZ = destZ; found = true; - usedArc = true; } } } @@ -1240,12 +1254,12 @@ bool KarazhanPrinceMalchezaarTankAvoidHazardAction::Execute(Event event) { bot->AttackStop(); bot->InterruptNonMeleeSpells(false); - + return MoveTo(bot->GetMapId(), bestDestX, bestDestY, bestDestZ, false, false, false, true, MovementPriority::MOVEMENT_COMBAT); } } - + return false; } diff --git a/src/strategy/raids/karazhan/RaidKarazhanHelpers.cpp b/src/strategy/raids/karazhan/RaidKarazhanHelpers.cpp index 19dd0b33..b4d6d5df 100644 --- a/src/strategy/raids/karazhan/RaidKarazhanHelpers.cpp +++ b/src/strategy/raids/karazhan/RaidKarazhanHelpers.cpp @@ -132,7 +132,28 @@ bool RaidKarazhanHelpers::IsFlameWreathActive() return false; } -// Blue beam blockers: non-Rogue/Warrior DPS bots, no Nether Exhaustion Blue and <25 stacks of Blue Beam debuff +// Red beam blockers: tank bots, no Nether Exhaustion Red +std::vector RaidKarazhanHelpers::GetRedBlockers() +{ + std::vector redBlockers; + if (Group* group = bot->GetGroup()) + { + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (!member || !member->IsAlive() || !botAI->IsTank(member) || !GET_PLAYERBOT_AI(member) || + member->HasAura(SPELL_NETHER_EXHAUSTION_RED)) + { + continue; + } + redBlockers.push_back(member); + } + } + + return redBlockers; +} + +// Blue beam blockers: non-Rogue/Warrior DPS bots, no Nether Exhaustion Blue and ≤25 stacks of Blue Beam debuff std::vector RaidKarazhanHelpers::GetBlueBlockers() { std::vector blueBlockers; @@ -150,7 +171,7 @@ std::vector RaidKarazhanHelpers::GetBlueBlockers() bool isRogue = member->getClass() == CLASS_ROGUE; bool hasExhaustion = member->HasAura(SPELL_NETHER_EXHAUSTION_BLUE); Aura* blueBuff = member->GetAura(SPELL_BLUE_BEAM_DEBUFF); - bool overStack = blueBuff && blueBuff->GetStackAmount() >= 25; + bool overStack = blueBuff && blueBuff->GetStackAmount() >= 26; if (isDps && !isWarrior && !isRogue && !hasExhaustion && !overStack) { blueBlockers.push_back(member); @@ -163,7 +184,7 @@ std::vector RaidKarazhanHelpers::GetBlueBlockers() // Green beam blockers: // (1) Rogue and non-tank Warrior bots, no Nether Exhaustion Green -// (2) Healer bots, no Nether Exhaustion Green and <25 stacks of Green Beam debuff +// (2) Healer bots, no Nether Exhaustion Green and ≤25 stacks of Green Beam debuff std::vector RaidKarazhanHelpers::GetGreenBlockers() { std::vector greenBlockers; @@ -178,7 +199,7 @@ std::vector RaidKarazhanHelpers::GetGreenBlockers() } bool hasExhaustion = member->HasAura(SPELL_NETHER_EXHAUSTION_GREEN); Aura* greenBuff = member->GetAura(SPELL_GREEN_BEAM_DEBUFF); - bool overStack = greenBuff && greenBuff->GetStackAmount() >= 25; + bool overStack = greenBuff && greenBuff->GetStackAmount() >= 26; bool isRogue = member->getClass() == CLASS_ROGUE; bool isDpsWarrior = member->getClass() == CLASS_WARRIOR && botAI->IsDps(member); bool eligibleRogueWarrior = (isRogue || isDpsWarrior) && !hasExhaustion; @@ -221,47 +242,91 @@ Position RaidKarazhanHelpers::GetPositionOnBeam(Unit* boss, Unit* portal, float std::tuple RaidKarazhanHelpers::GetCurrentBeamBlockers() { + static ObjectGuid currentRedBlocker; + static ObjectGuid currentGreenBlocker; + static ObjectGuid currentBlueBlocker; + Player* redBlocker = nullptr; Player* greenBlocker = nullptr; Player* blueBlocker = nullptr; - std::vector redBlockers; - if (Group* group = bot->GetGroup()) - { - for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) - { - Player* member = itr->GetSource(); - if (!member || !member->IsAlive() || !botAI->IsTank(member) || !GET_PLAYERBOT_AI(member) || - member->HasAura(SPELL_NETHER_EXHAUSTION_RED)) - { - continue; - } - redBlockers.push_back(member); - } - } + std::vector redBlockers = GetRedBlockers(); if (!redBlockers.empty()) { - redBlocker = redBlockers.front(); + auto it = std::find_if(redBlockers.begin(), redBlockers.end(), [](Player* p) + { + return p && p->GetGUID() == currentRedBlocker; + }); + if (it != redBlockers.end()) + { + redBlocker = *it; + } + else + { + redBlocker = redBlockers.front(); + } + currentRedBlocker = redBlocker ? redBlocker->GetGUID() : ObjectGuid::Empty; } + else + { + currentRedBlocker = ObjectGuid::Empty; + redBlocker = nullptr; + } + std::vector greenBlockers = GetGreenBlockers(); if (!greenBlockers.empty()) { - greenBlocker = greenBlockers.front(); + auto it = std::find_if(greenBlockers.begin(), greenBlockers.end(), [](Player* p) + { + return p && p->GetGUID() == currentGreenBlocker; + }); + if (it != greenBlockers.end()) + { + greenBlocker = *it; + } + else + { + greenBlocker = greenBlockers.front(); + } + currentGreenBlocker = greenBlocker ? greenBlocker->GetGUID() : ObjectGuid::Empty; } - std::vector blueBlockers = GetBlueBlockers(); - if (!blueBlockers.empty()) + else { - blueBlocker = blueBlockers.front(); + currentGreenBlocker = ObjectGuid::Empty; + greenBlocker = nullptr; } + std::vector blueBlockers = GetBlueBlockers(); + if (!blueBlockers.empty()) + { + auto it = std::find_if(blueBlockers.begin(), blueBlockers.end(), [](Player* p) + { + return p && p->GetGUID() == currentBlueBlocker; + }); + if (it != blueBlockers.end()) + { + blueBlocker = *it; + } + else + { + blueBlocker = blueBlockers.front(); + } + currentBlueBlocker = blueBlocker ? blueBlocker->GetGUID() : ObjectGuid::Empty; + } + else + { + currentBlueBlocker = ObjectGuid::Empty; + blueBlocker = nullptr; + } + return std::make_tuple(redBlocker, greenBlocker, blueBlocker); } std::vector RaidKarazhanHelpers::GetAllVoidZones() { std::vector voidZones; - const float radius = 15.0f; - const GuidVector npcs = botAI->GetAiObjectContext()->GetValue("nearest hostile npcs")->Get(); + const float radius = 30.0f; + const GuidVector npcs = botAI->GetAiObjectContext()->GetValue("nearest npcs")->Get(); for (const auto& npcGuid : npcs) { Unit* unit = botAI->GetUnit(npcGuid); @@ -280,7 +345,7 @@ std::vector RaidKarazhanHelpers::GetAllVoidZones() } bool RaidKarazhanHelpers::IsSafePosition(float x, float y, float z, - const std::vector& hazards, float hazardRadius) + const std::vector& hazards, float hazardRadius) { for (Unit* hazard : hazards) { @@ -297,7 +362,7 @@ bool RaidKarazhanHelpers::IsSafePosition(float x, float y, float z, std::vector RaidKarazhanHelpers::GetSpawnedInfernals() const { std::vector infernals; - const GuidVector npcs = botAI->GetAiObjectContext()->GetValue("nearest hostile npcs")->Get(); + const GuidVector npcs = botAI->GetAiObjectContext()->GetValue("nearest npcs")->Get(); for (const auto& npcGuid : npcs) { Unit* unit = botAI->GetUnit(npcGuid); @@ -342,44 +407,3 @@ bool RaidKarazhanHelpers::IsStraightPathSafe(const Position& start, const Positi return true; } - -Position RaidKarazhanHelpers::CalculateArcPoint(const Position& current, const Position& target, const Position& center) -{ - float arcFraction = 0.25f; - float currentX = current.GetPositionX() - center.GetPositionX(); - float currentY = current.GetPositionY() - center.GetPositionY(); - float targetX = target.GetPositionX() - center.GetPositionX(); - float targetY = target.GetPositionY() - center.GetPositionY(); - - float currentDist = std::sqrt(currentX * currentX + currentY * currentY); - float targetDist = std::sqrt(targetX * targetX + targetY * targetY); - if (currentDist == 0.0f || targetDist == 0.0f) - { - return current; - } - - currentX /= currentDist; - currentY /= currentDist; - targetX /= targetDist; - targetY /= targetDist; - - float dotProduct = currentX * targetX + currentY * targetY; - dotProduct = std::max(-1.0f, std::min(1.0f, dotProduct)); - float angle = std::acos(dotProduct); - float crossProduct = currentX * targetY - currentY * targetX; - float stepAngle = angle * arcFraction; - if (crossProduct < 0) - { - stepAngle = -stepAngle; - } - - float cos_a = std::cos(stepAngle); - float sin_a = std::sin(stepAngle); - float rotatedX = currentX * cos_a - currentY * sin_a; - float rotatedY = currentX * sin_a + currentY * cos_a; - float desiredDist = currentDist * 0.9f + targetDist * 0.1f; - - return Position(center.GetPositionX() + rotatedX * desiredDist, - center.GetPositionY() + rotatedY * desiredDist, - current.GetPositionZ()); -} diff --git a/src/strategy/raids/karazhan/RaidKarazhanHelpers.h b/src/strategy/raids/karazhan/RaidKarazhanHelpers.h index 2746b09d..89d06a95 100644 --- a/src/strategy/raids/karazhan/RaidKarazhanHelpers.h +++ b/src/strategy/raids/karazhan/RaidKarazhanHelpers.h @@ -70,6 +70,7 @@ public: Unit* GetNearestPlayerInRadius(float /*radius*/ = 5.0f); bool IsFlameWreathActive(); Position GetPositionOnBeam(Unit* boss, Unit* portal, float distanceFromBoss); + std::vector GetRedBlockers(); std::vector GetBlueBlockers(); std::vector GetGreenBlockers(); std::tuple GetCurrentBeamBlockers(); @@ -79,7 +80,6 @@ public: std::vector GetSpawnedInfernals() const; bool IsStraightPathSafe(const Position& start, const Position& target, const std::vector& hazards, float hazardRadius, float stepSize); - Position CalculateArcPoint(const Position& current, const Position& target, const Position& center); }; #endif diff --git a/src/strategy/raids/karazhan/RaidKarazhanMultipliers.cpp b/src/strategy/raids/karazhan/RaidKarazhanMultipliers.cpp index 01c1c179..ac235603 100644 --- a/src/strategy/raids/karazhan/RaidKarazhanMultipliers.cpp +++ b/src/strategy/raids/karazhan/RaidKarazhanMultipliers.cpp @@ -5,6 +5,7 @@ #include "AttackAction.h" #include "DruidBearActions.h" #include "DruidCatActions.h" +#include "RogueActions.h" #include "WarriorActions.h" static bool IsChargeAction(Action* action) @@ -15,6 +16,20 @@ static bool IsChargeAction(Action* action) dynamic_cast(action); } +float KarazhanAttumenTheHuntsmanMultiplier::GetValue(Action* action) +{ + RaidKarazhanHelpers karazhanHelper(botAI); + Unit* boss = karazhanHelper.GetFirstAliveUnitByEntry(NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED); + if (boss && !(botAI->IsTank(bot) && botAI->HasAggro(boss) && boss->GetVictim() == bot) && + (dynamic_cast(action) && + !dynamic_cast(action))) + { + return 0.0f; + } + + return 1.0f; +} + float KarazhanBigBadWolfMultiplier::GetValue(Action* action) { Unit* boss = AI_VALUE2(Unit*, "find target", "the big bad wolf"); @@ -61,7 +76,6 @@ float KarazhanShadeOfAranMultiplier::GetValue(Action* action) } bool flameWreathActive = boss->HasAura(SPELL_FLAME_WREATH); - if (!flameWreathActive && bot->GetGroup()) { for (GroupReference* itr = bot->GetGroup()->GetFirstMember(); itr != nullptr; itr = itr->next()) @@ -74,7 +88,6 @@ float KarazhanShadeOfAranMultiplier::GetValue(Action* action) } } } - if (flameWreathActive) { if (dynamic_cast(action) || IsChargeAction(action)) @@ -94,6 +107,11 @@ float KarazhanNetherspiteBlueAndGreenBeamMultiplier::GetValue(Action* action) return 1.0f; } + if (dynamic_cast(action) || dynamic_cast(action)) + { + return 0.0f; + } + RaidKarazhanHelpers karazhanHelper(botAI); auto [redBlocker /*unused*/, greenBlocker, blueBlocker] = karazhanHelper.GetCurrentBeamBlockers(); bool isBlocker = (bot == greenBlocker || bot == blueBlocker); @@ -101,9 +119,9 @@ float KarazhanNetherspiteBlueAndGreenBeamMultiplier::GetValue(Action* action) { Unit* bluePortal = bot->FindNearestCreature(NPC_BLUE_PORTAL, 150.0f); Unit* greenPortal = bot->FindNearestCreature(NPC_GREEN_PORTAL, 150.0f); - bool inBeam = false; - for (Unit* portal : {bluePortal, greenPortal}) { + for (Unit* portal : {bluePortal, greenPortal}) + { if (!portal) { continue; @@ -121,18 +139,18 @@ float KarazhanNetherspiteBlueAndGreenBeamMultiplier::GetValue(Action* action) float t = (botdx * dx + botdy * dy); float beamX = bx + dx * t, beamY = by + dy * t; float distToBeam = sqrt(pow(bot->GetPositionX() - beamX, 2) + pow(bot->GetPositionY() - beamY, 2)); - if (distToBeam < 5.0f && t > 0.0f && t < length) + if (distToBeam < 0.3f && t > 0.0f && t < length) { inBeam = true; break; } } - if (inBeam) { std::vector voidZones = karazhanHelper.GetAllVoidZones(); bool inVoidZone = false; - for (Unit* vz : voidZones) { + for (Unit* vz : voidZones) + { if (bot->GetExactDist2d(vz) < 4.0f) { inVoidZone = true; @@ -160,9 +178,13 @@ float KarazhanNetherspiteRedBeamMultiplier::GetValue(Action* action) return 1.0f; } + if (dynamic_cast(action)) + { + return 0.0f; + } + RaidKarazhanHelpers karazhanHelper(botAI); auto [redBlocker, greenBlocker /*unused*/, blueBlocker /*unused*/] = karazhanHelper.GetCurrentBeamBlockers(); - static std::map beamMoveTimes; static std::map lastBeamMoveSideways; ObjectGuid botGuid = bot->GetGUID(); @@ -198,11 +220,9 @@ float KarazhanNetherspiteRedBeamMultiplier::GetValue(Action* action) lastBeamMoveSideways[botGuid] = !lastBeamMoveSideways[botGuid]; beamMoveTimes[botGuid] = time(nullptr); } - Position targetPos = lastBeamMoveSideways[botGuid] ? sidewaysPos : blockingPos; float distToTarget = bot->GetExactDist2d(targetPos.GetPositionX(), targetPos.GetPositionY()); const float positionTolerance = 1.5f; - if (distToTarget < positionTolerance) { if (dynamic_cast(action) || IsChargeAction(action)) @@ -219,12 +239,28 @@ float KarazhanNetherspiteRedBeamMultiplier::GetValue(Action* action) float KarazhanPrinceMalchezaarMultiplier::GetValue(Action* action) { Unit* boss = AI_VALUE2(Unit*, "find target", "prince malchezaar"); + if (!boss || !boss->IsAlive()) + { + return 1.0f; + } - if (boss && botAI->IsMelee(bot) && bot->HasAura(SPELL_ENFEEBLE) && + if (dynamic_cast(action)) + { + return 0.0f; + } + + if (botAI->IsMelee(bot) && bot->HasAura(SPELL_ENFEEBLE) && !dynamic_cast(action)) { return 0.0f; } + if (botAI->IsRanged(bot) && bot->HasAura(SPELL_ENFEEBLE) && + (dynamic_cast(action) && + !dynamic_cast(action))) + { + return 0.0f; + } + return 1.0f; } diff --git a/src/strategy/raids/karazhan/RaidKarazhanMultipliers.h b/src/strategy/raids/karazhan/RaidKarazhanMultipliers.h index d29aa027..c8a4ba37 100644 --- a/src/strategy/raids/karazhan/RaidKarazhanMultipliers.h +++ b/src/strategy/raids/karazhan/RaidKarazhanMultipliers.h @@ -3,6 +3,13 @@ #include "Multiplier.h" +class KarazhanAttumenTheHuntsmanMultiplier : public Multiplier +{ +public: + KarazhanAttumenTheHuntsmanMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "karazhan attumen the huntsman multiplier") {} + virtual float GetValue(Action* action); +}; + class KarazhanBigBadWolfMultiplier : public Multiplier { public: