Merge pull request 'fixbug/task-bug-resolve' (#163) from fixbug/task-bug-resolve into develop

Reviewed-on: https://git.pthub.vn/Unity/perfect-world-unity/pulls/163
This commit is contained in:
namth
2026-02-24 09:26:24 +00:00
8 changed files with 520 additions and 922 deletions
File diff suppressed because it is too large Load Diff
@@ -8,13 +8,11 @@ using System.Text;
using System.Text.RegularExpressions;
namespace BrewMonster
{
public class StyledTaskTraceText : MonoBehaviour, IPointerMoveHandler,IPointerClickHandler
public class StyledTaskTraceText : MonoBehaviour, IPointerClickHandler
{
public TMP_Text tmp;
private List<LinkCommand> linkCommands =new List<LinkCommand>();
int hoveredLink = -1;
private void Start()
{
@@ -22,18 +20,41 @@ namespace BrewMonster
tmp.ForceMeshUpdate();
}
public void OnPointerMove(PointerEventData eventData)
// Helper method to find intersecting link with proper camera handling // 辅助方法,用于查找相交的链接并正确处理相机
private int FindIntersectingLink(PointerEventData eventData)
{
int linkIndex = TMP_TextUtilities.FindIntersectingLink(
tmp,
eventData.position,
eventData.pressEventCamera
);
if (tmp == null) return -1;
if (linkIndex != hoveredLink)
tmp.ForceMeshUpdate();
// Get camera for link detection // 获取用于链接检测的相机
Camera camera = null;
Canvas canvas = tmp.GetComponentInParent<Canvas>();
if (canvas != null)
{
hoveredLink = linkIndex;
// IMPORTANT: TMP_TextUtilities.FindIntersectingLink expects camera=null for ScreenSpaceOverlay.
// 重要:ScreenSpaceOverlay 必须传 camera=null,否则会算错导致 linkIndex = -1。
if (canvas.renderMode == RenderMode.ScreenSpaceOverlay)
{
camera = null;
}
else
{
// ScreenSpaceCamera / WorldSpace
camera = eventData.pressEventCamera != null ? eventData.pressEventCamera : canvas.worldCamera;
}
}
else
{
// No canvas found; fall back to pressEventCamera (may be null) then main
camera = eventData.pressEventCamera != null ? eventData.pressEventCamera : Camera.main;
}
// Try with camera first, then fallback to null // 先尝试使用相机,然后回退到null
int linkIndexCam = TMP_TextUtilities.FindIntersectingLink(tmp, eventData.position, camera);
int linkIndexNull = TMP_TextUtilities.FindIntersectingLink(tmp, eventData.position, null);
return linkIndexCam != -1 ? linkIndexCam : linkIndexNull;
}
public void Set(string text)
{
@@ -80,53 +101,49 @@ namespace BrewMonster
tmp.text = "";
linkCommands.Clear();
}
void SetLinkColor(int linkIndex, Color32 color)
{
TMP_LinkInfo linkInfo = tmp.textInfo.linkInfo[linkIndex];
for (int i = 0; i < linkInfo.linkTextLength; i++)
{
int charIndex = linkInfo.linkTextfirstCharacterIndex + i;
var charInfo = tmp.textInfo.characterInfo[charIndex];
if (!charInfo.isVisible) continue;
int meshIndex = charInfo.materialReferenceIndex;
int vertexIndex = charInfo.vertexIndex;
var colors = tmp.textInfo.meshInfo[meshIndex].colors32;
colors[vertexIndex + 0] = color;
colors[vertexIndex + 1] = color;
colors[vertexIndex + 2] = color;
colors[vertexIndex + 3] = color;
}
tmp.UpdateVertexData(TMP_VertexDataUpdateFlags.Colors32);
}
public void OnPointerClick(PointerEventData eventData)
{
TMP_LinkInfo linkInfo = tmp.textInfo.linkInfo[hoveredLink];
if (tmp == null) return;
// Detect link directly in click handler // 在点击处理程序中直接检测链接
int linkIndex = FindIntersectingLink(eventData);
// Validate link index // 验证链接索引
if (linkIndex == -1 || linkIndex >= tmp.textInfo.linkCount || linkIndex >= linkCommands.Count)
{
return;
}
tmp.ForceMeshUpdate();
TMP_LinkInfo linkInfo = tmp.textInfo.linkInfo[linkIndex];
string linkText = linkInfo.GetLinkText();
string entityID = linkInfo.GetLinkID();
switch (entityID)
{
case "coord":
linkCommands[hoveredLink].Execute(tmp);
//Debug.Log($"[StyledTaskTraceText] OnPointerClick: linkCommands[hoveredLink].Execute(tmp) => {linkCommands[hoveredLink].Execute(tmp)}");
if (linkIndex < linkCommands.Count && linkCommands[linkIndex] != null)
{
linkCommands[linkIndex].Execute(tmp);
}
//Debug.Log($"[StyledTaskTraceText] OnPointerClick: linkCommands[{linkIndex}].Execute(tmp) => {linkCommands[linkIndex].Execute(tmp)}");
break;
case "npc":
linkCommands[hoveredLink].Execute(tmp);
if (linkIndex < linkCommands.Count && linkCommands[linkIndex] != null)
{
linkCommands[linkIndex].Execute(tmp);
}
break;
case "monster":
break;
default:
Debug.Log($"Clicked unknown link: ID={entityID} Text={linkText}");
break;
break;
}
hoveredLink = -1;
}
}
}
@@ -1349,6 +1349,7 @@ public static class generate_item_temp
content_length = (int)(size - offset);
WriteInt(data, ref content_length_ptr, content_length);
WriteInt(data, ref item_content, offset);
WriteInt(data, ref offset, ess.food_type); //ƷĿװ־
WriteInt(data, ref offset, ess.hornor); //ƷӦID guid
@@ -1357,7 +1358,69 @@ public static class generate_item_temp
itemdataman.set_to_classid(DATA_TYPE.DT_PET_FOOD_ESSENCE, data, -1);
return 0;
}
public static int generate_flysword<RAND_CLASS>(uint id, ID_SPACE idspace, out byte[] data, out uint size, RAND_CLASS cls,item_tag_t tag)
{
data = new byte[0];
size = 0;
DATA_TYPE datatype = DATA_TYPE.DT_INVALID;
object obj = itemdataman._edm.get_data_ptr(id, idspace, ref datatype);
if(obj == null || datatype != DATA_TYPE.DT_FLYSWORD_ESSENCE)
return -1;
FLYSWORD_ESSENCE ess = (FLYSWORD_ESSENCE)obj;
size = (uint)(Marshal.SizeOf(typeof(item_data)) + Marshal.SizeOf(typeof(FLYSWORD_ESSENCE)));
data = new byte[size];
int offset = 0;
WriteUInt(data, ref offset, id);
WriteUInt(data, ref offset, 1);
WriteUInt(data, ref offset, (uint)ess.pile_num_max);
WriteInt(data, ref offset, (int)itemdataman.ELEMENTDATAMAN_EQUIP_MASK_FLYSWORD);
WriteInt(data, ref offset, (int)ess.proc_type);
WriteInt(data, ref offset, (int)DATA_TYPE.DT_FLYSWORD_ESSENCE);
if(ess.has_guid == 1){
int g1,g2;
itemdataman.get_item_guid(id,out g1,out g2);
WriteInt(data, ref offset, g1);
WriteInt(data, ref offset, g2);
}
else{
WriteInt(data, ref offset, 0);
}
WriteInt(data, ref offset, ess.price);
WriteInt(data, ref offset, 0); //ʱ
int content_length = 0;
int content_length_ptr = offset;
WriteInt(data, ref offset, 0);
int item_content = offset;
WriteInt(data, ref offset, 0);
content_length = (int)(size - offset);
WriteInt(data, ref content_length_ptr, content_length);
WriteInt(data, ref item_content, offset);
int t = element_data.RandNormal<RAND_CLASS, LOWER>((int)ess.time_max_min, (int)ess.time_max_max, cls, LOWER.LOWER_TREND);
// essence
WriteInt(data, ref offset, (int)(t*0.5f));
WriteInt(data, ref offset, (int)t);
WriteShort(data, ref offset, (short)ess.require_player_level_min);
WriteByte(data, ref offset, (byte)ess.level);
WriteByte(data, ref offset, 0);
WriteInt(data, ref offset, (int)ess.character_combo_id);
WriteInt(data, ref offset, (int)ess.time_increase_per_element);
float speed = element_data.Rand<RAND_CLASS, LOWER>(ess.speed_increase_min, ess.speed_increase_max, cls, LOWER.LOWER_TREND);
WriteFloat(data, ref offset, speed);
speed = element_data.Rand<RAND_CLASS, LOWER>(ess.speed_rush_increase_min, ess.speed_rush_increase_max, cls, LOWER.LOWER_TREND);
WriteFloat(data, ref offset, speed);
//߱ǩ
WriteByte(data, ref offset, tag.type);
WriteByte(data, ref offset, (byte)tag.size);
itemdataman.set_to_classid(DATA_TYPE.DT_FLYSWORD_ESSENCE, data, -1);
return 0;
}
public static int generate_townscroll<RAND_CLASS>(uint id, ID_SPACE idspace, out byte[] data, out uint size, RAND_CLASS cls)
{
DATA_TYPE datatype = DATA_TYPE.DT_INVALID;
@@ -1402,6 +1465,7 @@ public static class generate_item_temp
itemdataman.set_to_classid(DATA_TYPE.DT_TOWNSCROLL_ESSENCE, data, -1);
return 0;
}
#region Write Functions
private static void WriteUInt(byte[] buf, ref int offset, uint value)
{
@@ -491,6 +491,9 @@ namespace BrewMonster
case DATA_TYPE.DT_PET_FOOD_ESSENCE:
ret = generate_item_temp.generate_pet_food(id, ID_SPACE.ID_SPACE_ESSENCE, out item, out size, SPECIFIC.SPECIFIC_RAND);
break;
case DATA_TYPE.DT_FLYSWORD_ESSENCE:
ret = generate_item_temp.generate_flysword(id, ID_SPACE.ID_SPACE_ESSENCE, out item, out size, SPECIFIC.SPECIFIC_RAND, tag);
break;
case DATA_TYPE.DT_TOWNSCROLL_ESSENCE:
ret = generate_item_temp.generate_townscroll(id, ID_SPACE.ID_SPACE_ESSENCE, out item, out size, SPECIFIC.SPECIFIC_RAND);
break;
@@ -500,7 +500,6 @@ namespace BrewMonster.Scripts.Task
// Follow C++ logic: valid_size checks the buffer size before deserializing
// In C++, pNotify is a pointer to the structure, so valid_size can read sub_tags.sz directly
// In C#, we need to read sub_tags.sz from the buffer first, then validate
// Calculate base size: task_notify_base (3) + cur_time (4) + cap_task (4) = 11
int baseSzNew = Marshal.SizeOf<task_notify_base>() + 8;
if (sz <= (uint)baseSzNew)
@@ -709,13 +708,16 @@ namespace BrewMonster.Scripts.Task
// pTask.UpdateTaskEmotionAction(m_FixedData.m_ID);
if ((m_enumMethod == (uint)TaskCompletionMethod.enumTMSimpleClientTask)
&& m_FixedData.m_uiEmotion > 0)
{
// TODO: Pop emotion UI
// PopEmotionUI(m_ID,m_uiEmotion,false);
}
if (m_FixedData.m_bDisplayInTitleTaskUI)
{
// TODO: Update title UI
// UpdateTitleUI(m_ID);
}
pTask.OnCompleteTask((int)m_FixedData.m_ID);
break;
+14 -5
View File
@@ -155,6 +155,10 @@ namespace BrewMonster.Scripts.Task.UI
OnShowDialog();
OnCommand_havequest();
}
private new void OnDisable()
{
OnHideDialog();
}
private new void Awake()
{
@@ -345,10 +349,6 @@ namespace BrewMonster.Scripts.Task.UI
CECUIHelper.FollowCoord(entityID, taskID);
}
private new void Update()
{
Tick();
}
#endregion
@@ -713,7 +713,7 @@ namespace BrewMonster.Scripts.Task.UI
return string.Empty;
}
//
private bool Tick()
public bool Tick()
{
RefreshTaskTrace();
// Time-window task refresh: while in Search view, refresh the list when server time crosses a minute boundary.
@@ -1386,6 +1386,15 @@ namespace BrewMonster.Scripts.Task.UI
if (m_idSelTask != 0)
UpdateTask();
}
void OnHideDialog()
{
if (m_szName != "Win_Quest") return;
var pTree = m_pTv_Quest;
var pItem = pTree?.GetSelectedItem();
if( pItem )
m_idSelTask = (int)pTree.GetItemData(pItem);
else m_idSelTask = 0;
}
// virtual void OnHideDialog();
// virtual bool OnChangeLayout(PAUIOBJECT pMine, PAUIOBJECT pTheir);
// virtual void OnChangeLayoutEnd(bool bAllDone);
@@ -31,9 +31,9 @@ namespace BrewMonster.Scripts.Task.UI
[SerializeField] protected Toggle m_pChk_Collapse;
[SerializeField] protected GameObject m_pCollapsedArea;
protected Dictionary<string, int> m_uniqueNameCountMap = new Dictionary<string, int>();
[SerializeField] protected Button m_pBtnUnTrace;
[SerializeField] protected Button m_pBtnMap;
[SerializeField] protected Button m_pBtnChat;
// [SerializeField] protected Button m_pBtnUnTrace;
// [SerializeField] protected Button m_pBtnMap;
// [SerializeField] protected Button m_pBtnChat;
[SerializeField] protected Button m_pBtnFinishTaskByContribution;
[SerializeField] protected Button m_pBtnContributionTaskHelp;
protected int m_nLastTracedTasks; // ʾݶӦ׷ijЩбл
@@ -53,11 +53,11 @@ namespace BrewMonster.Scripts.Task.UI
//AUI_ON_COMMAND("Rdo_Quest*", OnCommand_SwitchShowType)
//Not sure if we need this
//AUI_ON_COMMAND("Btn_UnTrace", OnCommand_UnTraceTask)
m_pBtnUnTrace.onClick.AddListener(OnCommand_UnTraceTask);
//AUI_ON_COMMAND("Btn_Map", OnCommand_Map)
m_pBtnMap.onClick.AddListener(OnCommand_Map);
//AUI_ON_COMMAND("Btn_ForChat", OnCommand_Chat)
m_pBtnChat.onClick.AddListener(UpdateContributionTask);
// m_pBtnUnTrace.onClick.AddListener(OnCommand_UnTraceTask);
// //AUI_ON_COMMAND("Btn_Map", OnCommand_Map)
// m_pBtnMap.onClick.AddListener(OnCommand_Map);
// //AUI_ON_COMMAND("Btn_ForChat", OnCommand_Chat)
// m_pBtnChat.onClick.AddListener(UpdateContributionTask);
//AUI_ON_COMMAND("Btn_Finish", OnCommand_FinishTask)
//m_pBtnFinishTaskByContribution.onClick.AddListener(() => OnCommand_FinishTask(null));
+40
View File
@@ -33,6 +33,10 @@ public class CECUIManager : MonoSingleton<CECUIManager>
Sprite[] m_iconlistIvtr;
private CDlgInfoTooltip m_pDlgSkillTooltip;
// Task update timer / 任务更新计时器
private float _nextTaskUpdateTime = 0f;
private const float TASK_UPDATE_INTERVAL = .1f;
protected override void Awake()
{
base.Awake();
@@ -58,6 +62,42 @@ public class CECUIManager : MonoSingleton<CECUIManager>
// _fpsText.text = $"{Mathf.RoundToInt(1f / Time.deltaTime)}";
if (m_pDlgQuickBar1 != null)
{ m_pDlgQuickBar1.UpdateShortcuts(); }
// Periodically update task UI (DlgTask and DlgTaskTrace) / 定期更新任务UIDlgTask和DlgTaskTrace
if (Time.unscaledTime >= _nextTaskUpdateTime)
{
_nextTaskUpdateTime = Time.unscaledTime + TASK_UPDATE_INTERVAL;
UpdateTaskUI();
}
}
/// <summary>
/// Update task UI from DlgTask and DlgTaskTrace / 从DlgTask和DlgTaskTrace更新任务UI
/// </summary>
private void UpdateTaskUI()
{
try
{
var inGameUIMan = GetInGameUIMan();
if (inGameUIMan == null) return;
var dlgTaskDialog = inGameUIMan.GetDialog("Win_Quest");
var dlgTaskTraceDialog = inGameUIMan.GetDialog("Win_QuestMinion");
if (dlgTaskDialog != null && dlgTaskDialog is BrewMonster.Scripts.Task.UI.DlgTask dlgTaskForTrace)
{
if (dlgTaskTraceDialog != null && dlgTaskTraceDialog.IsShow())
{
dlgTaskForTrace.RefreshTaskTrace();
}
dlgTaskForTrace.Tick();
}
}
catch (System.Exception)
{
// Silently handle errors to avoid spamming logs / 静默处理错误以避免日志刷屏
// Task UI update errors are handled silently / 任务UI更新错误被静默处理
}
}
private void OnDestroy()