Merge branch 'develop' of https://git.brew.monster/Unity/perfect-world-unity into feature/attach_mesh
This commit is contained in:
@@ -4,6 +4,7 @@ using CSNetwork;
|
||||
using ModelRenderer.Scripts.GameData;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
@@ -114,6 +115,10 @@ namespace BrewMonster.Network
|
||||
GetGameRun().Init();
|
||||
InitializeStringTables();
|
||||
|
||||
// Load coord_data.txt (C++: Configs/Coord_data.txt) for clickable task links auto-move.
|
||||
// 加载 coord_data.txt(C++:Configs/Coord_data.txt)用于任务可点击链接的自动移动。
|
||||
LoadObjectCoord();
|
||||
|
||||
return true;
|
||||
}
|
||||
public static CECConfigs GetConfigs() { return m_pConfigs; }
|
||||
@@ -262,6 +267,140 @@ namespace BrewMonster.Network
|
||||
long unixTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
return (int)unixTime + m_iTimeError;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Coord_data.txt support (C++: Configs/Coord_data.txt, CECGame::LoadObjectCoord/GetObjectCoord)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private struct OBJECT_COORD
|
||||
{
|
||||
public string strMap;
|
||||
public Vector3 vPos;
|
||||
}
|
||||
|
||||
private static readonly Dictionary<string, List<OBJECT_COORD>> m_CoordTab =
|
||||
new Dictionary<string, List<OBJECT_COORD>>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
private static bool m_bCoordLoaded = false;
|
||||
|
||||
public static bool LoadObjectCoord()
|
||||
{
|
||||
if (m_bCoordLoaded)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Addressables.InitializeAsync().WaitForCompletion();
|
||||
var ta = Addressables.LoadAssetAsync<TextAsset>("Assets/Addressable/coord_data.txt").WaitForCompletion();
|
||||
if (ta == null)
|
||||
{
|
||||
Debug.LogError("[EC_Game] LoadObjectCoord: failed to load Addressable 'Assets/Addressable/coord_data.txt'");
|
||||
return false;
|
||||
}
|
||||
|
||||
ParseCoordDataText(ta.text);
|
||||
m_bCoordLoaded = true;
|
||||
Debug.Log($"[EC_Game] LoadObjectCoord: loaded {m_CoordTab.Count} coord keys from coord_data.txt");
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"[EC_Game] LoadObjectCoord exception: {ex}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ParseCoordDataText(string text)
|
||||
{
|
||||
m_CoordTab.Clear();
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using var sr = new StringReader(text);
|
||||
string line;
|
||||
int lineNo = 0;
|
||||
|
||||
while ((line = sr.ReadLine()) != null)
|
||||
{
|
||||
lineNo++;
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
line = line.Trim();
|
||||
if (line.StartsWith("#", StringComparison.Ordinal) || line.StartsWith("//", StringComparison.Ordinal))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lineNo == 1 && line.StartsWith("ID", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string[] parts = line.Split((char[])null, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (parts.Length < 5)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string key = parts[0];
|
||||
string map = parts[1];
|
||||
|
||||
if (!float.TryParse(parts[2], NumberStyles.Float, CultureInfo.InvariantCulture, out float x) ||
|
||||
!float.TryParse(parts[3], NumberStyles.Float, CultureInfo.InvariantCulture, out float y) ||
|
||||
!float.TryParse(parts[4], NumberStyles.Float, CultureInfo.InvariantCulture, out float z))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var coord = new OBJECT_COORD
|
||||
{
|
||||
strMap = map,
|
||||
vPos = new Vector3(x, y, z),
|
||||
};
|
||||
|
||||
if (!m_CoordTab.TryGetValue(key, out var list))
|
||||
{
|
||||
list = new List<OBJECT_COORD>(1);
|
||||
m_CoordTab[key] = list;
|
||||
}
|
||||
|
||||
list.Add(coord);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryGetFirstObjectCoord(string targetId, out Vector3 pos, out string map)
|
||||
{
|
||||
pos = default;
|
||||
map = null;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(targetId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_bCoordLoaded)
|
||||
{
|
||||
LoadObjectCoord();
|
||||
}
|
||||
|
||||
if (!m_CoordTab.TryGetValue(targetId, out var list) || list == null || list.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
pos = list[0].vPos;
|
||||
map = list[0].strMap;
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -502,6 +502,25 @@ public class CECNPCMan : IMsgHandler
|
||||
|
||||
return npc;
|
||||
}
|
||||
|
||||
// Find first NPC/Monster by template id (tid). Used by UI auto-move coordinate resolving.
|
||||
// 通过模板ID(tid)查找第一个NPC/怪物。用于UI自动寻路坐标解析。
|
||||
public CECNPC FindNPCByTemplateID(int tid)
|
||||
{
|
||||
if (tid == 0) return null;
|
||||
|
||||
foreach (var npc in m_NPCTab.Values)
|
||||
{
|
||||
if (!npc) continue;
|
||||
var info = npc.GetNPCInfo();
|
||||
if (info.tid == tid || info.vis_tid == tid)
|
||||
{
|
||||
return npc;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
public CECNPC GetNPCFromAll(int nid)
|
||||
{
|
||||
CECNPC pNPC = GetNPC(nid);
|
||||
|
||||
@@ -586,7 +586,7 @@ namespace BrewMonster.Scripts
|
||||
case CECHPWork.Host_work_ID.WORK_PASSIVEMOVE: pWork = new CECHPWorkPassiveMove(this); break;
|
||||
//case CECHPWork.Host_work_ID.WORK_CONGREGATE: pWork = new CECHPWorkCongregate(this); break;
|
||||
//case CECHPWork.Host_work_ID.WORK_SKILLSTATEACT: pWork = new CECHPWorkSkillStateAction(this); break;
|
||||
//case CECHPWork.Host_work_ID.WORK_FORCENAVIGATEMOVE: pWork = new CECHPWorkNavigate(this); break;
|
||||
case CECHPWork.Host_work_ID.WORK_FORCENAVIGATEMOVE: pWork = new CECHPWorkNavigate(this); break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c3491aaddacef1a418865bbcd31fb975
|
||||
@@ -68,6 +68,13 @@ namespace BrewMonster.Scripts.Task
|
||||
uint m_ulNPCInfoTimeMark;
|
||||
private Dictionary<uint, NPC_INFO> m_NPCInfoMap = new();
|
||||
|
||||
// Lookup NPC/task object coordinates info by template id (loaded from task_npc pack)
|
||||
// 通过模板ID查找NPC/任务对象坐标信息(从task_npc包加载)
|
||||
public bool TryGetTaskNPCInfo(uint id, out NPC_INFO info)
|
||||
{
|
||||
return m_NPCInfoMap.TryGetValue(id, out info);
|
||||
}
|
||||
|
||||
private TaskTemplContainerSO _taskTemplContainerSO;
|
||||
|
||||
#if _TASK_CLIENT
|
||||
|
||||
@@ -11,6 +11,7 @@ using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
using System;
|
||||
using BrewMonster.Scripts; // For CECNavigateCtrl
|
||||
|
||||
namespace BrewMonster.Scripts.Task
|
||||
{
|
||||
@@ -1625,19 +1626,25 @@ namespace BrewMonster.Scripts.Task
|
||||
{
|
||||
return m_pFinishedCountListBuf;
|
||||
}
|
||||
void SetForceNavigateFinishFlag(bool bFinish) { m_bForceNavigateFinish = bFinish;} //
|
||||
public void OnNewTask(int iTaskID)
|
||||
public void SetForceNavigateFinishFlag(bool bFinish) { m_bForceNavigateFinish = bFinish;} // Set force navigate finish flag // 设置强制导航完成标志
|
||||
public void OnNewTask(int iTaskID)
|
||||
{
|
||||
UnityEngine.Debug.Log($"[CECTaskInterface] OnNewTask: TaskID={iTaskID}");
|
||||
ATaskTempl pTempl = GetTaskTemplMan().GetTaskTemplByID((uint)iTaskID);
|
||||
if (pTempl != null
|
||||
&& pTempl.m_FixedData.m_enumMethod== (uint)TaskCompletionMethod.enumTMSimpleClientTaskForceNavi)
|
||||
{
|
||||
ATaskTempl pTempl = GetTaskTemplMan().GetTaskTemplByID((uint)iTaskID);
|
||||
if (pTempl != null
|
||||
&& pTempl.m_FixedData.m_enumMethod== (uint)TaskCompletionMethod.enumTMSimpleClientTaskForceNavi)
|
||||
{
|
||||
SetForceNavigateFinishFlag(false);
|
||||
|
||||
// TODO: trigger navigation event
|
||||
// m_pHost.OnNaviageEvent(iTaskID,CECNavigateCtrl::EM_PREPARE);
|
||||
}
|
||||
UnityEngine.Debug.Log($"[CECTaskInterface] OnNewTask: Task {iTaskID} is force navigate task, triggering EM_PREPARE");
|
||||
SetForceNavigateFinishFlag(false);
|
||||
|
||||
// Trigger navigation event // 触发导航事件
|
||||
m_pHost.OnNaviageEvent(iTaskID, (int)CECNavigateCtrl.NavigateEvent.EM_PREPARE);
|
||||
}
|
||||
else
|
||||
{
|
||||
UnityEngine.Debug.Log($"[CECTaskInterface] OnNewTask: Task {iTaskID} is not a force navigate task (pTempl={pTempl != null}, method={pTempl?.m_FixedData.m_enumMethod})");
|
||||
}
|
||||
}
|
||||
|
||||
public void OnTaskConfirmUpdate()
|
||||
{
|
||||
@@ -1653,34 +1660,99 @@ namespace BrewMonster.Scripts.Task
|
||||
OnTaskConfirmUpdate();
|
||||
}
|
||||
|
||||
public void OnCompleteTask(int iTaskID)
|
||||
public void OnCompleteTask(int iTaskID)
|
||||
{
|
||||
UnityEngine.Debug.Log($"[CECTaskInterface] OnCompleteTask: TaskID={iTaskID}");
|
||||
ATaskTempl pTempl = GetTaskTemplMan().GetTaskTemplByID((uint)iTaskID);
|
||||
if (pTempl != null &&
|
||||
pTempl.m_FixedData.m_enumMethod == (uint)TaskCompletionMethod.enumTMSimpleClientTaskForceNavi)
|
||||
{
|
||||
ATaskTempl pTempl = GetTaskTemplMan().GetTaskTemplByID((uint)iTaskID);
|
||||
if (pTempl != null &&
|
||||
pTempl.m_FixedData.m_enumMethod == (uint)TaskCompletionMethod.enumTMSimpleClientTaskForceNavi)
|
||||
{
|
||||
//TODO: trigger navigation end event
|
||||
// m_pHost.OnNaviageEvent(iTaskID,CECNavigateCtrl::EM_END);
|
||||
SetForceNavigateFinishFlag(false);
|
||||
}
|
||||
UnityEngine.Debug.Log($"[CECTaskInterface] OnCompleteTask: Task {iTaskID} is force navigate task, triggering EM_END");
|
||||
// Trigger navigation end event // 触发导航结束事件
|
||||
m_pHost.OnNaviageEvent(iTaskID, (int)CECNavigateCtrl.NavigateEvent.EM_END);
|
||||
SetForceNavigateFinishFlag(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
UnityEngine.Debug.Log($"[CECTaskInterface] OnCompleteTask: Task {iTaskID} is not a force navigate task");
|
||||
}
|
||||
}
|
||||
|
||||
public void TakeAwayCommonItem(uint ulTemplId, uint ulNum) {}
|
||||
|
||||
public void TakeAwayTaskItem(uint ulTemplId, uint ulNum) {}
|
||||
public void TakeAwayGold(uint ulNum) {}
|
||||
public void TakeAwayFactionConsumeContrib(int ulNum){}
|
||||
public void OnGiveupTask(int iTaskID)
|
||||
public void OnGiveupTask(int iTaskID)
|
||||
{
|
||||
UnityEngine.Debug.Log($"[CECTaskInterface] OnGiveupTask: TaskID={iTaskID}");
|
||||
ATaskTempl pTempl = GetTaskTemplMan().GetTaskTemplByID((uint)iTaskID);
|
||||
if (pTempl != null &&
|
||||
pTempl.m_FixedData.m_enumMethod == (uint)TaskCompletionMethod.enumTMSimpleClientTaskForceNavi)
|
||||
{
|
||||
ATaskTempl pTempl = GetTaskTemplMan().GetTaskTemplByID((uint)iTaskID);
|
||||
if (pTempl != null &&
|
||||
pTempl.m_FixedData.m_enumMethod == (uint)TaskCompletionMethod.enumTMSimpleClientTaskForceNavi)
|
||||
UnityEngine.Debug.Log($"[CECTaskInterface] OnGiveupTask: Task {iTaskID} is force navigate task, triggering EM_END");
|
||||
// Trigger navigation end event // 触发导航结束事件
|
||||
m_pHost.OnNaviageEvent(iTaskID, (int)CECNavigateCtrl.NavigateEvent.EM_END);
|
||||
SetForceNavigateFinishFlag(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
UnityEngine.Debug.Log($"[CECTaskInterface] OnGiveupTask: Task {iTaskID} is not a force navigate task");
|
||||
}
|
||||
}
|
||||
|
||||
// Handle task text click in UI - trigger navigation if it's a force navigate task // 处理任务UI文本点击 - 如果是强制导航任务则触发导航
|
||||
// This is called when user clicks on task name/link in the task UI // 当用户在任务UI中点击任务名称/链接时调用
|
||||
public void OnTaskTextClick(int iTaskID)
|
||||
{
|
||||
UnityEngine.Debug.Log($"[CECTaskInterface] OnTaskTextClick: TaskID={iTaskID}");
|
||||
|
||||
// Check if task exists and is a force navigate task // 检查任务是否存在且为强制导航任务
|
||||
ATaskTempl pTempl = GetTaskTemplMan().GetTaskTemplByID((uint)iTaskID);
|
||||
if (pTempl != null &&
|
||||
pTempl.m_FixedData.m_enumMethod == (uint)TaskCompletionMethod.enumTMSimpleClientTaskForceNavi)
|
||||
{
|
||||
UnityEngine.Debug.Log($"[CECTaskInterface] OnTaskTextClick: Task {iTaskID} is force navigate task, triggering navigation");
|
||||
|
||||
// Check if navigation is already prepared // 检查导航是否已准备
|
||||
CECHostNavigatePlayer pNavigatePlayer = m_pHost.GetNavigatePlayer();
|
||||
if (pNavigatePlayer != null && pNavigatePlayer.GetNavigateCtrl() != null)
|
||||
{
|
||||
// TODO: trigger navigation end event
|
||||
// m_pHost.OnNaviageEvent(iTaskID,CECNavigateCtrl::EM_END);
|
||||
SetForceNavigateFinishFlag(false);
|
||||
// If already prepared, trigger begin // 如果已准备,触发开始
|
||||
if (pNavigatePlayer.GetNavigateCtrl().IsInForceNavigateState())
|
||||
{
|
||||
UnityEngine.Debug.Log($"[CECTaskInterface] OnTaskTextClick: Navigation already prepared, triggering EM_BEGIN");
|
||||
m_pHost.OnNaviageEvent(iTaskID, (int)CECNavigateCtrl.NavigateEvent.EM_BEGIN);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Prepare first, then begin // 先准备,然后开始
|
||||
UnityEngine.Debug.Log($"[CECTaskInterface] OnTaskTextClick: Preparing navigation first, then beginning");
|
||||
m_pHost.OnNaviageEvent(iTaskID, (int)CECNavigateCtrl.NavigateEvent.EM_PREPARE);
|
||||
// Note: EM_BEGIN will be triggered after preparation is complete
|
||||
// 注意:EM_BEGIN 将在准备完成后触发
|
||||
// For now, trigger it immediately after a short delay
|
||||
// 目前,在短暂延迟后立即触发
|
||||
// TODO: Implement proper async handling if needed
|
||||
// TODO: 如果需要,实现适当的异步处理
|
||||
m_pHost.OnNaviageEvent(iTaskID, (int)CECNavigateCtrl.NavigateEvent.EM_BEGIN);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Prepare and begin navigation // 准备并开始导航
|
||||
UnityEngine.Debug.Log($"[CECTaskInterface] OnTaskTextClick: Preparing and beginning navigation");
|
||||
m_pHost.OnNaviageEvent(iTaskID, (int)CECNavigateCtrl.NavigateEvent.EM_PREPARE);
|
||||
m_pHost.OnNaviageEvent(iTaskID, (int)CECNavigateCtrl.NavigateEvent.EM_BEGIN);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UnityEngine.Debug.Log($"[CECTaskInterface] OnTaskTextClick: Task {iTaskID} is not a force navigate task, will use normal auto-move");
|
||||
// TODO: Implement normal auto-move behavior here
|
||||
// TODO: 在此处实现正常的自动移动行为
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateTaskUI(uint idTask, int reason)
|
||||
{
|
||||
|
||||
@@ -15,6 +15,7 @@ using NUnit.Framework;
|
||||
using PerfectWorld.Scripts.Task;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.EventSystems;
|
||||
using TMPro;
|
||||
using Unity.VisualScripting;
|
||||
|
||||
@@ -166,10 +167,171 @@ namespace BrewMonster.Scripts.Task.UI
|
||||
m_pBtn_SearchQuest.onClick.AddListener(OnCommand_searchquest);
|
||||
m_pBtn_Abandon.onClick.AddListener(OnCommand_abandon);
|
||||
|
||||
// Convert exactly like C++ OnEventLButtonDown_Txt_QuestItem // 完全按照C++ OnEventLButtonDown_Txt_QuestItem转换
|
||||
// C++ uses WM_LBUTTONDOWN event, we use EventTrigger PointerClick // C++使用WM_LBUTTONDOWN事件,我们使用EventTrigger PointerClick
|
||||
if (m_pTxt_QuestItem != null)
|
||||
{
|
||||
// Enable raycast for TMP link detection (like C++ GetItemLinkItemOn needs link info) // 为TMP链接检测启用射线投射(如C++ GetItemLinkItemOn需要链接信息)
|
||||
m_pTxt_QuestItem.raycastTarget = true;
|
||||
|
||||
// Add EventTrigger for PointerClick (like WM_LBUTTONDOWN in C++) // 为PointerClick添加EventTrigger(如C++中的WM_LBUTTONDOWN)
|
||||
EventTrigger trigger = m_pTxt_QuestItem.GetComponent<EventTrigger>();
|
||||
if (trigger == null)
|
||||
{
|
||||
trigger = m_pTxt_QuestItem.gameObject.AddComponent<EventTrigger>();
|
||||
}
|
||||
|
||||
// Clear existing triggers // 清除现有触发器
|
||||
trigger.triggers.Clear();
|
||||
|
||||
// Add PointerClick event (like WM_LBUTTONDOWN) // 添加PointerClick事件(如WM_LBUTTONDOWN)
|
||||
EventTrigger.Entry entry = new EventTrigger.Entry();
|
||||
entry.eventID = EventTriggerType.PointerClick;
|
||||
entry.callback.AddListener((data) => {
|
||||
PointerEventData pointerData = (PointerEventData)data;
|
||||
OnEventLButtonDown_Txt_QuestItem(pointerData);
|
||||
});
|
||||
trigger.triggers.Add(entry);
|
||||
|
||||
UnityEngine.Debug.Log($"[DlgTask] Awake: Added EventTrigger for PointerClick to m_pTxt_QuestItem (like C++ WM_LBUTTONDOWN)");
|
||||
}
|
||||
|
||||
OnInitDialog();
|
||||
}
|
||||
|
||||
// Convert exactly like C++ OnEventLButtonDown_Txt_QuestItem // 完全按照C++ OnEventLButtonDown_Txt_QuestItem转换
|
||||
// C++: void CDlgTask::OnEventLButtonDown_Txt_QuestItem(WPARAM wParam, LPARAM lParam, AUIObject *pObj)
|
||||
private void OnEventLButtonDown_Txt_QuestItem(PointerEventData eventData)
|
||||
{
|
||||
if (m_pTxt_QuestItem == null) return;
|
||||
|
||||
const string LINK_CLICK_VER = "DlgTaskLinkClickCamFix_v2";
|
||||
UnityEngine.Debug.Log($"[DlgTask] OnEventLButtonDown_Txt_QuestItem: {LINK_CLICK_VER} pressEventCamera={(eventData.pressEventCamera != null ? eventData.pressEventCamera.name : "null")}");
|
||||
|
||||
// C++: int x = GET_X_LPARAM(lParam); int y = GET_Y_LPARAM(lParam); // C++: int x = GET_X_LPARAM(lParam); int y = GET_Y_LPARAM(lParam);
|
||||
Vector2 localPoint;
|
||||
RectTransformUtility.ScreenPointToLocalPointInRectangle(
|
||||
m_pTxt_QuestItem.rectTransform,
|
||||
eventData.position,
|
||||
eventData.pressEventCamera,
|
||||
out localPoint);
|
||||
|
||||
UnityEngine.Debug.Log($"[DlgTask] OnEventLButtonDown_Txt_QuestItem: Click at localPoint={localPoint}, screenPos={eventData.position}");
|
||||
|
||||
// C++: GetItemLinkItemOn(x, y, pObj, &Item); // C++: GetItemLinkItemOn(x, y, pObj, &Item);
|
||||
// Find which link was clicked (like C++ GetItemLinkItemOn checks vecItemLink[i].rc.PtInRect) // 查找点击了哪个链接(如C++ GetItemLinkItemOn检查vecItemLink[i].rc.PtInRect)
|
||||
m_pTxt_QuestItem.ForceMeshUpdate();
|
||||
|
||||
private void Update()
|
||||
// Debug: verify TMP parsed <link> tags and we have linkInfo
|
||||
int linkCount = m_pTxt_QuestItem.textInfo?.linkCount ?? -1;
|
||||
UnityEngine.Debug.Log($"[DlgTask] OnEventLButtonDown_Txt_QuestItem: TMP linkCount={linkCount}, raycastTarget={m_pTxt_QuestItem.raycastTarget}, richText={m_pTxt_QuestItem.richText}");
|
||||
if (linkCount <= 0)
|
||||
{
|
||||
string text = m_pTxt_QuestItem.text ?? string.Empty;
|
||||
string preview = text.Substring(0, Mathf.Min(300, text.Length));
|
||||
UnityEngine.Debug.LogWarning($"[DlgTask] OnEventLButtonDown_Txt_QuestItem: No links parsed. Text preview: {preview}");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get camera for link detection // 获取用于链接检测的相机
|
||||
Camera camera = null;
|
||||
Canvas canvas = m_pTxt_QuestItem.GetComponentInParent<Canvas>();
|
||||
if (canvas != null)
|
||||
{
|
||||
// IMPORTANT: TMP_TextUtilities.FindIntersectingLink expects camera=null for ScreenSpaceOverlay.
|
||||
// 重要:ScreenSpaceOverlay 必须传 camera=null,否则会算错导致 linkIndex = -1。
|
||||
UnityEngine.Debug.Log($"[DlgTask] OnEventLButtonDown_Txt_QuestItem: Canvas renderMode={canvas.renderMode}, worldCamera={(canvas.worldCamera != null ? canvas.worldCamera.name : "null")}, pressEventCamera={(eventData.pressEventCamera != null ? eventData.pressEventCamera.name : "null")}");
|
||||
if (canvas.renderMode == RenderMode.ScreenSpaceOverlay)
|
||||
{
|
||||
camera = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
// ScreenSpaceCamera / WorldSpace
|
||||
camera = eventData.pressEventCamera != null ? eventData.pressEventCamera : canvas.worldCamera;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UnityEngine.Debug.LogWarning($"[DlgTask] OnEventLButtonDown_Txt_QuestItem: {LINK_CLICK_VER} Canvas not found in parents of m_pTxt_QuestItem!");
|
||||
// No canvas found; fall back to pressEventCamera (may be null) then main
|
||||
camera = eventData.pressEventCamera != null ? eventData.pressEventCamera : Camera.main;
|
||||
}
|
||||
|
||||
// Find intersecting link (like C++ checks vecItemLink[i].rc.PtInRect(x, y)) // 查找相交的链接(如C++检查vecItemLink[i].rc.PtInRect(x, y))
|
||||
int linkIndexCam = TMP_TextUtilities.FindIntersectingLink(m_pTxt_QuestItem, eventData.position, camera);
|
||||
int linkIndexNull = TMP_TextUtilities.FindIntersectingLink(m_pTxt_QuestItem, eventData.position, null);
|
||||
UnityEngine.Debug.Log($"[DlgTask] OnEventLButtonDown_Txt_QuestItem: {LINK_CLICK_VER} FindIntersectingLink(cam={(camera != null ? camera.name : "null")})={linkIndexCam}, FindIntersectingLink(null)={linkIndexNull}, screenPos={eventData.position}");
|
||||
|
||||
int linkIndex = linkIndexCam != -1 ? linkIndexCam : linkIndexNull;
|
||||
UnityEngine.Debug.Log($"[DlgTask] OnEventLButtonDown_Txt_QuestItem: FindIntersectingLink => linkIndex={linkIndex}, camera={(camera != null ? camera.name : "null")}, screenPos={eventData.position}");
|
||||
|
||||
// Dump all links for debugging
|
||||
for (int i = 0; i < m_pTxt_QuestItem.textInfo.linkCount; i++)
|
||||
{
|
||||
TMP_LinkInfo li = m_pTxt_QuestItem.textInfo.linkInfo[i];
|
||||
UnityEngine.Debug.Log($"[DlgTask] OnEventLButtonDown_Txt_QuestItem: Link[{i}] id={li.GetLinkID()} text={li.GetLinkText()}");
|
||||
}
|
||||
|
||||
// C++: if( Item.m_pItem != NULL ) // C++: if( Item.m_pItem != NULL )
|
||||
if (linkIndex != -1 && linkIndex < m_pTxt_QuestItem.textInfo.linkCount)
|
||||
{
|
||||
TMP_LinkInfo linkInfo = m_pTxt_QuestItem.textInfo.linkInfo[linkIndex];
|
||||
string linkID = linkInfo.GetLinkID();
|
||||
|
||||
UnityEngine.Debug.Log($"[DlgTask] OnEventLButtonDown_Txt_QuestItem: Found link - linkID={linkID}");
|
||||
|
||||
// C++: if( Item.m_pItem->GetType() == enumEICoord ) // C++: if( Item.m_pItem->GetType() == enumEICoord )
|
||||
// Check if this is a coordinate link (NPC, monster, item, target - all use enumEICoord in C++) // 检查这是否是坐标链接(NPC、怪物、物品、目标 - 在C++中都使用enumEICoord)
|
||||
if (linkID.StartsWith("coord_"))
|
||||
{
|
||||
// C++: if (IsTreasureMapSelected()){ OnCommand_TreasureMap(NULL); } // C++: if (IsTreasureMapSelected()){ OnCommand_TreasureMap(NULL); }
|
||||
// TODO: Implement IsTreasureMapSelected check if needed // TODO: 如果需要,实现IsTreasureMapSelected检查
|
||||
|
||||
// C++: else { CECUIHelper::FollowCoord(Item.m_pItem, m_idSelTask); } // C++: else { CECUIHelper::FollowCoord(Item.m_pItem, m_idSelTask); }
|
||||
// Extract entity ID from link (NPC, monster, item, target - all handled the same) // 从链接中提取实体ID(NPC、怪物、物品、目标 - 处理方式相同)
|
||||
string entityIdStr = linkID.Substring(6); // Remove "coord_" prefix
|
||||
if (int.TryParse(entityIdStr, out int entityID))
|
||||
{
|
||||
// Use currently selected task ID (like m_idSelTask in C++ FollowCoord call) // 使用当前选中的任务ID(如C++ FollowCoord调用中的m_idSelTask)
|
||||
int currentTaskID = m_idSelTask;
|
||||
|
||||
UnityEngine.Debug.Log($"[DlgTask] OnEventLButtonDown_Txt_QuestItem: Coordinate link clicked, EntityID={entityID} (NPC/Monster/Item/Target), task={currentTaskID}");
|
||||
|
||||
// C++: CECUIHelper::FollowCoord(Item.m_pItem, m_idSelTask); // C++: CECUIHelper::FollowCoord(Item.m_pItem, m_idSelTask);
|
||||
// FollowCoord triggers auto-move to the coordinates (for NPC, monster, item, target) // FollowCoord触发自动移动到坐标(适用于NPC、怪物、物品、目标)
|
||||
// This works for all entity types - NPC names, monster names, items, targets // 这适用于所有实体类型 - NPC名称、怪物名称、物品、目标
|
||||
if (currentTaskID > 0)
|
||||
{
|
||||
CECHostPlayer hostPlayer = GetHostPlayer();
|
||||
if (hostPlayer != null)
|
||||
{
|
||||
CECTaskInterface taskInterface = hostPlayer.GetTaskInterface();
|
||||
if (taskInterface != null)
|
||||
{
|
||||
// This matches C++: CECUIHelper::FollowCoord(Item.m_pItem, m_idSelTask)
|
||||
// 这匹配C++:CECUIHelper::FollowCoord(Item.m_pItem, m_idSelTask)
|
||||
bool ok = CECUIHelper.FollowCoord(entityID, currentTaskID);
|
||||
UnityEngine.Debug.Log($"[DlgTask] OnEventLButtonDown_Txt_QuestItem: FollowCoord(entity={entityID}, task={currentTaskID}) => {ok}");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Even without a task, still follow coord
|
||||
// 即使没有任务,也照样跟随坐标
|
||||
bool ok = CECUIHelper.FollowCoord(entityID, 0);
|
||||
UnityEngine.Debug.Log($"[DlgTask] OnEventLButtonDown_Txt_QuestItem: FollowCoord(entity={entityID}, task=0) => {ok}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// C++: ChangeFocus(NULL); // C++: ChangeFocus(NULL);
|
||||
// TODO: Implement ChangeFocus if needed // TODO: 如果需要,实现ChangeFocus
|
||||
}
|
||||
|
||||
private new void Update()
|
||||
{
|
||||
Tick();
|
||||
}
|
||||
@@ -545,7 +707,7 @@ namespace BrewMonster.Scripts.Task.UI
|
||||
|
||||
// Award NPC
|
||||
int nANPC = (int)pTemp.GetAwardNPC();
|
||||
UpdateAwardNPC(ref strNewTextItem, nANPC);
|
||||
UpdateAwardNPC(ref strNewTextItem, nANPC, idTask);
|
||||
|
||||
// Complete condition - always refresh to show updated progress
|
||||
UpdateCompleteCondition(ref strNewTextItem, ref strNewHintItem, tsi);
|
||||
@@ -668,11 +830,11 @@ namespace BrewMonster.Scripts.Task.UI
|
||||
UpdateTaskBaseDesc(ref strNewTextItem, tsi);
|
||||
|
||||
// Append: deliver NPC
|
||||
UpdateDeliverNPC(ref strNewTextItem, (int)pTemp.GetDeliverNPC());
|
||||
UpdateDeliverNPC(ref strNewTextItem, (int)pTemp.GetDeliverNPC(), idTask);
|
||||
|
||||
// Append: award NPC
|
||||
int nANPC = (int)pTemp.GetAwardNPC();
|
||||
UpdateAwardNPC(ref strNewTextItem, nANPC);
|
||||
UpdateAwardNPC(ref strNewTextItem, nANPC, idTask);
|
||||
|
||||
// Append: completion conditions
|
||||
UpdateCompleteCondition(ref strNewTextItem, ref strNewHintItem, tsi);
|
||||
@@ -1216,7 +1378,7 @@ namespace BrewMonster.Scripts.Task.UI
|
||||
}
|
||||
|
||||
|
||||
private void UpdateDeliverNPC(ref string strText, int nDNPC)
|
||||
private void UpdateDeliverNPC(ref string strText, int nDNPC, int idTask = 0)
|
||||
{
|
||||
// [中文] 交付NPC
|
||||
// [English] Deliver NPC
|
||||
@@ -1241,60 +1403,59 @@ namespace BrewMonster.Scripts.Task.UI
|
||||
}
|
||||
if (string.IsNullOrEmpty(npcName)) npcName = nDNPC.ToString();
|
||||
|
||||
// [中文] 追加到内容文本
|
||||
// [English] Append to content text
|
||||
// [中文] 追加到内容文本,如果找到坐标则添加可点击链接(如C++中的enumEICoord)
|
||||
// [English] Append to content text, add clickable link if coordinates found (like enumEICoord in C++)
|
||||
var sb = new System.Text.StringBuilder();
|
||||
sb.Append(GetStringFromTable(7620));
|
||||
sb.Append(npcName);
|
||||
|
||||
// Always create clickable link (even if coords aren't known yet).
|
||||
// 总是创建可点击链接(即使暂时不知道坐标)。
|
||||
sb.Append($"<link=\"coord_{nDNPC}\"><color=#00FF00>{npcName}</color></link>");
|
||||
sb.Append("\n");
|
||||
// if (m_pTxt_QuestItem != null)
|
||||
// {
|
||||
// Debug.Log($"UpdateDeliverNPC: {sb.ToString()}");
|
||||
// m_pTxt_QuestItem.text += sb.ToString();
|
||||
// }
|
||||
|
||||
strText += sb.ToString();
|
||||
}
|
||||
|
||||
private A3DVECTOR3 UpdateAwardNPC(ref string strText, int nANPC)
|
||||
private A3DVECTOR3 UpdateAwardNPC(ref string strText, int nANPC, int idTask = 0)
|
||||
{
|
||||
A3DVECTOR3 ret = new A3DVECTOR3(0f);
|
||||
// Award NPC
|
||||
if (nANPC == 0)
|
||||
{
|
||||
A3DVECTOR3 ret = new A3DVECTOR3(0f);
|
||||
// Award NPC
|
||||
if (nANPC == 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Lookup NPC name from element data
|
||||
string npcName = string.Empty;
|
||||
var edm = BrewMonster.ElementDataManProvider.GetElementDataMan();
|
||||
if (edm != null)
|
||||
{
|
||||
if (edm.essence_id_data_type_map.TryGetValue((uint)nANPC, out var dtype)
|
||||
&& dtype == DATA_TYPE.DT_NPC_ESSENCE
|
||||
&& edm.essence_id_data_map.TryGetValue((uint)nANPC, out var obj)
|
||||
&& obj is NPC_ESSENCE npc)
|
||||
{
|
||||
npcName = npc.Name;
|
||||
}
|
||||
}
|
||||
if (string.IsNullOrEmpty(npcName)) npcName = nANPC.ToString();
|
||||
|
||||
// Append to content
|
||||
var sb = new System.Text.StringBuilder();
|
||||
sb.Append(GetStringFromTable(7621));
|
||||
sb.Append(npcName);
|
||||
sb.Append("\n");
|
||||
// if (m_pTxt_Content != null)
|
||||
// {
|
||||
// Debug.Log($"Award NPC: {sb.ToString()}");
|
||||
// m_pTxt_QuestItem.text += sb.ToString();
|
||||
// }
|
||||
|
||||
strText += sb.ToString();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Lookup NPC name from element data
|
||||
string npcName = string.Empty;
|
||||
var edm = BrewMonster.ElementDataManProvider.GetElementDataMan();
|
||||
if (edm != null)
|
||||
{
|
||||
if (edm.essence_id_data_type_map.TryGetValue((uint)nANPC, out var dtype)
|
||||
&& dtype == DATA_TYPE.DT_NPC_ESSENCE
|
||||
&& edm.essence_id_data_map.TryGetValue((uint)nANPC, out var obj)
|
||||
&& obj is NPC_ESSENCE npc)
|
||||
{
|
||||
npcName = npc.Name;
|
||||
}
|
||||
}
|
||||
if (string.IsNullOrEmpty(npcName)) npcName = nANPC.ToString();
|
||||
|
||||
// Append to content, add clickable link if coordinates found (like enumEICoord in C++) // 追加到内容,如果找到坐标则添加可点击链接(如C++中的enumEICoord)
|
||||
var sb = new System.Text.StringBuilder();
|
||||
sb.Append(GetStringFromTable(7621));
|
||||
|
||||
// Add NPC name as clickable link if coordinates found (like enumEICoord in C++) // 如果找到坐标,将NPC名称添加为可点击链接(如C++中的enumEICoord)
|
||||
// In C++, it always creates a clickable link if coordinates are found // 在C++中,如果找到坐标,它总是创建一个可点击链接
|
||||
// For force navigate tasks, we'll trigger navigation when clicked // 对于强制导航任务,点击时将触发导航
|
||||
// Always create clickable link (even if coords aren't known yet).
|
||||
// 总是创建可点击链接(即使暂时不知道坐标)。
|
||||
sb.Append($"<link=\"coord_{nANPC}\"><color=#00FF00>{npcName}</color></link>");
|
||||
sb.Append("\n");
|
||||
|
||||
strText += sb.ToString();
|
||||
|
||||
return ret;
|
||||
}
|
||||
// Update completion conditions (monsters, players, gold, level/reincarnation/realm)
|
||||
private void UpdateCompleteCondition(ref string strText, ref string strHint, Task_State_info tsi)
|
||||
{
|
||||
@@ -1322,24 +1483,10 @@ namespace BrewMonster.Scripts.Task.UI
|
||||
{
|
||||
// strName = ModelRenderer.Scripts.Common.ByteToStringUtils.UshortArrayToUnicodeString(me.name);
|
||||
|
||||
bool bFind = false;
|
||||
A3DVECTOR3 vPos = CECUIHelper.GetTaskObjectCoordinates((int)id, ref bFind);
|
||||
|
||||
// TODO: serialize position info if found
|
||||
// ACHAR szPos[100];
|
||||
// EditBoxItemBase item(enumEICoord);
|
||||
// item.SetName(pMonster->name);
|
||||
if (bFind)
|
||||
{
|
||||
// a_sprintf(szPos, _AL("%f %f %f %d"), vPos.x, vPos.y, vPos.z, id);
|
||||
// item.SetInfo(szPos);
|
||||
// item.SetColor(A3DCOLORRGB(0, 255, 0));
|
||||
// strName = (ACHAR)AUICOMMON_ITEM_CODE_START + item.Serialize();
|
||||
}
|
||||
else
|
||||
{
|
||||
strName = ByteToStringUtils.UshortArrayToUnicodeString(pMonster.name);
|
||||
}
|
||||
// Always create clickable link (even if coords aren't known yet).
|
||||
// 总是创建可点击链接(即使暂时不知道坐标)。
|
||||
string monsterName = ByteToStringUtils.UshortArrayToUnicodeString(pMonster.name);
|
||||
strName = $"<link=\"coord_{id}\"><color=#00FF00>{monsterName}</color></link>";
|
||||
}
|
||||
|
||||
// Build description for this monster requirement
|
||||
@@ -1468,9 +1615,34 @@ namespace BrewMonster.Scripts.Task.UI
|
||||
string itemName = EC_IvtrItemUtils.Instance.ResolveItemName(itemTid);
|
||||
if (string.IsNullOrEmpty(itemName)) itemName = $"Item {itemTid}";
|
||||
|
||||
// Find coordinates for item (like C++ GetTaskObjectCoordinates) // 查找物品的坐标(如C++ GetTaskObjectCoordinates)
|
||||
int search_id = 0;
|
||||
if (pTempl.m_FixedData.m_enumMethod != (uint)TaskCompletionMethod.enumTMKillPlayer)
|
||||
{
|
||||
int id = (int)tsi.m_ItemsWanted[i].m_ulMonsterId;
|
||||
if (id > 0)
|
||||
{
|
||||
search_id = id;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Search for mine essence if needed // TODO: 如果需要,搜索矿点精华
|
||||
// const MINE_ESSENCE* pMine = SearchTaskMine(idTask);
|
||||
// if(pMine) search_id = pMine->id;
|
||||
}
|
||||
}
|
||||
|
||||
// Always create clickable link for the target (search_id).
|
||||
// 总是为目标(search_id)创建可点击链接。
|
||||
string displayName = itemName;
|
||||
if (search_id > 0)
|
||||
{
|
||||
displayName = $"<link=\"coord_{search_id}\"><color=#00FF00>{itemName}</color></link>";
|
||||
}
|
||||
|
||||
// Compose line: name and progress (gained/toGet)
|
||||
// 组合文本:名称与进度(已获得/所需)
|
||||
string strTemp = Format(GetStringFromTable(7625), itemName,
|
||||
string strTemp = Format(GetStringFromTable(7625), displayName,
|
||||
tsi.m_ItemsWanted[i].m_ulItemsGained,
|
||||
tsi.m_ItemsWanted[i].m_ulItemsToGet);
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
using BrewMonster.Network;
|
||||
using BrewMonster.Managers;
|
||||
using BrewMonster.Scripts.Task;
|
||||
using BrewMonster.Scripts;
|
||||
using CSNetwork.GPDataType;
|
||||
|
||||
namespace BrewMonster.Scripts.UI
|
||||
@@ -10,7 +13,65 @@ namespace BrewMonster.Scripts.UI
|
||||
public static A3DVECTOR3 GetTaskObjectCoordinates(int id, ref bool in_table)
|
||||
{
|
||||
in_table = false;
|
||||
return new A3DVECTOR3(0);
|
||||
|
||||
// 0) If the id is actually a runtime object id (npc/player/matter), fetch it from world directly.
|
||||
// IMPORTANT: GPDataTypeHelper.ISPLAYERID returns true for ANY positive int, so we must not use it to
|
||||
// decide whether something is an "object id". Use a heuristic instead.
|
||||
// 0) 如果这个id其实是运行时对象ID(NPC/玩家/物品),直接从World取坐标。
|
||||
// 重要:ISPLAYERID 对任何正整数都为 true,不能用它判断“是否对象ID”,这里用启发式判断。
|
||||
bool isLikelyRuntimeObjectId =
|
||||
GPDataTypeHelper.ISNPCID(id) ||
|
||||
GPDataTypeHelper.ISMATTERID(id) ||
|
||||
(id > 100000000); // player ids are typically huge; template ids are usually small
|
||||
|
||||
var world = EC_Game.GetGameRun()?.GetWorld();
|
||||
if (world != null && isLikelyRuntimeObjectId)
|
||||
{
|
||||
var obj = world.GetObject(id, 0);
|
||||
if (obj != null)
|
||||
{
|
||||
var objPos = obj.transform.position;
|
||||
in_table = true;
|
||||
return new A3DVECTOR3(objPos.x, objPos.y, objPos.z);
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to task_npc table (C++: ATaskTemplMan::GetTaskNPCInfo)
|
||||
// 回退到task_npc表(C++:ATaskTemplMan::GetTaskNPCInfo)
|
||||
ATaskTemplMan pMan = EC_Game.GetTaskTemplateMan();
|
||||
if (pMan != null && pMan.TryGetTaskNPCInfo((uint)id, out NPC_INFO info))
|
||||
{
|
||||
// NOTE: Keep original PW coordinate mapping: ret.Set(x, z, y)
|
||||
// 注意:保持原版坐标映射:ret.Set(x, z, y)
|
||||
in_table = true;
|
||||
return new A3DVECTOR3(info.x, info.z, info.y);
|
||||
}
|
||||
|
||||
// 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))
|
||||
{
|
||||
UnityEngine.Debug.Log($"[CECUIHelper] GetTaskObjectCoordinates: Resolved id={id} via coord_data.txt map={mapName} pos=({coordPos.x:F2},{coordPos.y:F2},{coordPos.z:F2})");
|
||||
in_table = true;
|
||||
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);
|
||||
|
||||
// TODO: Implement this method properly
|
||||
// A3DVECTOR3 ret(0.f);
|
||||
@@ -28,5 +89,101 @@ namespace BrewMonster.Scripts.UI
|
||||
// }
|
||||
// return ret;
|
||||
}
|
||||
|
||||
// Follow coord like C++ CECUIHelper::FollowCoord(enumEICoord, taskId)
|
||||
// 像C++的CECUIHelper::FollowCoord(enumEICoord, taskId)一样跟随坐标
|
||||
public static bool FollowCoord(int id, int taskId)
|
||||
{
|
||||
// Resolve coordinates
|
||||
bool inTable = false;
|
||||
A3DVECTOR3 vPos = GetTaskObjectCoordinates(id, ref inTable);
|
||||
if (!inTable)
|
||||
{
|
||||
// Fallback: use task regions if available (move to the center of the first region)
|
||||
// 回退:如果任务有区域信息,则移动到第一个区域的中心
|
||||
if (taskId > 0)
|
||||
{
|
||||
var taskMan = EC_Game.GetTaskTemplateMan();
|
||||
var templ = taskMan != null ? taskMan.GetTaskTemplByID((uint)taskId) : null;
|
||||
var world = EC_Game.GetGameRun()?.GetWorld();
|
||||
int curWorldId = world != null ? world.GetInstanceID() : 0;
|
||||
|
||||
if (templ != null)
|
||||
{
|
||||
// Helper local function: pick first region center if in current world
|
||||
bool TryUseRegion(uint worldId, uint cnt, BrewMonster.Scripts.Task.Task_Region[] regions, string tag, out A3DVECTOR3 pos)
|
||||
{
|
||||
pos = new A3DVECTOR3(0);
|
||||
if (cnt == 0 || regions == null || regions.Length == 0) return false;
|
||||
if (curWorldId != 0 && worldId != 0 && worldId != (uint)curWorldId) return false;
|
||||
|
||||
var r = regions[0];
|
||||
float cx = (r.zvMin.x + r.zvMax.x) * 0.5f;
|
||||
float cy = (r.zvMin.y + r.zvMax.y) * 0.5f;
|
||||
float cz = (r.zvMin.z + r.zvMax.z) * 0.5f;
|
||||
pos = new A3DVECTOR3(cx, cy, cz);
|
||||
UnityEngine.Debug.Log($"[CECUIHelper] FollowCoord: Fallback {tag} region center=({cx:F2},{cy:F2},{cz:F2}) worldId={worldId} curWorldId={curWorldId}");
|
||||
return true;
|
||||
}
|
||||
|
||||
// 1) Deliver zone (often where quest giver is)
|
||||
if (templ.m_FixedData.m_bDelvInZone &&
|
||||
TryUseRegion(templ.m_FixedData.m_ulDelvWorld, templ.m_FixedData.m_ulDelvRegionCnt, templ.m_FixedData.m_pDelvRegion, "DelvInZone", out vPos))
|
||||
{
|
||||
inTable = true;
|
||||
}
|
||||
// 2) Reach-site regions (for reach-site tasks / also can be used as guidance)
|
||||
else if (TryUseRegion(templ.m_FixedData.m_ulReachSiteId, templ.m_FixedData.m_ulReachSiteCnt, templ.m_FixedData.m_pReachSite, "ReachSite", out vPos))
|
||||
{
|
||||
inTable = true;
|
||||
}
|
||||
// 3) Enter region / leave region zones (some tasks use these to define where objectives happen)
|
||||
else if (TryUseRegion(templ.m_FixedData.m_ulEnterRegionWorld, templ.m_FixedData.m_ulEnterRegionCnt, templ.m_FixedData.m_pEnterRegion, "EnterRegion", out vPos))
|
||||
{
|
||||
inTable = true;
|
||||
}
|
||||
else if (TryUseRegion(templ.m_FixedData.m_ulLeaveRegionWorld, templ.m_FixedData.m_ulLeaveRegionCnt, templ.m_FixedData.m_pLeaveRegion, "LeaveRegion", out vPos))
|
||||
{
|
||||
inTable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!inTable)
|
||||
{
|
||||
UnityEngine.Debug.LogWarning($"[CECUIHelper] FollowCoord: No coordinates for id={id}, taskId={taskId} (will not move)");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Start auto-move work to destination (this is what actually moves the player in this project)
|
||||
// 启动自动移动工作(这才是本项目中真正驱动角色移动的系统)
|
||||
CECHostPlayer host = EC_Game.GetGameRun()?.GetHostPlayer();
|
||||
if (host == null)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"[CECUIHelper] FollowCoord: Host player is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
CECHPWorkMan wm = host.GetWorkMan();
|
||||
if (wm == null)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"[CECUIHelper] FollowCoord: WorkMan is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
CECHPWorkMove work = wm.CreateWork(CECHPWork.Host_work_ID.WORK_MOVETOPOS) as CECHPWorkMove;
|
||||
if (work == null)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"[CECUIHelper] FollowCoord: Failed to create WORK_MOVETOPOS");
|
||||
return false;
|
||||
}
|
||||
|
||||
work.SetDestination(CECHPWorkMove.DestTypes.DEST_2D, vPos);
|
||||
wm.StartWork_p2(work);
|
||||
|
||||
UnityEngine.Debug.Log($"[CECUIHelper] FollowCoord: Started auto-move to ({vPos.x},{vPos.y},{vPos.z}) for id={id}, taskId={taskId}");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -191,6 +191,51 @@ namespace BrewMonster
|
||||
return m_taskInventory;
|
||||
}
|
||||
|
||||
// Get work manager // 获取工作管理器
|
||||
public CECHPWorkMan GetWorkMan()
|
||||
{
|
||||
return m_pWorkMan;
|
||||
}
|
||||
|
||||
// Get navigate player // 获取导航玩家
|
||||
private CECHostNavigatePlayer m_pNavigatePlayer = null;
|
||||
public CECHostNavigatePlayer GetNavigatePlayer()
|
||||
{
|
||||
if (m_pNavigatePlayer == null)
|
||||
{
|
||||
// TODO: Implement proper creation of navigate player
|
||||
// m_pNavigatePlayer = CreateNavigatePlayer();
|
||||
}
|
||||
return m_pNavigatePlayer;
|
||||
}
|
||||
|
||||
// Check if in force navigate state // 检查是否在强制导航状态
|
||||
public bool IsInForceNavigateState()
|
||||
{
|
||||
CECHostNavigatePlayer pNavigatePlayer = GetNavigatePlayer();
|
||||
if (pNavigatePlayer != null && pNavigatePlayer.GetNavigateCtrl() != null)
|
||||
{
|
||||
return pNavigatePlayer.GetNavigateCtrl().IsInForceNavigateState();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle navigation event // 处理导航事件
|
||||
public void OnNaviageEvent(int task, int e)
|
||||
{
|
||||
UnityEngine.Debug.Log($"[CECHostPlayer] OnNaviageEvent: Task={task}, Event={e} ({(BrewMonster.Scripts.CECNavigateCtrl.NavigateEvent)e})");
|
||||
CECHostNavigatePlayer pNavigatePlayer = GetNavigatePlayer();
|
||||
if (pNavigatePlayer != null)
|
||||
{
|
||||
UnityEngine.Debug.Log($"[CECHostPlayer] OnNaviageEvent: Forwarding to NavigatePlayer");
|
||||
pNavigatePlayer.OnNavigateEvent(task, e);
|
||||
}
|
||||
else
|
||||
{
|
||||
UnityEngine.Debug.LogWarning($"[CECHostPlayer] OnNaviageEvent: NavigatePlayer is null");
|
||||
}
|
||||
}
|
||||
|
||||
public EC_Inventory GetInventory(byte byPackage)
|
||||
{
|
||||
switch (byPackage)
|
||||
|
||||
Reference in New Issue
Block a user