From 410ce134fe1e09ac4b76a4bfd4699957db20a2ea Mon Sep 17 00:00:00 2001 From: HennyWilly <5954598+HennyWilly@users.noreply.github.com> Date: Sat, 2 May 2026 21:19:11 +0200 Subject: [PATCH] Fix Deep Breath issues during Onyxia encounter (#2318) ## Pull Request Description The current strategy for Onyxia causes bots to get hit by her breath attack relatively consistently during phase 2. The problem was that the safe zone coordinates always use the bot's z-coordinate. If the bots are standing at the lower altitude part of the arena, `SearchForBestPath` inside `MoveTo` causes `INVALID_HEIGHT`, resulting in the bot not moving at all and getting hit by the breath attack. This PR fixes this behavior by using the actual terrain z-coordinates for the predefined safe zones instead of always using the bot's z-coordinate. These values are chosen to ensure valid pathfinding regardless of the bot's current elevation. Additionally, bots now interrupt their spells if they are not inside a safe zone during the breath. This causes the bots to immediately start running instead of finishing their casts first. ## Feature Evaluation - Describe the **minimum logic** required to achieve the intended behavior. Replaced the use of `bot->GetPositionZ()` in `GetSafeZonesForBreath` with predefined safe zone z-coordinates to ensure valid pathfinding. Added `AttackStop` and `InterruptNonMeleeSpells` to guarantee immediate movement when outside safe zones. No additional condition checks or branching logic were introduced. - Describe the **processing cost** when this logic executes across many bots. Minimal. The logic only runs within the Onyxia encounter script and calling `AttackStop` and `InterruptNonMeleeSpells` should be negligible. ## How to Test the Changes Enter Onyxia's Lair (10, 25 or 40 (mod-individual-progression)) and engage Onyxia with the appropriate number of bots. During phase 2 (Onyxia takes off), check if the bots move to the safe zones during the breath attack. Tip: Mark Onyxia as moon (RTI), so that phase 2 doesn't end too quickly. ## Impact Assessment - Does this change increase per-bot/per-tick processing or risk scaling poorly with thousands of bots? - - [ ] No, not at all - - [x] Minimal impact (**explain below**) - - [ ] Moderate impact (**explain below**) The calls of `AttackStop` and `InterruptNonMeleeSpells` cause minimal overhead compared to the original strategy. This should be negligible. - Does this change modify default bot behavior? - - [ ] No - - [x] Yes (**explain why**) Yes (encounter-specific). Bots will now interrupt casts earlier during Onyxia phase 2 to prioritize movement to safe zones. - Does this change add new decision branches or increase maintenance complexity? - - [x] No - - [ ] Yes (**explain below**) ## AI Assistance Was AI assistance used while working on this change? - - [x] No - - [ ] Yes (**explain below**) ## Final Checklist - - [x] Stability is not compromised. - - [x] Performance impact is understood, tested, and acceptable. - - [x] Added logic complexity is justified and explained. - - [x] Any new bot dialogue lines are translated. - - [x] Documentation updated if needed (Conf comments, WiKi commands). ## Notes for Reviewers The strategy for Onyxia might need additional work: For example, the Onyxian Lair Guards are completely ignored while whelps are alive and their Blast Nova doesn't get handled at all. This PR focuses on fixing the Deep Breath behavior. Handling of Onyxian Lair Guards is not included and should be implemented in a separate PR. --- .../Raid/Onyxia/Action/RaidOnyxiaActions.cpp | 6 +++- src/Ai/Raid/Onyxia/Action/RaidOnyxiaActions.h | 30 ++++++++++--------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/Ai/Raid/Onyxia/Action/RaidOnyxiaActions.cpp b/src/Ai/Raid/Onyxia/Action/RaidOnyxiaActions.cpp index e2fe3cbe6..5b470ee5c 100644 --- a/src/Ai/Raid/Onyxia/Action/RaidOnyxiaActions.cpp +++ b/src/Ai/Raid/Onyxia/Action/RaidOnyxiaActions.cpp @@ -99,8 +99,12 @@ bool RaidOnyxiaMoveToSafeZoneAction::Execute(Event /*event*/) if (bot->IsWithinDist2d(bestZone->pos.GetPositionX(), bestZone->pos.GetPositionY(), bestZone->radius)) return false; // Already safe + // Stop current spell first + bot->AttackStop(); + bot->InterruptNonMeleeSpells(false); + // bot->Yell("Moving to Safe Zone!", LANG_UNIVERSAL); - return MoveTo(bot->GetMapId(), bestZone->pos.GetPositionX(), bestZone->pos.GetPositionY(), bot->GetPositionZ(), + return MoveTo(bot->GetMapId(), bestZone->pos.GetPositionX(), bestZone->pos.GetPositionY(), bestZone->pos.GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT); } diff --git a/src/Ai/Raid/Onyxia/Action/RaidOnyxiaActions.h b/src/Ai/Raid/Onyxia/Action/RaidOnyxiaActions.h index 3943aaf60..d5b8eafd9 100644 --- a/src/Ai/Raid/Onyxia/Action/RaidOnyxiaActions.h +++ b/src/Ai/Raid/Onyxia/Action/RaidOnyxiaActions.h @@ -2,7 +2,6 @@ #ifndef _PLAYERBOT_RAIDONYXIAACTIONS_H_ #define _PLAYERBOT_RAIDONYXIAACTIONS_H_ -#include "Action.h" #include "AttackAction.h" #include "GenericSpellActions.h" #include "MovementActions.h" @@ -45,42 +44,45 @@ public: bool Execute(Event event) override; private: - std::vector GetSafeZonesForBreath(uint32 spellId) + static std::vector GetSafeZonesForBreath(uint32 spellId) { - // Define your safe zone coordinates based on the map - // Example assumes Onyxia's lair map coordinates - float z = bot->GetPositionZ(); // Stay at current height + // Safe zone coordinates based on the map + // Assumes Onyxia's lair map coordinates switch (spellId) { case 17086: // N to S case 18351: // S to N - return {SafeZone{Position(-10.0f, -180.0f, z), 5.0f}, - SafeZone{Position(-20.0f, -250.0f, z), 5.0f}}; // Bottom Safe Zone + return { + SafeZone{Position(-10.0f, -180.0f, -87.0f), 5.0f}, + SafeZone{Position(-20.0f, -250.0f, -88.0f), 5.0f} + }; // Bottom Safe Zone case 18576: // E to W case 18609: // W to E return { - SafeZone{Position(20.0f, -210.0f, z), 5.0f}, - SafeZone{Position(-75.0f, -210.0f, z), 5.0f}, + SafeZone{Position(20.0f, -210.0f, -85.5f), 5.0f}, + SafeZone{Position(-75.0f, -210.0f, -83.4f), 5.0f}, }; // Left Safe Zone case 18564: // SE to NW case 18584: // NW to SE return { - SafeZone{Position(-60.0f, -195.0f, z), 5.0f}, - SafeZone{Position(10.0f, -240.0f, z), 5.0f}, + SafeZone{Position(-60.0f, -195.0f, -85.0f), 5.0f}, + SafeZone{Position(10.0f, -240.0f, -85.9f), 5.0f}, }; // NW Safe Zone case 18596: // SW to NE case 18617: // NE to SW return { - SafeZone{Position(7.0f, -185.0f, z), 5.0f}, - SafeZone{Position(-60.0f, -240.0f, z), 5.0f}, + SafeZone{Position(7.0f, -185.0f, -86.2f), 5.0f}, + SafeZone{Position(-60.0f, -240.0f, -85.2f), 5.0f}, }; // NE Safe Zone default: - return {SafeZone{Position(0.0f, 0.0f, z), 5.0f}}; // Fallback center - shouldn't ever happen + return { + SafeZone{Position(-40.0f, -214.0f, -86.6f), 5.0f} + }; // Fallback center - shouldn't ever happen } } };