From 5ceb60ffeb98d71139bffa8d1287577bbcef37d6 Mon Sep 17 00:00:00 2001 From: agatho Date: Wed, 4 Feb 2026 09:19:45 +0100 Subject: [PATCH] feat(movement): Task 3 COMPLETE - Final 3 Dungeon files migrated to BotMovementController MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This completes Task 3 (Movement Generator Replacement) at 100% - all 33 files migrated from legacy MotionMaster to validated BotMovementController. Final migration details: DungeonAutonomyManager.cpp (3 MovePoint → MoveTo migrations): - Line 739: Pack navigation movement - Line 765: Healer positioning behind tank - Line 790: DPS positioning behind tank - Kept MoveChase at line 429 as legacy (combat engagement) DungeonBehavior.cpp (5 MovePoint → MoveTo migrations): - Lines 663-664: Tank optimal position - Lines 703-704: Healer safe position - Lines 748-749: DPS optimal position - Lines 880-881: Player optimal position - Lines 983-984: Safe spot avoidance EncounterStrategy.cpp (7 MovePoint → MoveTo migrations): - Lines 440-441: AoE avoidance - Lines 580-581: Optimal positioning - Lines 665-666: Danger zone avoidance - Line 1421: General positioning - Lines 1577-1578: Target positioning - Line 1742: Movement positioning - Lines 1805-1806: Stack on tank positioning All migrations follow enterprise pattern: - BotAI::MoveTo() with ground/collision/liquid validation - Fallback to legacy MotionMaster if validation fails - Safe bot AI retrieval via GetBotAI() helper - Added PlayerBotHelpers.h includes where needed TASK 3 STATISTICS: - Files migrated: 33/33 (100%) - Total usages: 70 (69 migrated + 1 legacy MoveChase) - HIGH PRIORITY: 13 files (combat systems) ✓ - MEDIUM PRIORITY: 9 files (ClassAI/Actions) ✓ - LOW PRIORITY: 11 files (dungeon/travel/behavior tree) ✓ Build: Successful (RelWithDebInfo, worldserver.exe created) Test: Ready for production testing Co-Authored-By: Claude Opus 4.5 Signed-off-by: luis --- .../Dungeon/DungeonAutonomyManager.cpp | 48 ++++++- .../Playerbot/Dungeon/DungeonBehavior.cpp | 95 +++++++++++-- .../Playerbot/Dungeon/EncounterStrategy.cpp | 131 +++++++++++++++--- 3 files changed, 236 insertions(+), 38 deletions(-) diff --git a/src/modules/Playerbot/Dungeon/DungeonAutonomyManager.cpp b/src/modules/Playerbot/Dungeon/DungeonAutonomyManager.cpp index 115e0b290..5342f2347 100644 --- a/src/modules/Playerbot/Dungeon/DungeonAutonomyManager.cpp +++ b/src/modules/Playerbot/Dungeon/DungeonAutonomyManager.cpp @@ -23,6 +23,7 @@ #include "MotionMaster.h" #include "MovementGenerator.h" #include "DBCEnums.h" // For ChrSpecialization enum +#include "Core/PlayerBotHelpers.h" namespace Playerbot { @@ -735,8 +736,21 @@ bool DungeonAutonomyManager::UpdateTankAI(Player* tank, BotAI* ai, Group* group, TrashPack* pack = coordinator->GetCurrentPullTarget(); if (pack) { - // Move toward next pack - tank->GetMotionMaster()->MovePoint(0, pack->x, pack->y, pack->z); + // Use validated pathfinding for pack navigation + Position dest(pack->x, pack->y, pack->z, 0.0f); + if (BotAI* ai = GetBotAI(tank)) + { + if (!ai->MoveTo(dest, true)) + { + // Fallback to legacy if validation fails + tank->GetMotionMaster()->MovePoint(0, pack->x, pack->y, pack->z); + } + } + else + { + // Non-bot player - use standard movement + tank->GetMotionMaster()->MovePoint(0, pack->x, pack->y, pack->z); + } return true; } } @@ -762,7 +776,20 @@ bool DungeonAutonomyManager::UpdateHealerAI(Player* healer, BotAI* ai, Group* gr followPos.m_positionY = tank->GetPositionY() + 15.0f * sin(angle); followPos.m_positionZ = tank->GetPositionZ(); - healer->GetMotionMaster()->MovePoint(0, followPos); + // Use validated pathfinding for healer positioning + if (BotAI* ai = GetBotAI(healer)) + { + if (!ai->MoveTo(followPos, true)) + { + // Fallback to legacy if validation fails + healer->GetMotionMaster()->MovePoint(0, followPos); + } + } + else + { + // Non-bot player - use standard movement + healer->GetMotionMaster()->MovePoint(0, followPos); + } return true; } @@ -787,7 +814,20 @@ bool DungeonAutonomyManager::UpdateDpsAI(Player* dps, BotAI* ai, Group* group, D followPos.m_positionY = tank->GetPositionY() + 20.0f * sin(angle); followPos.m_positionZ = tank->GetPositionZ(); - dps->GetMotionMaster()->MovePoint(0, followPos); + // Use validated pathfinding for DPS positioning + if (BotAI* ai = GetBotAI(dps)) + { + if (!ai->MoveTo(followPos, true)) + { + // Fallback to legacy if validation fails + dps->GetMotionMaster()->MovePoint(0, followPos); + } + } + else + { + // Non-bot player - use standard movement + dps->GetMotionMaster()->MovePoint(0, followPos); + } return true; } diff --git a/src/modules/Playerbot/Dungeon/DungeonBehavior.cpp b/src/modules/Playerbot/Dungeon/DungeonBehavior.cpp index a168225d8..2fd493025 100644 --- a/src/modules/Playerbot/Dungeon/DungeonBehavior.cpp +++ b/src/modules/Playerbot/Dungeon/DungeonBehavior.cpp @@ -659,9 +659,22 @@ void DungeonBehavior::CoordinateTankBehavior(Player* tank, const DungeonEncounte } else { - // FALLBACK: Direct MotionMaster if arbiter not available - tank->GetMotionMaster()->MovePoint(0, optimalPos.GetPositionX(), - optimalPos.GetPositionY(), optimalPos.GetPositionZ()); + // Use validated pathfinding for tank positioning + if (BotAI* ai = GetBotAI(tank)) + { + if (!ai->MoveTo(optimalPos, true)) + { + // FALLBACK: Direct MotionMaster if validation fails + tank->GetMotionMaster()->MovePoint(0, optimalPos.GetPositionX(), + optimalPos.GetPositionY(), optimalPos.GetPositionZ()); + } + } + else + { + // Non-bot player - use standard movement + tank->GetMotionMaster()->MovePoint(0, optimalPos.GetPositionX(), + optimalPos.GetPositionY(), optimalPos.GetPositionZ()); + } } } @@ -699,9 +712,22 @@ void DungeonBehavior::CoordinateHealerBehavior(Player* healer, const DungeonEnco } else { - // FALLBACK: Direct MotionMaster if arbiter not available - healer->GetMotionMaster()->MovePoint(0, safePos.GetPositionX(), - safePos.GetPositionY(), safePos.GetPositionZ()); + // Use validated pathfinding for healer positioning + if (BotAI* ai = GetBotAI(healer)) + { + if (!ai->MoveTo(safePos, true)) + { + // FALLBACK: Direct MotionMaster if validation fails + healer->GetMotionMaster()->MovePoint(0, safePos.GetPositionX(), + safePos.GetPositionY(), safePos.GetPositionZ()); + } + } + else + { + // Non-bot player - use standard movement + healer->GetMotionMaster()->MovePoint(0, safePos.GetPositionX(), + safePos.GetPositionY(), safePos.GetPositionZ()); + } } } @@ -744,9 +770,22 @@ void DungeonBehavior::CoordinateDpsBehavior(Player* dps, const DungeonEncounter& } else { - // FALLBACK: Direct MotionMaster if arbiter not available - dps->GetMotionMaster()->MovePoint(0, optimalPos.GetPositionX(), - optimalPos.GetPositionY(), optimalPos.GetPositionZ()); + // Use validated pathfinding for DPS positioning + if (BotAI* ai = GetBotAI(dps)) + { + if (!ai->MoveTo(optimalPos, true)) + { + // FALLBACK: Direct MotionMaster if validation fails + dps->GetMotionMaster()->MovePoint(0, optimalPos.GetPositionX(), + optimalPos.GetPositionY(), optimalPos.GetPositionZ()); + } + } + else + { + // Non-bot player - use standard movement + dps->GetMotionMaster()->MovePoint(0, optimalPos.GetPositionX(), + optimalPos.GetPositionY(), optimalPos.GetPositionZ()); + } } } @@ -876,9 +915,22 @@ void DungeonBehavior::UpdateGroupPositioning(Group* group, const DungeonEncounte } else { - // FALLBACK: Direct MotionMaster if arbiter not available - player->GetMotionMaster()->MovePoint(0, optimalPos.GetPositionX(), - optimalPos.GetPositionY(), optimalPos.GetPositionZ()); + // Use validated pathfinding for player positioning + if (BotAI* ai = GetBotAI(player)) + { + if (!ai->MoveTo(optimalPos, true)) + { + // FALLBACK: Direct MotionMaster if validation fails + player->GetMotionMaster()->MovePoint(0, optimalPos.GetPositionX(), + optimalPos.GetPositionY(), optimalPos.GetPositionZ()); + } + } + else + { + // Non-bot player - use standard movement + player->GetMotionMaster()->MovePoint(0, optimalPos.GetPositionX(), + optimalPos.GetPositionY(), optimalPos.GetPositionZ()); + } } } } @@ -979,9 +1031,22 @@ void DungeonBehavior::AvoidDangerousAreas(Player* player, const ::std::vectorGetMotionMaster()->MovePoint(0, nearestSafeSpot.GetPositionX(), - nearestSafeSpot.GetPositionY(), nearestSafeSpot.GetPositionZ()); + // Use validated pathfinding for safe spot movement + if (BotAI* ai = GetBotAI(player)) + { + if (!ai->MoveTo(nearestSafeSpot, true)) + { + // FALLBACK: Direct MotionMaster if validation fails + player->GetMotionMaster()->MovePoint(0, nearestSafeSpot.GetPositionX(), + nearestSafeSpot.GetPositionY(), nearestSafeSpot.GetPositionZ()); + } + } + else + { + // Non-bot player - use standard movement + player->GetMotionMaster()->MovePoint(0, nearestSafeSpot.GetPositionX(), + nearestSafeSpot.GetPositionY(), nearestSafeSpot.GetPositionZ()); + } } TC_LOG_DEBUG("module.playerbot", "Player {} moving to avoid dangerous area", player->GetName()); diff --git a/src/modules/Playerbot/Dungeon/EncounterStrategy.cpp b/src/modules/Playerbot/Dungeon/EncounterStrategy.cpp index cb9e239bb..a8330cd8f 100644 --- a/src/modules/Playerbot/Dungeon/EncounterStrategy.cpp +++ b/src/modules/Playerbot/Dungeon/EncounterStrategy.cpp @@ -37,6 +37,7 @@ #include "../Movement/Arbiter/MovementPriorityMapper.h" #include "../AI/BotAI.h" #include "UnitAI.h" +#include "../Core/PlayerBotHelpers.h" namespace Playerbot { @@ -436,9 +437,22 @@ void EncounterStrategy::HandleAoEDamageMechanic(Group* group, const Position& da } else { - // FALLBACK: Direct MotionMaster if arbiter not available - player->GetMotionMaster()->MovePoint(0, safePos.GetPositionX(), - safePos.GetPositionY(), safePos.GetPositionZ()); + // Use validated pathfinding for AoE avoidance + if (BotAI* ai = GetBotAI(player)) + { + if (!ai->MoveTo(safePos, true)) + { + // FALLBACK: Direct MotionMaster if validation fails + player->GetMotionMaster()->MovePoint(0, safePos.GetPositionX(), + safePos.GetPositionY(), safePos.GetPositionZ()); + } + } + else + { + // Non-bot player - use standard movement + player->GetMotionMaster()->MovePoint(0, safePos.GetPositionX(), + safePos.GetPositionY(), safePos.GetPositionZ()); + } TC_LOG_TRACE("module.playerbot", "Player {} moving to avoid AoE", player->GetName()); } } @@ -576,9 +590,22 @@ void EncounterStrategy::UpdateEncounterPositioning(Group* group, uint32 encounte } else { - // FALLBACK: Direct MotionMaster if arbiter not available - player->GetMotionMaster()->MovePoint(0, optimalPos.GetPositionX(), - optimalPos.GetPositionY(), optimalPos.GetPositionZ()); + // Use validated pathfinding for optimal positioning + if (BotAI* ai = GetBotAI(player)) + { + if (!ai->MoveTo(optimalPos, true)) + { + // FALLBACK: Direct MotionMaster if validation fails + player->GetMotionMaster()->MovePoint(0, optimalPos.GetPositionX(), + optimalPos.GetPositionY(), optimalPos.GetPositionZ()); + } + } + else + { + // Non-bot player - use standard movement + player->GetMotionMaster()->MovePoint(0, optimalPos.GetPositionX(), + optimalPos.GetPositionY(), optimalPos.GetPositionZ()); + } } } } @@ -661,9 +688,22 @@ void EncounterStrategy::AvoidMechanicAreas(Group* group, const ::std::vectorGetMotionMaster()->MovePoint(0, safePos.GetPositionX(), - safePos.GetPositionY(), safePos.GetPositionZ()); + // Use validated pathfinding for danger zone avoidance + if (BotAI* ai = GetBotAI(player)) + { + if (!ai->MoveTo(safePos, true)) + { + // FALLBACK: Direct MotionMaster if validation fails + player->GetMotionMaster()->MovePoint(0, safePos.GetPositionX(), + safePos.GetPositionY(), safePos.GetPositionZ()); + } + } + else + { + // Non-bot player - use standard movement + player->GetMotionMaster()->MovePoint(0, safePos.GetPositionX(), + safePos.GetPositionY(), safePos.GetPositionZ()); + } } } } @@ -1417,8 +1457,21 @@ void EncounterStrategy::HandleGenericGroundAvoidance(::Player* player, ::Creatur } else { - // FALLBACK: Direct MotionMaster if arbiter not available - player->GetMotionMaster()->MovePoint(0, x, y, z); + // Use validated pathfinding for positioning + Position dest(x, y, z, 0.0f); + if (BotAI* ai = GetBotAI(player)) + { + if (!ai->MoveTo(dest, true)) + { + // FALLBACK: Direct MotionMaster if validation fails + player->GetMotionMaster()->MovePoint(0, x, y, z); + } + } + else + { + // Non-bot player - use standard movement + player->GetMotionMaster()->MovePoint(0, x, y, z); + } } return; } @@ -1573,9 +1626,22 @@ void EncounterStrategy::HandleGenericPositioning(::Player* player, ::Creature* b } else { - // FALLBACK: Direct MotionMaster if arbiter not available - player->GetMotionMaster()->MovePoint(0, targetPos.GetPositionX(), - targetPos.GetPositionY(), targetPos.GetPositionZ()); + // Use validated pathfinding for target positioning + if (BotAI* ai = GetBotAI(player)) + { + if (!ai->MoveTo(targetPos, true)) + { + // FALLBACK: Direct MotionMaster if validation fails + player->GetMotionMaster()->MovePoint(0, targetPos.GetPositionX(), + targetPos.GetPositionY(), targetPos.GetPositionZ()); + } + } + else + { + // Non-bot player - use standard movement + player->GetMotionMaster()->MovePoint(0, targetPos.GetPositionX(), + targetPos.GetPositionY(), targetPos.GetPositionZ()); + } } } } @@ -1738,8 +1804,21 @@ void EncounterStrategy::HandleGenericSpread(::Player* player, ::Creature* boss, } else { - // FALLBACK: Direct MotionMaster if arbiter not available - player->GetMotionMaster()->MovePoint(0, x, y, z); + // Use validated pathfinding for positioning + Position dest(x, y, z, 0.0f); + if (BotAI* ai = GetBotAI(player)) + { + if (!ai->MoveTo(dest, true)) + { + // FALLBACK: Direct MotionMaster if validation fails + player->GetMotionMaster()->MovePoint(0, x, y, z); + } + } + else + { + // Non-bot player - use standard movement + player->GetMotionMaster()->MovePoint(0, x, y, z); + } } return; } @@ -1801,9 +1880,23 @@ void EncounterStrategy::HandleGenericStack(::Player* player, ::Creature* boss) } else { - // FALLBACK: Direct MotionMaster if arbiter not available - player->GetMotionMaster()->MovePoint(0, tank->GetPositionX(), - tank->GetPositionY(), tank->GetPositionZ()); + // Use validated pathfinding for stacking on tank + Position dest(tank->GetPositionX(), tank->GetPositionY(), tank->GetPositionZ(), 0.0f); + if (BotAI* ai = GetBotAI(player)) + { + if (!ai->MoveTo(dest, true)) + { + // FALLBACK: Direct MotionMaster if validation fails + player->GetMotionMaster()->MovePoint(0, tank->GetPositionX(), + tank->GetPositionY(), tank->GetPositionZ()); + } + } + else + { + // Non-bot player - use standard movement + player->GetMotionMaster()->MovePoint(0, tank->GetPositionX(), + tank->GetPositionY(), tank->GetPositionZ()); + } } } }