Add tasktrace task following behavior

This commit is contained in:
Chomper9981
2026-04-06 17:30:39 +07:00
parent 458f3cc518
commit bb4b349930
6 changed files with 255 additions and 48 deletions
@@ -38,7 +38,7 @@ RectTransform:
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 161.25, y: 0}
m_SizeDelta: {x: 322.5, y: 0}
m_SizeDelta: {x: 322.5, y: 300}
m_Pivot: {x: 0.5, y: 1}
--- !u!222 &9154858122360570458
CanvasRenderer:
@@ -372,6 +372,8 @@ MonoBehaviour:
m_pTxt_Desc: {fileID: 2531022629832384091}
m_pBtnFinishTaskByContribution: {fileID: 0}
m_pBtnContributionTaskHelp: {fileID: 0}
m_pToggleReceived: {fileID: 3754705535557026321}
m_pToggleNotReceived: {fileID: 6274674518893574845}
--- !u!1 &2291589776560736050
GameObject:
m_ObjectHideFlags: 0
@@ -449,7 +451,7 @@ MonoBehaviour:
m_PressedTrigger: Pressed
m_SelectedTrigger: Selected
m_DisabledTrigger: Disabled
m_Interactable: 0
m_Interactable: 1
m_TargetGraphic: {fileID: 8307833885626771741}
toggleTransition: 1
graphic: {fileID: 1446671815119800730}
@@ -587,6 +587,14 @@ namespace BrewMonster.Scripts.Task
pTempl.ClearValidCount();
// OnServerNotify method signature may need adjustment for C# (ref/out parameters)
pTempl.OnServerNotify(pTask, pEntry, pNotify, sz, pBuf);
// TASK_SVR_NOTIFY_COMPLETE (reason 2): re-expand trace for this quest line (subtask chain advance).
// TASK_SVR_NOTIFY_COMPLETEreason=2):子任务推进后重新挂接追踪链。
if (pNotify.reason == TaskTemplConstants.TASK_SVR_NOTIFY_COMPLETE)
{
var gameUi = EC_Game.GetGameRun()?.GetUIManager()?.GetInGameUIMan() as CECGameUIMan;
gameUi?.RetraceTaskAfterServerNotifyComplete(pNotify.task);
}
}
// Helper method to get task template manager
+163 -41
View File
@@ -122,6 +122,7 @@ namespace BrewMonster.Scripts.Task.UI
// private:
private static List<int> m_vecTasksUnFinish = new List<int>();
private static List<int> m_vecTasksCanFinish = new List<int>();
private static List<int> m_vecTasksCanGet = new List<int>();
// [中文] 目标坐标集合
// [English] Target coordinates collection
@@ -378,6 +379,7 @@ namespace BrewMonster.Scripts.Task.UI
m_pBtn_SearchQuest.interactable = true;
m_pBtn_HaveQuest.interactable = false;
UpdateTask();
RefreshVecTasksCanGet();
}
public void OnCommand_showtrace(string szCommand,bool state) {
m_bShowTrace = state;
@@ -401,6 +403,7 @@ namespace BrewMonster.Scripts.Task.UI
vec_task.Remove((int)idTask);
}
}
Debug.Log($"[DlgTask] TraceTask: idTask={idTask}, task name={pTask.GetTaskTemplMan().GetTaskTemplByID((uint)idTask).GetName()}");
//get the position of idTask in pLst
int position = pTask.GetFirstSubTaskPosition(idTask);
while(position != -1)
@@ -410,6 +413,60 @@ namespace BrewMonster.Scripts.Task.UI
}
ShowTraceDialog();
}
/// <summary>
/// Add task to trace lists if traceable (no toggle). Used when seeding trace after TASK_DATA init; mirrors TraceTask minus remove/toggle.
/// 若可追踪则加入追踪列表(无切换)。在 TASK_DATA 初始化后填充追踪时用;逻辑对齐 TraceTask 但不移除/切换。
/// </summary>
private void AddTaskToTraceListsNonToggle(uint idTask)
{
if (IsPQTaskOrSubTask((int)idTask))
return;
CECTaskInterface pTask = GetHostPlayer()?.GetTaskInterface();
if (pTask == null)
return;
bool bFinishedTask = pTask.CanFinishTask(idTask);
List<int> vec_task = bFinishedTask ? m_vecTasksCanFinish : m_vecTasksUnFinish;
if (IsTaskTraceable(idTask))
{
if (!vec_task.Contains((int)idTask))
vec_task.Add((int)idTask);
}
int position = pTask.GetFirstSubTaskPosition(idTask);
while (position != -1)
{
int idSub = pTask.GetNextSub(ref position);
AddTaskToTraceListsNonToggle((uint)idSub);
}
}
/// <summary>
/// After server task pack and templates are loaded (CECTaskInterface.Init finished), apply trace like USER_LAYOUT from server:
/// SyncTrace with a full dwTraceMask for the first 32 top-level slots, then add any further top-level tasks (mask is only 32 bits).
/// 服务端任务包与模板加载完成后调用:用满掩码对前 32 个顶层槽位 SyncTrace,再为更多顶层任务补充追踪(掩码仅 32 位)。
/// </summary>
public void SyncTraceAfterTaskDataInit(CECTaskInterface pTask)
{
if (pTask == null || GetHostPlayer()?.GetTaskInterface() != pTask)
return;
var ul = new USER_LAYOUT { bTraceAll = true };
int n = (int)pTask.GetTaskCount();
if (n > 0)
{
int bits = Mathf.Min(n, 32);
ul.dwTraceMask = bits >= 32 ? uint.MaxValue : ((1u << bits) - 1u);
}
SyncTrace(ul, true);
for (int i = 32; i < n; i++)
AddTaskToTraceListsNonToggle(pTask.GetTaskId((uint)i));
RefreshVecTasksCanGet(pTask);
RefreshTaskTrace(ul.bTraceAll);
}
public void ShowTraceDialog()
{
Debug.Log($"[DlgTask] ShowTraceDialog: m_bShowTrace={m_bShowTrace}");
@@ -422,6 +479,28 @@ namespace BrewMonster.Scripts.Task.UI
EventBus.Publish(new UIEvent(UIEventType.HideTrace));
}
}
/// <summary>
/// TASK_SVR_NOTIFY_COMPLETE (reason 2): re-expand the quest trace from the top task (add-only, same subchain walk as TraceTask).
/// Using TraceTask() here would toggle entries off/on incorrectly; this re-seeds the active subtask chain after server advance.
/// 服务端 reason=2 任务推进/完成后,从顶层任务重新挂接追踪子链(与 TraceTask 同 walk,但只添加不切换)。
/// </summary>
public void RetraceAfterTaskServerNotifyComplete(uint notifyTaskId, bool bShowTrace)
{
var pIface = GetHostPlayer()?.GetTaskInterface();
if (pIface == null)
return;
ATaskTemplMan pMan = EC_Game.GetTaskTemplateMan();
ATaskTempl t = pMan?.GetTaskTemplByID(notifyTaskId);
if (t == null)
return;
uint rootId = t.GetTopTask().GetID();
if (rootId == 0)
return;
if (pIface.HasTask(rootId))
AddTaskToTraceListsNonToggle(rootId);
RefreshTaskTrace(bShowTrace);
}
public void OnCommand_focus(string szCommand="") {
var pTree = m_pTv_Quest;
var pItem = pTree?.GetSelectedItem();
@@ -506,6 +585,7 @@ namespace BrewMonster.Scripts.Task.UI
int taskIdInt = (int)topTaskId;
m_vecTasksCanFinish.Remove(taskIdInt);
m_vecTasksUnFinish.Remove(taskIdInt);
RefreshVecTasksCanGet();
// Send notification to server to abandon the currently selected task
TaskClient._notify_svr(pTask, (byte)ClientNotificationConstants.TASK_CLT_NOTIFY_CHECK_GIVEUP, (ushort)topTaskId);
@@ -788,48 +868,62 @@ namespace BrewMonster.Scripts.Task.UI
{
CECHostPlayer host = GetHostPlayer();
CECTaskInterface pTask = host.GetTaskInterface();
if( m_vecTasksCanFinish.Count > 0 )
{
for(int i = 0; i < m_vecTasksCanFinish.Count; i++ )
{
int idTask = m_vecTasksCanFinish[i];
bool bCanFinish = pTask.CanFinishTask((uint)idTask);
bool bHasTask = pTask.HasTask((uint)idTask);
if(!bHasTask || !bCanFinish)
{
// 任务未完成,状态变化要删除
m_vecTasksCanFinish.RemoveAt(i);
i--;
if (bHasTask && !bCanFinish) {
if (!m_vecTasksUnFinish.Contains(idTask))
m_vecTasksUnFinish.Add(idTask);
}
}
}
}
if( m_vecTasksUnFinish.Count > 0 )
{
for(int i = 0; i < m_vecTasksUnFinish.Count; i++ )
{
int idTask = m_vecTasksUnFinish[i];
bool bCanFinish = pTask.CanFinishTask((uint)idTask);
bool bHasTask = pTask.HasTask((uint)idTask);
if(!bHasTask || bCanFinish)
{
// ûˣ״̬仯ҪƳ
m_vecTasksUnFinish.RemoveAt(i);
i--;
if (bHasTask && bCanFinish) {
if (!m_vecTasksCanFinish.Contains(idTask))
m_vecTasksCanFinish.Add(idTask);
}
}
}
}
ShowType2 showType2 = pDlgTaskTrace.GetShowType2();
List<int> tasks = new List<int>();
tasks.Capacity = m_vecTasksCanFinish.Count + m_vecTasksUnFinish.Count;
tasks.AddRange(m_vecTasksCanFinish);
tasks.AddRange(m_vecTasksUnFinish);
if(showType2 == ShowType2.ST_RECIEVED)
{
if( m_vecTasksCanFinish.Count > 0 )
{
for(int i = 0; i < m_vecTasksCanFinish.Count; i++ )
{
int idTask = m_vecTasksCanFinish[i];
bool bCanFinish = pTask.CanFinishTask((uint)idTask);
bool bHasTask = pTask.HasTask((uint)idTask);
if(!bHasTask || !bCanFinish)
{
// 任务未完成,状态变化要删除
m_vecTasksCanFinish.RemoveAt(i);
i--;
if (bHasTask && !bCanFinish) {
if (!m_vecTasksUnFinish.Contains(idTask))
m_vecTasksUnFinish.Add(idTask);
}
}
}
}
if( m_vecTasksUnFinish.Count > 0 )
{
for(int i = 0; i < m_vecTasksUnFinish.Count; i++ )
{
int idTask = m_vecTasksUnFinish[i];
bool bCanFinish = pTask.CanFinishTask((uint)idTask);
bool bHasTask = pTask.HasTask((uint)idTask);
if(!bHasTask || bCanFinish)
{
// ûˣ״̬仯ҪƳ
m_vecTasksUnFinish.RemoveAt(i);
i--;
if (bHasTask && bCanFinish) {
if (!m_vecTasksCanFinish.Contains(idTask))
m_vecTasksCanFinish.Add(idTask);
}
}
}
}
tasks.Capacity = m_vecTasksCanFinish.Count + m_vecTasksUnFinish.Count;
tasks.AddRange(m_vecTasksCanFinish);
tasks.AddRange(m_vecTasksUnFinish);
}
else if(showType2 == ShowType2.ST_NOT_RECIEVED)
{
// Re-run GetAvailableTasks every trace refresh; m_vecTasksCanGet was only filled on quest UI events and stayed stale.
// 每次追踪刷新重新计算可接任务;原先仅在任务界面事件里更新列表,游戏中会一直停留在开局数据。
RefreshVecTasksCanGet(pTask);
tasks.Capacity = m_vecTasksCanGet.Count;
tasks.AddRange(m_vecTasksCanGet);
}
List<ATaskTempl> titlle_list = new List<ATaskTempl>();
List<int> titletask_list = new List<int>();
//Dictionary<int, int> title_map = new Dictionary<int, int>();
@@ -1129,6 +1223,11 @@ namespace BrewMonster.Scripts.Task.UI
// TaskTemplLst ttl;
List<ATaskTempl> ttl = new List<ATaskTempl>(); // TaskTemplLst -> List<ATaskTempl>
pMan.GetAvailableTasks(pTask, ttl);
// Mirror available-to-accept IDs for trace / other systems (same set as tree nodes).
// 与可接任务树节点一致,填充可接任务 ID 列表供追踪等系统使用。
m_vecTasksCanGet.Clear();
for (int j = 0; j < ttl.Count; j++)
m_vecTasksCanGet.Add((int)ttl[j].GetID());
if( ttl.Count <= 0 ) return true;
for(int i = 0; i < ttl.Count; i++ )
@@ -1198,6 +1297,11 @@ namespace BrewMonster.Scripts.Task.UI
}
}
}
// Search view already rebuilt m_vecTasksCanGet inside SearchForTask(-1).
// 搜索页在 SearchForTask 内已更新 m_vecTasksCanGet。
if (m_iType == 0)
RefreshVecTasksCanGet();
return result;
}
@@ -1227,6 +1331,7 @@ namespace BrewMonster.Scripts.Task.UI
// store m_bShowTrace flag instead of bTraceAll flag
m_bShowTrace = pul.bTraceAll;
RefreshVecTasksCanGet();
}
else
{
@@ -1303,6 +1408,23 @@ namespace BrewMonster.Scripts.Task.UI
#region PRIVATE METHODS
/// <summary>
/// Rebuild m_vecTasksCanGet from ATaskTemplMan.GetAvailableTasks (same source as Search quest tree).
/// 用 GetAvailableTasks 重建可接任务 ID 列表,与可接任务搜索树数据源一致。
/// </summary>
private void RefreshVecTasksCanGet(CECTaskInterface pTaskIfKnown = null)
{
m_vecTasksCanGet.Clear();
CECTaskInterface pTask = pTaskIfKnown ?? GetHostPlayer()?.GetTaskInterface();
if (pTask == null) return;
ATaskTemplMan pMan = EC_Game.GetTaskTemplateMan();
if (pMan == null) return;
List<ATaskTempl> ttl = new List<ATaskTempl>();
pMan.GetAvailableTasks(pTask, ttl);
for (int i = 0; i < ttl.Count; i++)
m_vecTasksCanGet.Add((int)ttl[i].GetID());
}
private bool OnInitDialog()
{
// m_pTxt_QuestNO = (PAUILABEL)GetDlgItem("Txt_QuestNO");
@@ -20,6 +20,13 @@ namespace BrewMonster.Scripts.Task.UI
ST_TITLE, // ʾѽӳƺ
ST_CONTRIBUTION, // ʾѵĹ׶
}
//New show type in Unity version
public enum ShowType2
{
ST_NONE,
ST_RECIEVED,
ST_NOT_RECIEVED,
}
public class DlgTaskTrace : DlgNameLink
{
static string COLOR_YELLOW = "<color=#ffcb4a>";
@@ -34,8 +41,11 @@ namespace BrewMonster.Scripts.Task.UI
// [SerializeField] protected Button m_pBtnChat;
[SerializeField] protected Button m_pBtnFinishTaskByContribution;
[SerializeField] protected Button m_pBtnContributionTaskHelp;
[SerializeField] protected Toggle m_pToggleReceived;
[SerializeField] protected Toggle m_pToggleNotReceived;
protected int m_nLastTracedTasks; // ʾݶӦ׷ijЩбл
protected ShowType m_nLastShowType; // ʾݶӦб
protected ShowType2 m_nLastShowType2;
protected int m_nTraceWorldID; // ʾϢ׷ϢӦĵͼID
protected int m_iContributionCurrentPage;
protected int m_iContributionTotalPage;
@@ -69,13 +79,29 @@ namespace BrewMonster.Scripts.Task.UI
}
public override void Show(bool value)
{
// RefreshTaskTrace calls Show(true) every tick while visible; do not reset filter or re-wire toggles then.
// RefreshTaskTrace 每帧对已显示窗口调用 Show(true);勿重置“已接/可接”筛选,也勿重复注册 Toggle。
bool wasActive = gameObject.activeSelf;
gameObject.SetActive(value);
m_bShow = value;
if (value && !wasActive)
m_nLastShowType2 = ShowType2.ST_RECIEVED;
// Remove-before-Add in OnShowDialogue keeps this safe when RefreshTaskTrace calls Show(true) every frame.
OnShowDialogue();
}
public override void OnShowDialogue()
{
base.OnShowDialogue();
if (m_pToggleReceived != null && m_pToggleNotReceived != null)
{
m_pToggleReceived.onValueChanged.RemoveListener(OnToggleReceivedValueChanged);
m_pToggleNotReceived.onValueChanged.RemoveListener(OnToggleNotReceivedValueChanged);
if (m_bShow)
{
m_pToggleReceived.onValueChanged.AddListener(OnToggleReceivedValueChanged);
m_pToggleNotReceived.onValueChanged.AddListener(OnToggleNotReceivedValueChanged);
}
}
//m_pTxt_Desc = dynamic_cast<PAUITEXTAREA>(GetDlgItem("Txt_Link_Trace"));
//if(m_pTxt_Desc) m_pTxt_Desc->SetForceRenderScroll(false);
//m_pChk_Collapse = dynamic_cast<PAUICHECKBOX>(GetDlgItem("Chk_Collapse"));
@@ -96,6 +122,20 @@ namespace BrewMonster.Scripts.Task.UI
// );
// ׷ٽʾĬб
}
public void OnToggleReceivedValueChanged(bool value)
{
if(value)
{
m_nLastShowType2 = ShowType2.ST_RECIEVED;
}
}
public void OnToggleNotReceivedValueChanged(bool value)
{
if(value)
{
m_nLastShowType2 = ShowType2.ST_NOT_RECIEVED;
}
}
public void OnShowDialog()
{
GameObject pObjRadio = transform.Find("Rdo_Quest3").gameObject;
@@ -316,6 +356,10 @@ namespace BrewMonster.Scripts.Task.UI
{
return m_nLastShowType;
}
public ShowType2 GetShowType2()
{
return m_nLastShowType2;
}
void PrepareRebuildTaskTrace(){
m_Buffer = new StringRef();
m_Buffer.Value = "";
@@ -541,6 +585,10 @@ namespace BrewMonster.Scripts.Task.UI
var pTempl = pTaskMan.GetTaskTemplByID((uint)id);
var pEntry = pActiveLst.GetEntry((uint)id);
if (pTempl != null) {
// 任务名与描述都为空时不显示(数据缺失或损坏) / Hide when both name and description are empty (missing or corrupt data)
if (string.IsNullOrWhiteSpace(pTempl.GetName()) && string.IsNullOrWhiteSpace(pTempl.GetDescription()))
return;
int contribution = 0;
// ׶ȵֻڶʾֵ
if (hasContributionAward) {
@@ -635,17 +683,15 @@ namespace BrewMonster.Scripts.Task.UI
if (withName)
{
AppendText(strIndentIn);
string strNum = index.ToString();
strNum = string.Format("{0}", index);
string strName = pTemp.GetName();
DeleteColorStr(strName);
strName = strNum + " " + strName;
strName = " " + strName;
TaskNameHoverCommand cmd = new TaskNameHoverCommand(m_Buffer, strName, idTask, tsi.m_ulErrCode != 0, bCanContributionFinish);
BindLinkCommand(m_pTxt_Desc.tmp, strName, cmd);
if (contribution != 0) {
int colorIndex = contribution > 0 ? 11288 : 11291;
strNum = string.Format(" {0} {1}", GetStringFromTable(colorIndex), contribution);
AppendText(strNum);
strName = string.Format(" {0} {1}", GetStringFromTable(colorIndex), contribution);
AppendText(strName);
}
AppendText("\n");
}
@@ -193,6 +193,18 @@ namespace BrewMonster.UI
return m_pDlgTask.UpdateQuestView();
}
}
/// <summary>
/// Call after CECTaskInterface.Init completes (TASK_DATA). Restores/applies quest trace via DlgTask.SyncTrace and refreshes minimion.
/// 在 CECTaskInterface.InitTASK_DATA)完成后调用,通过 DlgTask.SyncTrace 应用任务追踪并刷新小窗。
/// </summary>
public void OnHostTaskDataInitialized(CECTaskInterface pTask)
{
if (m_pDlgTask == null || pTask == null)
return;
m_pDlgTask.SyncTraceAfterTaskDataInit(pTask);
}
public DialogScriptTableObject GetDialogResource()
{
return m_dialogResouce;
@@ -406,6 +418,18 @@ namespace BrewMonster.UI
}
}
/// <summary>
/// TASK_SVR_NOTIFY_COMPLETE (reason 2): refresh quest trace after server advances subtasks.
/// 服务端任务完成/推进通知后刷新任务追踪。
/// </summary>
public void RetraceTaskAfterServerNotifyComplete(ushort taskId)
{
if (m_pDlgTask == null)
return;
bool showTrace = IsDialogShow("Win_QuestMinion");
m_pDlgTask.RetraceAfterTaskServerNotifyComplete((uint)taskId, showTrace);
}
public void ShowErrorMsg(string pszMsg, string pszName)
{
+5
View File
@@ -107,6 +107,11 @@ namespace BrewMonster
// GET_ALL_DATA end flag tasks were here in C++ (LoadConfigData), omitted in C#
UnityGameSession.LoadConfigData();
// Quest trace lists + minimion: apply SyncTrace once active tasks and templates are ready.
// 任务追踪:在任务数据与模板就绪后调用 SyncTrace 同步追踪列表与小窗。
var pGameUI = EC_Game.GetGameRun()?.GetUIManager()?.GetInGameUIMan();
pGameUI?.OnHostTaskDataInitialized(m_pTaskInterface);
// if (UpdateEquipSkills()) UpdateEquipSkillCoolDown(); // methods not ported yet
}
else if (header == CommandID.TASK_VAR_DATA)