From cad727b81468dbf47a05d4f69a6c60facde682b1 Mon Sep 17 00:00:00 2001 From: Chomper9981 Date: Wed, 25 Feb 2026 14:41:15 +0700 Subject: [PATCH] Fix the nav bug(work around). fix the major map quest's npc load proplem. --- .../PerfectWorld/Scripts/MainFiles/EC_Game.cs | 15 +---- .../Scripts/Managers/EC_HPWorkMove.cs | 34 +++++++++++- .../Scripts/Move/CECIntelligentRoute.cs | 7 ++- .../Scripts/Task/ATaskTemplMan.cs | 5 +- Assets/PerfectWorld/Scripts/Task/TaskTempl.cs | 5 +- .../Scripts/Task/UI/DlgNameLink.cs | 2 +- .../Scripts/Task/UI/DlgTaskTrace.cs | 2 +- Assets/PerfectWorld/Scripts/UI/EC_UIHelper.cs | 55 +++++++++++++++---- Assets/Scripts/CECHostPlayer.cs | 33 +++++------ 9 files changed, 109 insertions(+), 49 deletions(-) diff --git a/Assets/PerfectWorld/Scripts/MainFiles/EC_Game.cs b/Assets/PerfectWorld/Scripts/MainFiles/EC_Game.cs index d4477815d1..7bd5336355 100644 --- a/Assets/PerfectWorld/Scripts/MainFiles/EC_Game.cs +++ b/Assets/PerfectWorld/Scripts/MainFiles/EC_Game.cs @@ -530,19 +530,10 @@ namespace BrewMonster.Network return iIndex; } - public static int GetObjectCoord(string strTargetID, ref List TargetCoord) + public static int GetObjectCoord(string strTargetID, out List TargetCoord) { - int count = 0; - foreach(var coord in TargetCoord) - { - if(coord.strMap == strTargetID) - { - count++; - TargetCoord.Add(coord); - } - } - - return count; + TargetCoord = m_CoordTab[strTargetID]; + return TargetCoord.Count; } public static bool IsPetAutoSkill(int skill_id) diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_HPWorkMove.cs b/Assets/PerfectWorld/Scripts/Managers/EC_HPWorkMove.cs index 1fcaf5562b..06dd6f42ff 100644 --- a/Assets/PerfectWorld/Scripts/Managers/EC_HPWorkMove.cs +++ b/Assets/PerfectWorld/Scripts/Managers/EC_HPWorkMove.cs @@ -1278,10 +1278,42 @@ namespace BrewMonster.Scripts m_vMoveDest, null); - if (ret != CECIntelligentRoute.SearchResult.enumSearchSuccess) + if (ret == CECIntelligentRoute.SearchResult.enumSearchNoPath) + { + // Calculate map coordinates from world coordinates + // 从世界坐标计算地图坐标 + // Map coord formula: (world / 10) + offset (X: +400, Z: +550) + // 地图坐标公式:(世界坐标 / 10) + 偏移量 (X: +400, Z: +550) + int mapX = Mathf.RoundToInt(m_vMoveDest.x / 10.0f) + 400; + int mapY = Mathf.RoundToInt(m_vMoveDest.y / 10.0f); + int mapZ = Mathf.RoundToInt(m_vMoveDest.z / 10.0f) + 550; + + // Show popup notification to player that path cannot be found + // 显示弹窗通知玩家无法找到路径 + if (CECUIManager.Instance != null) + { + string message = $"Cannot find path to target position.\nPlease move manually to target location.\n\nMap Coordinates: ({mapX}, {mapZ}, ↑{mapY})"; + string messageCN = $"无法找到到目标位置的路径。\n请手动移动到目标位置。\n\n地图坐标: ({mapX}, {mapZ}, ↑{mapY})"; + + CECUIManager.Instance.ShowMessageBox( + "Path Not Found", // 路径未找到 + message, // English message with map coordinates + BrewMonster.MessageBoxType.YesButton + ); + } + else + { + Debug.LogWarning($"[CECIntelligentRoute] Cannot find path to target position. Map Coordinates: ({mapX}, {mapZ}, ↑{mapY}). Please move manually."); + } + + Finish(); + return; + } + else if (ret != CECIntelligentRoute.SearchResult.enumSearchSuccess) { break; } + bSwitchTo2D = false; break; diff --git a/Assets/PerfectWorld/Scripts/Move/CECIntelligentRoute.cs b/Assets/PerfectWorld/Scripts/Move/CECIntelligentRoute.cs index 6b46d6afdf..6cbedc9016 100644 --- a/Assets/PerfectWorld/Scripts/Move/CECIntelligentRoute.cs +++ b/Assets/PerfectWorld/Scripts/Move/CECIntelligentRoute.cs @@ -364,9 +364,12 @@ namespace BrewMonster.Scripts { if (DEBUG_AUTOPF) { - BMLogger.LogWarning($"[CECIntelligentRoute] Search: no path. start=({start.x:F2},{start.y:F2},{start.z:F2}) end=({end.x:F2},{end.y:F2},{end.z:F2}) agentIndex={idx}"); + BMLogger.Log($"[CECIntelligentRoute] Search: no path found, ignoring. End position=({end.x:F2},{end.y:F2},{end.z:F2})"); } - ResetSearch(); + // Don't call ResetSearch() again - it was already called at the start of Search() + // This prevents the idle state deadlock that blocks all movement + // 不再调用 ResetSearch() - 已在 Search() 开始时调用 + // 这防止了阻止所有移动的空闲状态死锁 return SearchResult.enumSearchNoPath; } diff --git a/Assets/PerfectWorld/Scripts/Task/ATaskTemplMan.cs b/Assets/PerfectWorld/Scripts/Task/ATaskTemplMan.cs index 1c38eb3a8a..2012a6e28d 100644 --- a/Assets/PerfectWorld/Scripts/Task/ATaskTemplMan.cs +++ b/Assets/PerfectWorld/Scripts/Task/ATaskTemplMan.cs @@ -1122,7 +1122,7 @@ namespace BrewMonster.Scripts.Task // p += sizeof(TASK_NPC_PACK_HEADER); TASK_NPC_PACK_HEADER header = GPDataTypeHelper.FromBytes(data); - var p = Marshal.SizeOf(); + long p = Marshal.SizeOf(); if (header.version != TASK_NPC_INFO_VERSION) { @@ -1150,8 +1150,7 @@ namespace BrewMonster.Scripts.Task // const NPC_INFO& info = pInfos[i]; // m_NPCInfoMap[info.id] = info; - NPC_INFO info = GPDataTypeHelper.FromBytes(data, p); - p += Marshal.SizeOf(); + NPC_INFO info = GPDataTypeHelper.FromBytes(data, ref p); m_NPCInfoMap[info.id] = info; } diff --git a/Assets/PerfectWorld/Scripts/Task/TaskTempl.cs b/Assets/PerfectWorld/Scripts/Task/TaskTempl.cs index 9bd4384d78..113492b25d 100644 --- a/Assets/PerfectWorld/Scripts/Task/TaskTempl.cs +++ b/Assets/PerfectWorld/Scripts/Task/TaskTempl.cs @@ -129,10 +129,13 @@ namespace BrewMonster.Scripts.Task [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct NPC_INFO { - public uint id; + public uint id; // uint in C++ // uint in C++ public short x; public short y; public short z; + //Add padding to because the c++ struct size is auto add 2 bytes padding if not have pragmapack. + public byte padding1; // 1 byte padding to match C++ struct size (12 bytes total) // 1字节填充以匹配C++结构体大小(总共12字节) + public byte padding2; // 1 byte padding to match C++ struct size (12 bytes total) // 1字节填充以匹配C++结构体大小(总共12字节) } // public class ServerNotificationConstants diff --git a/Assets/PerfectWorld/Scripts/Task/UI/DlgNameLink.cs b/Assets/PerfectWorld/Scripts/Task/UI/DlgNameLink.cs index ce8d64d7ae..ef60fa8ee7 100644 --- a/Assets/PerfectWorld/Scripts/Task/UI/DlgNameLink.cs +++ b/Assets/PerfectWorld/Scripts/Task/UI/DlgNameLink.cs @@ -133,7 +133,7 @@ namespace BrewMonster.UI m_TargetPos = new A3DVECTOR3(0, 0, 0); bool bInTable = false; m_TargetPos = EC_Game.GetGameRun().GetHostPlayer().GetObjectCoordinates( - idTarget, ref m_Targets, ref bInTable); + idTarget, out m_Targets, ref bInTable); //todo: add map feature here. if(!bInTable /*&& MAJOR_MAP== CECWorld.Instance.GetInstanceID())*/) { diff --git a/Assets/PerfectWorld/Scripts/Task/UI/DlgTaskTrace.cs b/Assets/PerfectWorld/Scripts/Task/UI/DlgTaskTrace.cs index 1904eada98..b28b970217 100644 --- a/Assets/PerfectWorld/Scripts/Task/UI/DlgTaskTrace.cs +++ b/Assets/PerfectWorld/Scripts/Task/UI/DlgTaskTrace.cs @@ -249,7 +249,7 @@ namespace BrewMonster.Scripts.Task.UI if(cur != worldid) { - EC_Game.GetObjectCoord(mapName, ref tempCoord); + EC_Game.GetObjectCoord(mapName, out tempCoord); var iter = tempCoord.Find(coord => coord.strMap == strCurMap); instCoord.Add(iter); } diff --git a/Assets/PerfectWorld/Scripts/UI/EC_UIHelper.cs b/Assets/PerfectWorld/Scripts/UI/EC_UIHelper.cs index 17bc943258..b043ecccc0 100644 --- a/Assets/PerfectWorld/Scripts/UI/EC_UIHelper.cs +++ b/Assets/PerfectWorld/Scripts/UI/EC_UIHelper.cs @@ -24,7 +24,8 @@ namespace BrewMonster.Scripts.UI GPDataTypeHelper.ISNPCID(id) || GPDataTypeHelper.ISMATTERID(id) || (id > 100000000); // player ids are typically huge; template ids are usually small - + + A3DVECTOR3 ret = new A3DVECTOR3(0); var world = EC_Game.GetGameRun()?.GetWorld(); if (world != null && isLikelyRuntimeObjectId) { @@ -33,22 +34,45 @@ namespace BrewMonster.Scripts.UI { var objPos = obj.transform.position; in_table = true; - return new A3DVECTOR3(objPos.x, objPos.y, objPos.z); + BMLogger.Log($"[CECUIHelper] GetTaskObjectCoordinates isLikelyRuntimeObjectId: objPos={objPos.x},{objPos.y},{objPos.z}, inTable={in_table}"); + ret.Set(objPos.x, objPos.y, objPos.z); + return ret; } } + List TargetTemp = new List(); + ret = EC_Game.GetGameRun()?.GetHostPlayer().GetObjectCoordinates(id, out TargetTemp, ref in_table) ?? new A3DVECTOR3(0); // 1) Try live NPC/Monster in scene by template id (best match to "click name -> go to that entity") // 1) 先尝试在场景中按模板ID查找活体NPC/怪物(最符合“点名字就去找它”) - var npcMan = EC_ManMessageMono.Instance != null ? EC_ManMessageMono.Instance.CECNPCMan : null; - if (npcMan != null) + + //This only work in Major map not A61. Skip for now. + // if(!in_table) + // { + // var npcMan = EC_ManMessageMono.Instance != null ? EC_ManMessageMono.Instance.CECNPCMan : null; + // if (npcMan != null) + // { + // var npc = npcMan.FindNPCByTemplateID(id); + // if (npc != null) + // { + // UnityEngine.Vector3 npcPos = npc.transform.position; + // in_table = true; + // BMLogger.Log($"[CECUIHelper] GetTaskObjectCoordinates npcMan: npcPos={npcPos.x},{npcPos.y},{npcPos.z}, inTable={in_table}"); + // ret.Set(npcPos.x, npcPos.y, npcPos.z); + // } + // } + // } + // else + // { + // BMLogger.Log($"[CECUIHelper] GetTaskObjectCoordinates in_table: ret={ret.x},{ret.y},{ret.z}, inTable={in_table}"); + // } + if(ret.x != 0 && ret.y != 0 && ret.z != 0) { - var npc = npcMan.FindNPCByTemplateID(id); - if (npc != null) - { - UnityEngine.Vector3 npcPos = npc.transform.position; - in_table = true; - return new A3DVECTOR3(npcPos.x, npcPos.y, npcPos.z); - } + BMLogger.Log($"[CECUIHelper] GetHostPlayer GetObjectCoordinates True ret={ret.x},{ret.y},{ret.z}, inTable={in_table}"); + return ret; + } + else + { + BMLogger.Log($"[CECUIHelper] GetHostPlayer TryGetFirstObjectCoord False ret={ret.x},{ret.y},{ret.z}, inTable={in_table}"); } // Fallback to task_npc table (C++: ATaskTemplMan::GetTaskNPCInfo) @@ -59,19 +83,25 @@ namespace BrewMonster.Scripts.UI // NOTE: Keep original PW coordinate mapping: ret.Set(x, z, y) // 注意:保持原版坐标映射:ret.Set(x, z, y) in_table = true; + BMLogger.Log($"[CECUIHelper] GetTaskObjectCoordinates TryGetTaskNPCInfo: info.x={info.x}, info.z={info.z}, info.y={info.y}, inTable={in_table}"); return new A3DVECTOR3(info.x, info.z, info.y); } + else{ + BMLogger.Log($"[CECUIHelper] GetTaskObjectCoordinates TryGetTaskNPCInfo: not found for id={id}"); + } // Fallback to coord_data.txt (C++: Configs/Coord_data.txt via CECGame::GetObjectCoord) // 回退到 coord_data.txt(C++:Configs/Coord_data.txt,通过 CECGame::GetObjectCoord) if (BrewMonster.Network.EC_Game.TryGetFirstObjectCoord(id.ToString(), out var coordPos, out var mapName)) { in_table = true; + BMLogger.Log($"[CECUIHelper] GetTaskObjectCoordinates TryGetFirstObjectCoord: coordPos.x={coordPos.x}, coordPos.y={coordPos.y}, coordPos.z={coordPos.z}, inTable={in_table}"); return new A3DVECTOR3(coordPos.x, coordPos.y, coordPos.z); } UnityEngine.Debug.LogWarning($"[CECUIHelper] GetTaskObjectCoordinates: Not found for id={id} (isLikelyRuntimeObjectId={isLikelyRuntimeObjectId})."); - return new A3DVECTOR3(0); + BMLogger.Log($"[CECUIHelper] GetTaskObjectCoordinates default return ret={ret.x},{ret.y},{ret.z}, inTable={in_table}"); + return ret; // TODO: Implement this method properly // A3DVECTOR3 ret(0.f); @@ -144,6 +174,7 @@ namespace BrewMonster.Scripts.UI // Resolve coordinates bool inTable = false; A3DVECTOR3 vPos = GetTaskObjectCoordinates(id, ref inTable); + BMLogger.Log($"[CECUIHelper] FollowCoord: vPos={vPos.x},{vPos.y},{vPos.z}, inTable={inTable}"); if (!inTable) { // Fallback: use task regions if available (move to the center of the first region) diff --git a/Assets/Scripts/CECHostPlayer.cs b/Assets/Scripts/CECHostPlayer.cs index d851a87ef9..6cea340c58 100644 --- a/Assets/Scripts/CECHostPlayer.cs +++ b/Assets/Scripts/CECHostPlayer.cs @@ -3540,21 +3540,19 @@ namespace BrewMonster public CECCounter GetIncantCnt() { return m_IncantCnt; } // Get key object(NPC..) coordinates - public A3DVECTOR3 GetObjectCoordinates(int idTarget, ref ObjectCoords TargetCoord, ref bool bInTable) + public A3DVECTOR3 GetObjectCoordinates(int idTarget, out List TargetCoord, ref bool bInTable) { - if (TargetCoord == null) - { - TargetCoord = new ObjectCoords(); - } - TargetCoord.Clear(); + + TargetCoord = new List(); A3DVECTOR3 vDestPos = new A3DVECTOR3(0, 0, 0); bInTable = false; // Get object coordinates from CECGame::m_CoordTab string szText = idTarget.ToString(); - List tempCoord = new List(); - int iCount = EC_Game.GetObjectCoord(szText, ref tempCoord); + List originalCoords = new List(); + int iCount = EC_Game.GetObjectCoord(szText, out originalCoords); + if (iCount == 0) { return vDestPos; @@ -3567,11 +3565,12 @@ namespace BrewMonster CECInstance pInstance = CECGameRun.Instance.GetInstance(idInstance); string strCurMap = pInstance.GetPath(); // �ȼ��ͬһ��ͼ���Ƿ���Ҫ���ҵ���Ʒ - bool bHasObjectInCurrentInstance = tempCoord.Any(coord => coord.strMap == strCurMap); + bool bHasObjectInCurrentInstance = originalCoords.Any(coord => coord.strMap == strCurMap); + + // Iterate over original list and build filtered TargetCoord list for (int i = 0; i < iCount; i++) { - OBJECT_COORD objCoord = tempCoord[i]; - + OBJECT_COORD objCoord = originalCoords[i]; if (strCurMap == objCoord.strMap) { TargetCoord.Add(objCoord); @@ -3583,22 +3582,24 @@ namespace BrewMonster fMinDist = tempDist; bInTable = true; vDestPos = objCoord.vPos; - } + } } // ��ǰ��ͼ��û��Ŀ��Ļ�����Ŀ�����ڵ�ͼ�ڵ�ǰ��ͼ����� else if (!bHasObjectInCurrentInstance) { + // find the entrance of instance List instCoord = new List(); - int iCount2 = EC_Game.GetObjectCoord(objCoord.strMap, ref instCoord); - for (int j = 0; j < iCount2; ++j) + int iCount2 = EC_Game.GetObjectCoord(objCoord.strMap, out instCoord); + for (int j = 0; j < iCount2; ++j) { if (instCoord[j].strMap == strCurMap) { TargetCoord.Add(instCoord[j]); - + // Check if this is the nearest target - float tempDist = (instCoord[i].vPos - GetPos()).Magnitude(); + float tempDist = (instCoord[j].vPos - GetPos()).Magnitude(); + if (tempDist < fMinDist) { fMinDist = tempDist;