From e76c54d57522d460e9f30ddce1468b314b4ba726 Mon Sep 17 00:00:00 2001 From: bash Date: Fri, 15 May 2026 00:12:46 +0200 Subject: [PATCH] fix(Core/RPG): Reject mmap paths that LOS-fail any segment --- src/Ai/World/Rpg/Action/NewRpgBaseAction.cpp | 22 ++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/Ai/World/Rpg/Action/NewRpgBaseAction.cpp b/src/Ai/World/Rpg/Action/NewRpgBaseAction.cpp index 1da88f47a..0b9b580bc 100644 --- a/src/Ai/World/Rpg/Action/NewRpgBaseAction.cpp +++ b/src/Ai/World/Rpg/Action/NewRpgBaseAction.cpp @@ -279,6 +279,28 @@ bool NewRpgBaseAction::DispatchPathPoints(WorldPosition const& dest, if (points.size() < 2) return false; + // LOS gate: reject paths whose segments pass through visual + // geometry. mmap is blind to M2 models (trees, decorative props) + // and will route through them; vmap LOS catches the cases that + // matter — solid trunks, walls, terrain features. + if (Map* map = bot->GetMap()) + { + float const eye = bot->GetCollisionHeight(); + for (size_t i = 0; i + 1 < points.size(); ++i) + { + if (!map->isInLineOfSight(points[i].x, points[i].y, points[i].z + eye, + points[i + 1].x, points[i + 1].y, points[i + 1].z + eye, + bot->GetPhaseMask(), + LINEOFSIGHT_ALL_CHECKS, + VMAP::ModelIgnoreFlags::Nothing)) + { + EmitDebugMove("MoveFar", "blocked", + dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ()); + return false; + } + } + } + // Save planner output before clip/fixup so next-tick reuse sees // the original intent, not a truncated tail. {