Fixing wrong logic check task and subtask
This commit is contained in:
@@ -449,40 +449,100 @@ namespace BrewMonster.Scripts.Task
|
||||
m_uUsedCount += pTempl.m_uDepth;
|
||||
}
|
||||
}
|
||||
void RealignTask(ActiveTaskEntry pEntry, byte uReserve)
|
||||
/// <summary>
|
||||
/// Shift-based realign that matches original C++ ActiveTaskList::RealignTask.
|
||||
/// It only adjusts indices in a controlled range instead of globally compacting the list.
|
||||
/// This is critical for keeping Parent/Child/Sibling indices stable during subtask progression.
|
||||
/// </summary>
|
||||
public void RealignTask(ActiveTaskEntry pEntry, byte uReserve)
|
||||
{
|
||||
// 简化实现:压缩数组,移除空洞(m_ID==0 或 null)
|
||||
// English: Simplified implementation: compact array, remove holes (m_ID==0 or null).
|
||||
//
|
||||
// NOTE: This does not preserve complex parent/child/sibling linkage yet. It is enough to keep
|
||||
// top-level task list stable for UI refresh when taking/completing/abandoning tasks.
|
||||
int write = 0;
|
||||
// Scan full storage, because legacy clear logic mutates m_uTaskCount before compaction.
|
||||
// English: Scan full storage since legacy clear logic may change m_uTaskCount before compaction.
|
||||
int count = m_TaskEntries.Length;
|
||||
for (int read = 0; read < count; read++)
|
||||
if (pEntry == null) return;
|
||||
int uCurIndex = Array.IndexOf(m_TaskEntries, pEntry);
|
||||
if (uCurIndex < 0) return;
|
||||
RealignTaskAtIndex(uCurIndex, uReserve);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Index-based variant used by deliver/award flows where the "slot" matters (C++ passes an entry pointer).
|
||||
/// </summary>
|
||||
public void RealignTaskAtIndex(int uCurIndex, byte uReserve)
|
||||
{
|
||||
if (uCurIndex < 0 || uCurIndex >= TaskInterfaceConstants.TASK_ACTIVE_LIST_MAX_LEN) return;
|
||||
|
||||
int ulCount = m_uTaskCount - uCurIndex; // remaining entries from uCurIndex
|
||||
if (ulCount == 0) return;
|
||||
|
||||
// Count consecutive empty entries starting from uCurIndex
|
||||
int uEmptyCount = 0;
|
||||
for (int uEmpty = uCurIndex; uEmpty < TaskInterfaceConstants.TASK_ACTIVE_LIST_MAX_LEN; uEmpty++)
|
||||
{
|
||||
ActiveTaskEntry e = m_TaskEntries[read];
|
||||
if (e == null || e.m_ID == 0) continue;
|
||||
if (write != read) m_TaskEntries[write] = e;
|
||||
write++;
|
||||
var e = m_TaskEntries[uEmpty];
|
||||
if (e == null || e.m_ID == 0) uEmptyCount++;
|
||||
else break;
|
||||
}
|
||||
|
||||
for (int i = write; i < m_TaskEntries.Length; i++)
|
||||
|
||||
if (uReserve == uEmptyCount) return;
|
||||
|
||||
int srcIndex = uCurIndex + uEmptyCount;
|
||||
int insertIndex = uCurIndex + uReserve;
|
||||
int uGap = insertIndex - srcIndex;
|
||||
if (uGap == 0) return;
|
||||
|
||||
// Move the block [srcIndex, srcIndex + ulCount) -> [insertIndex, insertIndex + ulCount)
|
||||
if (uGap > 0)
|
||||
{
|
||||
for (int i = ulCount - 1; i >= 0; i--)
|
||||
m_TaskEntries[insertIndex + i] = m_TaskEntries[srcIndex + i];
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < ulCount; i++)
|
||||
m_TaskEntries[insertIndex + i] = m_TaskEntries[srcIndex + i];
|
||||
}
|
||||
|
||||
// Clear vacated slots
|
||||
int clearStart, clearEnd;
|
||||
if (insertIndex > srcIndex)
|
||||
{
|
||||
clearStart = srcIndex;
|
||||
clearEnd = insertIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
clearStart = insertIndex + ulCount;
|
||||
clearEnd = srcIndex + ulCount;
|
||||
}
|
||||
for (int i = clearStart; i < clearEnd; i++)
|
||||
m_TaskEntries[i] = null;
|
||||
|
||||
m_uTaskCount = (byte)write;
|
||||
|
||||
// Reset linkage to "top-level only" defaults
|
||||
for (int i = 0; i < m_uTaskCount; i++)
|
||||
|
||||
// Adjust indices for entries before uCurIndex (child + next sibling that point into >= uCurIndex)
|
||||
for (int i = 0; i < uCurIndex; i++)
|
||||
{
|
||||
var e = m_TaskEntries[i];
|
||||
e.m_ParentIndex = (char)0xff;
|
||||
e.m_PrevSblIndex = (char)0xff;
|
||||
e.m_NextSblIndex = (char)0xff;
|
||||
e.m_ChildIndex = (char)0xff;
|
||||
var cur = m_TaskEntries[i];
|
||||
if (cur == null || cur.m_ID == 0) continue;
|
||||
|
||||
if (cur.m_ChildIndex != 0xff && cur.m_ChildIndex >= uCurIndex)
|
||||
cur.m_ChildIndex = (char)(cur.m_ChildIndex + uGap);
|
||||
if (cur.m_NextSblIndex != 0xff && cur.m_NextSblIndex >= uCurIndex)
|
||||
cur.m_NextSblIndex = (char)(cur.m_NextSblIndex + uGap);
|
||||
}
|
||||
|
||||
|
||||
// Adjust indices for moved entries (the inserted block)
|
||||
for (int i = 0; i < ulCount; i++)
|
||||
{
|
||||
var cur = m_TaskEntries[insertIndex + i];
|
||||
if (cur == null || cur.m_ID == 0) continue;
|
||||
|
||||
if (cur.m_ParentIndex != 0xff && cur.m_ParentIndex >= uCurIndex)
|
||||
cur.m_ParentIndex = (char)(cur.m_ParentIndex + uGap);
|
||||
if (cur.m_PrevSblIndex != 0xff && cur.m_PrevSblIndex >= uCurIndex)
|
||||
cur.m_PrevSblIndex = (char)(cur.m_PrevSblIndex + uGap);
|
||||
if (cur.m_ChildIndex != 0xff)
|
||||
cur.m_ChildIndex = (char)(cur.m_ChildIndex + uGap);
|
||||
if (cur.m_NextSblIndex != 0xff)
|
||||
cur.m_NextSblIndex = (char)(cur.m_NextSblIndex + uGap);
|
||||
}
|
||||
|
||||
RecountTaskCounters();
|
||||
}
|
||||
|
||||
@@ -516,6 +576,30 @@ namespace BrewMonster.Scripts.Task
|
||||
RecursiveClearTask(pTask, pEntry, bRemoveItem, true, true);
|
||||
RealignTask(pEntry, 0);
|
||||
}
|
||||
|
||||
// void ClearChildrenOf(TaskInterface* pTask, ActiveTaskEntry* pParent, bool bRemoveItem = true);
|
||||
// 清除指定父节点的所有子任务(不清除父节点自身) // English: Clear all children of a parent entry (but keep the parent entry)
|
||||
public void ClearChildrenOf(TaskInterface pTask, ActiveTaskEntry pParent, bool bRemoveItem = true)
|
||||
{
|
||||
if (pParent == null) return;
|
||||
|
||||
// Mirror C++: while parent has a first child, recursively clear that child subtree.
|
||||
while (pParent.m_ChildIndex != 0xff)
|
||||
{
|
||||
int childIndex = (byte)pParent.m_ChildIndex;
|
||||
if (childIndex < 0 || childIndex >= TaskInterfaceConstants.TASK_ACTIVE_LIST_MAX_LEN) break;
|
||||
|
||||
var child = m_TaskEntries[childIndex];
|
||||
if (child == null || child.m_ID == 0)
|
||||
{
|
||||
// Broken link: stop to avoid infinite loop
|
||||
pParent.m_ChildIndex = (char)0xff;
|
||||
break;
|
||||
}
|
||||
|
||||
RecursiveClearTask(pTask, child, bRemoveItem, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
void RecursiveClearTask(
|
||||
TaskInterface pTask,
|
||||
@@ -602,7 +686,6 @@ namespace BrewMonster.Scripts.Task
|
||||
|
||||
return null;
|
||||
}
|
||||
// void ClearChildrenOf(TaskInterface* pTask, ActiveTaskEntry* pParent, bool bRemoveItem = true);
|
||||
// ActiveTaskEntry* GetEntry(unsigned long ulId)
|
||||
// {
|
||||
// for (unsigned char i = 0; i < m_uTaskCount; i++)
|
||||
@@ -637,15 +720,27 @@ namespace BrewMonster.Scripts.Task
|
||||
m_uMaxSimultaneousCount = true;
|
||||
}
|
||||
|
||||
// 从列表中移除指定条目(并压缩列表) // English: Remove an entry from the list (and compact)
|
||||
// 从列表中移除指定条目(并重新对齐列表) // English: Remove an entry from the list (and realign)
|
||||
public void RemoveEntry(ActiveTaskEntry entry)
|
||||
{
|
||||
if (entry == null) return;
|
||||
// Mark as empty
|
||||
if (entry == null || entry.m_ID == 0) return;
|
||||
|
||||
// Best-effort unlink from sibling chain
|
||||
if (entry.m_ParentIndex != 0xff)
|
||||
{
|
||||
if (entry.m_PrevSblIndex != 0xff && m_TaskEntries[entry.m_PrevSblIndex] != null)
|
||||
m_TaskEntries[entry.m_PrevSblIndex].m_NextSblIndex = entry.m_NextSblIndex;
|
||||
else if (m_TaskEntries[entry.m_ParentIndex] != null)
|
||||
m_TaskEntries[entry.m_ParentIndex].m_ChildIndex = entry.m_NextSblIndex;
|
||||
|
||||
if (entry.m_NextSblIndex != 0xff && m_TaskEntries[entry.m_NextSblIndex] != null)
|
||||
m_TaskEntries[entry.m_NextSblIndex].m_PrevSblIndex = entry.m_PrevSblIndex;
|
||||
}
|
||||
|
||||
// Mark empty + decrement count, then realign from this slot.
|
||||
entry.m_ulTemplAddr = 0;
|
||||
entry.m_ID = 0;
|
||||
|
||||
// Compact list (RealignTask is our compaction implementation)
|
||||
if (m_uTaskCount > 0) m_uTaskCount--;
|
||||
RealignTask(entry, 0);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -698,8 +698,8 @@ namespace BrewMonster.Scripts.Task
|
||||
break;
|
||||
case TaskTemplConstants.TASK_SVR_NOTIFY_GIVE_UP:
|
||||
pLst = pTask.GetActiveTaskList();
|
||||
// Use simplified removal to keep list consistent in the managed port
|
||||
pLst.RemoveEntry(pEntry);
|
||||
// Match C++ behavior: ClearTask (and realign), do NOT globally compact or just zero the entry.
|
||||
pLst.ClearTask(pTask, pEntry, false);
|
||||
|
||||
// TODO: Log task give up
|
||||
// if (m_bDisplayInTitleTaskUI) TaskInterface::UpdateTitleUI(m_ID);
|
||||
@@ -1704,6 +1704,20 @@ namespace BrewMonster.Scripts.Task
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// 通过子任务序号获取子任务模板(0基) // English: Get child template by index (0-based)
|
||||
public ATaskTempl GetSubByIndex(byte index)
|
||||
{
|
||||
ATaskTempl child = m_pFirstChild;
|
||||
byte i = 0;
|
||||
while (child != null)
|
||||
{
|
||||
if (i == index) return child;
|
||||
i++;
|
||||
child = child.m_pNextSibling;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public ActiveTaskEntry DeliverTask(
|
||||
TaskInterface pTask,
|
||||
@@ -1716,266 +1730,149 @@ namespace BrewMonster.Scripts.Task
|
||||
ref task_sub_tags pSubTag,
|
||||
TaskGlobalData pGlobal,
|
||||
byte uParentIndex)
|
||||
{
|
||||
if (pTask == null || pList == null) return null;
|
||||
|
||||
// Avoid duplicates (server shouldn't send duplicates, but protects managed UI)
|
||||
var existed = pList.GetEntry(m_FixedData.m_ID);
|
||||
if (existed != null && existed.m_ID != 0) return existed;
|
||||
|
||||
int startIndex = (pEntry == null)
|
||||
? pList.m_uTaskCount
|
||||
: Array.IndexOf(pList.m_TaskEntries, pEntry);
|
||||
if (startIndex < 0) startIndex = pList.m_uTaskCount;
|
||||
|
||||
uint maskRef = ulMask;
|
||||
DeliverTask_Internal(
|
||||
pTask, pList, startIndex, ulCaptainTask, ref maskRef, ulCurTime,
|
||||
pSubTempl, ref pSubTag, pGlobal, uParentIndex);
|
||||
|
||||
return (startIndex >= 0 && startIndex < TaskInterfaceConstants.TASK_ACTIVE_LIST_MAX_LEN)
|
||||
? pList.m_TaskEntries[startIndex]
|
||||
: null;
|
||||
}
|
||||
|
||||
// C++ parity helper: returns the next free slot index (like returning `pEntry` pointer).
|
||||
private int DeliverTask_Internal(
|
||||
TaskInterface pTask,
|
||||
ActiveTaskList pList,
|
||||
int entryIndex,
|
||||
uint ulCaptainTask,
|
||||
ref uint ulMask,
|
||||
uint ulCurTime,
|
||||
ATaskTempl pSubTempl,
|
||||
ref task_sub_tags pSubTag,
|
||||
TaskGlobalData pGlobal,
|
||||
byte uParentIndex)
|
||||
{
|
||||
if (entryIndex < 0 || entryIndex >= TaskInterfaceConstants.TASK_ACTIVE_LIST_MAX_LEN)
|
||||
return entryIndex;
|
||||
|
||||
// If the slot is occupied, reserve 1 empty slot here (C++: RealignTask(pEntry, 1))
|
||||
if (pList.m_TaskEntries[entryIndex] != null && pList.m_TaskEntries[entryIndex].m_ID != 0)
|
||||
pList.RealignTaskAtIndex(entryIndex, 1);
|
||||
|
||||
var entry = pList.m_TaskEntries[entryIndex] ?? new ActiveTaskEntry();
|
||||
pList.m_TaskEntries[entryIndex] = entry;
|
||||
byte uIndex = (byte)entryIndex;
|
||||
|
||||
entry.m_ID = (ushort)m_FixedData.m_ID;
|
||||
entry.m_ulTemplAddr = m_FixedData.m_ID; // managed marker (resolve by id)
|
||||
entry.m_ParentIndex = (char)uParentIndex;
|
||||
entry.m_PrevSblIndex = (char)0xff;
|
||||
entry.m_NextSblIndex = (char)0xff;
|
||||
entry.m_ChildIndex = (char)0xff;
|
||||
entry.m_uState = (char)0;
|
||||
entry.m_ulTaskTime = ulCurTime;
|
||||
|
||||
if (ulCaptainTask != 0)
|
||||
{
|
||||
// 最小实现:保证接任务/放弃任务会真实改变 ActiveTaskList,从而驱动任务面板刷新
|
||||
// English: Minimal implementation: make NEW/GIVEUP actually mutate ActiveTaskList so UI can refresh.
|
||||
if (pTask == null || pList == null) return null;
|
||||
|
||||
// Already has task
|
||||
var existed = pList.GetEntry(m_FixedData.m_ID);
|
||||
if (existed != null && existed.m_ID != 0) return existed;
|
||||
|
||||
if (pList.m_uTaskCount >= TaskInterfaceConstants.TASK_ACTIVE_LIST_MAX_LEN) return null;
|
||||
|
||||
int insertIndex = pList.m_uTaskCount;
|
||||
ActiveTaskEntry entry = pEntry ?? new ActiveTaskEntry();
|
||||
|
||||
// Fill fixed data (top-level by default)
|
||||
entry.m_ID = (ushort)m_FixedData.m_ID;
|
||||
entry.m_ulTemplAddr = m_FixedData.m_ID; // non-zero marker; template is resolved by ID in GetTempl()
|
||||
entry.m_ulTaskTime = ulCurTime;
|
||||
|
||||
entry.m_ParentIndex = (char)0xff;
|
||||
entry.m_PrevSblIndex = (char)0xff;
|
||||
entry.m_NextSblIndex = (char)0xff;
|
||||
entry.m_ChildIndex = (char)0xff;
|
||||
|
||||
entry.m_uState = (char)0;
|
||||
entry.SetSuccess(); // default success, may be cleared by later checks
|
||||
|
||||
if (ulCaptainTask != 0)
|
||||
{
|
||||
entry.m_uCapTaskId = (ushort)ulCaptainTask;
|
||||
entry.m_ulCapTemplAddr = ulCaptainTask; // marker only
|
||||
}
|
||||
else
|
||||
{
|
||||
entry.m_uCapTaskId = 0;
|
||||
entry.m_ulCapTemplAddr = 0;
|
||||
}
|
||||
|
||||
// Clear union buffer
|
||||
if (entry.m_BufData != null)
|
||||
Array.Clear(entry.m_BufData, 0, entry.m_BufData.Length);
|
||||
|
||||
// Insert and update list count
|
||||
pList.m_TaskEntries[insertIndex] = entry;
|
||||
pList.m_uTaskCount = (byte)(insertIndex + 1);
|
||||
|
||||
// Recount for UI and budget checks
|
||||
pList.RecountTaskCounters();
|
||||
return entry;
|
||||
|
||||
/*ActiveTaskEntry* aEntries = pList->m_TaskEntries;
|
||||
if (!pEntry) pEntry = aEntries + pList->m_uTaskCount;
|
||||
else if (pEntry->m_ID != 0) // entry��ռ�ã���Ҫ���Ų��һ����λ;
|
||||
pList->RealignTask(pEntry, 1);
|
||||
|
||||
unsigned char uIndex = static_cast<unsigned char>(pEntry - aEntries);
|
||||
|
||||
pEntry->m_ID = static_cast<unsigned short>(m_ID);
|
||||
pEntry->m_ulTemplAddr = reinterpret_cast<unsigned long>(this);
|
||||
pEntry->m_ParentIndex = uParentIndex;
|
||||
pEntry->m_PrevSblIndex = 0xff;
|
||||
pEntry->m_NextSblIndex = 0xff;
|
||||
pEntry->m_ChildIndex = 0xff;
|
||||
pEntry->m_uState = 0;
|
||||
pEntry->m_ulTaskTime = ulCurTime;
|
||||
#ifndef _TASK_CLIENT
|
||||
// ��ΪPQ������Ҫ��ʱ�����¼��m_ulTaskTime��
|
||||
if (m_bPQTask)
|
||||
{
|
||||
pEntry->m_ulTaskTime = PublicQuestInterface::GetCurTaskStamp(m_ID);
|
||||
}
|
||||
#endif
|
||||
if (ulCaptainTask)
|
||||
{
|
||||
pEntry->m_ulCapTemplAddr = reinterpret_cast<unsigned long>(GetTaskTemplMan()->GetTopTaskByID(ulCaptainTask));
|
||||
if (pEntry->m_ulCapTemplAddr) pEntry->m_uCapTaskId = static_cast<unsigned short>(ulCaptainTask);
|
||||
else
|
||||
{
|
||||
pEntry->m_uCapTaskId = 0;
|
||||
|
||||
char log[1024];
|
||||
sprintf(log, "DeliverTask, Cant Find CapTask = %d", ulCaptainTask);
|
||||
TaskInterface::WriteLog(pTask->GetPlayerId(), m_ID, 0, log);
|
||||
}
|
||||
entry.m_uCapTaskId = (ushort)ulCaptainTask;
|
||||
entry.m_ulCapTemplAddr = ulCaptainTask; // managed marker
|
||||
}
|
||||
else
|
||||
{
|
||||
pEntry->m_uCapTaskId = 0;
|
||||
pEntry->m_ulCapTemplAddr = 0;
|
||||
entry.m_uCapTaskId = 0;
|
||||
entry.m_ulCapTemplAddr = 0;
|
||||
}
|
||||
|
||||
pEntry->SetSuccess(); // Later will check whether truly succeed
|
||||
memset(pEntry->m_BufData, 0, sizeof(pEntry->m_BufData));
|
||||
#ifndef _TASK_CLIENT
|
||||
entry.SetSuccess(); // Later will check whether truly succeed
|
||||
if (entry.m_BufData != null) Array.Clear(entry.m_BufData, 0, entry.m_BufData.Length);
|
||||
|
||||
// ��ΪPQ�����PQ��������Ҫ��ʱ�����¼��m_ulTaskTime��
|
||||
if (m_bPQTask)
|
||||
// C++ always increments here; deletion paths decrement before reusing slots.
|
||||
if (pList.m_uTaskCount < byte.MaxValue) pList.m_uTaskCount++;
|
||||
|
||||
// Root-node counters (client-side only)
|
||||
if (m_pParent == null)
|
||||
{
|
||||
ulCurTime = PublicQuestInterface::GetCurTaskStamp(m_ID);
|
||||
}
|
||||
|
||||
else if (m_enumMethod == enumTMReachTreasureZone)
|
||||
{
|
||||
// ����ر�λ�úͲر�ͼ���½�λ�õ�index
|
||||
unsigned short uZonesSum = m_ucZonesNumX * m_ucZonesNumZ;
|
||||
unsigned short uTreasureLocIndex = pTask->RandNormal(1,uZonesSum);
|
||||
char sTreasureLocMinX = (uTreasureLocIndex % m_ucZonesNumX - 1);
|
||||
char sTreasureLocMinZ = (uTreasureLocIndex / m_ucZonesNumX);
|
||||
// ������������IJر�ͼλ�ã�ʹ�ر�λ�ò��ܴ��ڲر�ͼ�ı�Ե
|
||||
int sMapMinX = pTask->RandNormal(sTreasureLocMinX,sTreasureLocMinX + TASK_TREASURE_MAP_SIDE_MULTIPLE - 3) - TASK_TREASURE_MAP_SIDE_MULTIPLE + 2;
|
||||
int sMapMinZ = pTask->RandNormal(sTreasureLocMinZ,sTreasureLocMinZ + TASK_TREASURE_MAP_SIDE_MULTIPLE - 3) - TASK_TREASURE_MAP_SIDE_MULTIPLE + 2;
|
||||
// �洢���ѽ������б���
|
||||
pEntry->m_iUsefulData1 = uTreasureLocIndex;
|
||||
pEntry->m_iUsefulData1 |= (sMapMinX << 16) & 0x00FF0000;
|
||||
pEntry->m_iUsefulData1 |= (sMapMinZ << 24) & 0xFF000000;
|
||||
}
|
||||
|
||||
ulMask |= m_ulMask;
|
||||
|
||||
#else
|
||||
if (m_enumMethod == enumTMReachTreasureZone)
|
||||
{
|
||||
task_notify_base notify;
|
||||
notify.reason = TASK_CLT_NOTIFY_REQUEST_TREASURE_INDEX;
|
||||
notify.task = static_cast<unsigned short>(m_ID);
|
||||
pTask->NotifyServer(¬ify, sizeof(notify));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
pList->m_uTaskCount++;
|
||||
|
||||
if (!m_pParent)
|
||||
{
|
||||
if (m_bHidden) pList->m_uTopHideTaskCount++;
|
||||
else if (m_bDisplayInTitleTaskUI) pList->m_uTitleTaskCount++;
|
||||
else pList->m_uTopShowTaskCount++;
|
||||
pList->m_uUsedCount += m_uDepth;
|
||||
|
||||
#ifndef _TASK_CLIENT
|
||||
|
||||
if (pGlobal)
|
||||
{
|
||||
pGlobal->AddRevNum();
|
||||
pGlobal->m_ulRcvUpdateTime = ulCurTime;
|
||||
TaskUpdateGlobalData(m_ID, pGlobal->buf);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (m_FixedData.m_bHidden) pList.m_uTopHideTaskCount++;
|
||||
else if (m_FixedData.m_bDisplayInTitleTaskUI) pList.m_uTitleTaskCount++;
|
||||
else pList.m_uTopShowTaskCount++;
|
||||
|
||||
int used = pList.m_uUsedCount + m_uDepth;
|
||||
pList.m_uUsedCount = (byte)Math.Clamp(used, 0, byte.MaxValue);
|
||||
}
|
||||
|
||||
// Attach to parent (append to end of child sibling chain)
|
||||
if (uParentIndex != 0xff)
|
||||
{
|
||||
ActiveTaskEntry& ParentEntry = aEntries[uParentIndex];
|
||||
if (ParentEntry.m_ChildIndex == 0xff) ParentEntry.m_ChildIndex = uIndex;
|
||||
else
|
||||
var parent = pList.m_TaskEntries[uParentIndex];
|
||||
if (parent != null)
|
||||
{
|
||||
unsigned char uChildEntry = ParentEntry.m_ChildIndex;
|
||||
while (aEntries[uChildEntry].m_NextSblIndex != 0xff)
|
||||
uChildEntry = aEntries[uChildEntry].m_NextSblIndex;
|
||||
aEntries[uChildEntry].m_NextSblIndex = uIndex;
|
||||
pEntry->m_PrevSblIndex = uChildEntry;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef _TASK_CLIENT
|
||||
|
||||
if (!m_pParent)
|
||||
DeliverGivenItems(pTask);
|
||||
|
||||
#endif
|
||||
|
||||
pEntry++;
|
||||
|
||||
if (pSubTempl)
|
||||
{
|
||||
return pSubTempl->DeliverTask(
|
||||
pTask,
|
||||
pList,
|
||||
pEntry,
|
||||
0,
|
||||
ulMask,
|
||||
ulCurTime,
|
||||
NULL,
|
||||
pSubTag,
|
||||
NULL,
|
||||
uIndex);
|
||||
}
|
||||
else if (m_bRandOne)
|
||||
{
|
||||
#ifdef _TASK_CLIENT
|
||||
if (pSubTag->cur_index < pSubTag->sz)
|
||||
{
|
||||
pSubTempl = GetSubByIndex(pSubTag->tags[pSubTag->cur_index]);
|
||||
pSubTag->cur_index++;
|
||||
|
||||
if (pSubTempl)
|
||||
if (parent.m_ChildIndex == (char)0xff)
|
||||
parent.m_ChildIndex = (char)uIndex;
|
||||
else
|
||||
{
|
||||
return pSubTempl->DeliverTask(
|
||||
pTask,
|
||||
pList,
|
||||
pEntry,
|
||||
0,
|
||||
ulMask,
|
||||
ulCurTime,
|
||||
NULL,
|
||||
pSubTag,
|
||||
NULL,
|
||||
uIndex);
|
||||
int uChildEntry = (byte)parent.m_ChildIndex;
|
||||
while (pList.m_TaskEntries[uChildEntry] != null && pList.m_TaskEntries[uChildEntry].m_NextSblIndex != 0xff)
|
||||
uChildEntry = (byte)pList.m_TaskEntries[uChildEntry].m_NextSblIndex;
|
||||
|
||||
if (pList.m_TaskEntries[uChildEntry] != null)
|
||||
{
|
||||
pList.m_TaskEntries[uChildEntry].m_NextSblIndex = (char)uIndex;
|
||||
entry.m_PrevSblIndex = (char)uChildEntry;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
int nSel;
|
||||
pSubTempl = RandOneChild(pTask, nSel);
|
||||
|
||||
if (pSubTempl)
|
||||
{
|
||||
if (pSubTag->sz < MAX_SUB_TAGS)
|
||||
pSubTag->tags[pSubTag->sz++] = static_cast<unsigned char>(nSel);
|
||||
|
||||
return pSubTempl->DeliverTask(
|
||||
pTask,
|
||||
pList,
|
||||
pEntry,
|
||||
0,
|
||||
ulMask,
|
||||
ulCurTime,
|
||||
NULL,
|
||||
pSubTag,
|
||||
NULL,
|
||||
uIndex);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int nextIndex = entryIndex + 1;
|
||||
|
||||
// Explicit server-specified sub-task comes first (C++ pSubTempl branch)
|
||||
if (pSubTempl != null)
|
||||
{
|
||||
return pSubTempl.DeliverTask_Internal(
|
||||
pTask, pList, nextIndex, 0, ref ulMask, ulCurTime, null, ref pSubTag, pGlobal, uIndex);
|
||||
}
|
||||
// Rand-one: on client, follow preselected tag path one step at a time
|
||||
else if (m_FixedData.m_bRandOne)
|
||||
{
|
||||
if (pSubTag.cur_index < pSubTag.sz)
|
||||
{
|
||||
byte sel = pSubTag.tags[pSubTag.cur_index++];
|
||||
var chosen = GetSubByIndex(sel);
|
||||
if (chosen != null)
|
||||
{
|
||||
return chosen.DeliverTask_Internal(
|
||||
pTask, pList, nextIndex, 0, ref ulMask, ulCurTime, null, ref pSubTag, pGlobal, uIndex);
|
||||
}
|
||||
}
|
||||
return nextIndex;
|
||||
}
|
||||
// Normal: deliver children (all, or first if execute-in-order)
|
||||
else
|
||||
{
|
||||
const ATaskTempl* pChild = m_pFirstChild;
|
||||
|
||||
while (pChild)
|
||||
var child = m_pFirstChild;
|
||||
while (child != null)
|
||||
{
|
||||
pEntry = pChild->DeliverTask(
|
||||
pTask,
|
||||
pList,
|
||||
pEntry,
|
||||
0,
|
||||
ulMask,
|
||||
ulCurTime,
|
||||
NULL,
|
||||
pSubTag,
|
||||
NULL,
|
||||
uIndex);
|
||||
nextIndex = child.DeliverTask_Internal(
|
||||
pTask, pList, nextIndex, 0, ref ulMask, ulCurTime, null, ref pSubTag, pGlobal, uIndex);
|
||||
|
||||
if (m_bExeChildInOrder) return pEntry;
|
||||
pChild = pChild->m_pNextSibling;
|
||||
if (m_FixedData.m_bExeChildInOrder) return nextIndex;
|
||||
child = child.m_pNextSibling;
|
||||
}
|
||||
return nextIndex;
|
||||
}
|
||||
|
||||
return pEntry;*/
|
||||
}
|
||||
|
||||
void RecursiveAward(
|
||||
@@ -1986,28 +1883,134 @@ namespace BrewMonster.Scripts.Task
|
||||
int nChoice,
|
||||
ref task_sub_tags pSubTag)
|
||||
{
|
||||
// 最小实现(客户端):
|
||||
// - 记录到 FinishedTaskList(用于前置任务/可重复接判断)
|
||||
// - 从 ActiveTaskList 移除该任务条目,从而驱动任务面板刷新
|
||||
// English minimal client port:
|
||||
// - Record into FinishedTaskList (prerequisites/redo checks)
|
||||
// - Remove entry from ActiveTaskList to refresh UI lists
|
||||
// 客户端侧尽量对齐 C++ RecursiveAward:
|
||||
// - 清除子任务
|
||||
// - 维护父/子/兄弟索引关系
|
||||
// - ExecuteChildInOrder 时复用 slot 投递下一个兄弟任务
|
||||
// English: Client-side parity with C++ RecursiveAward:
|
||||
// - Clear children
|
||||
// - Maintain parent/child/sibling indices
|
||||
// - If execute-children-in-order, reuse the freed slot to deliver the next sibling task
|
||||
if (pTask == null || pList == null || pEntry == null) return;
|
||||
|
||||
|
||||
int entryIndex = Array.IndexOf(pList.m_TaskEntries, pEntry);
|
||||
if (entryIndex < 0) return;
|
||||
|
||||
// 失败时不收取物品的特殊逻辑(仅影响清子任务时是否移除物品)
|
||||
// English: If failed and flagged, don't remove items when clearing children.
|
||||
bool bFailedTaskDoNotTakeItem = !pEntry.IsSuccess() && m_FixedData.m_bNotClearItemWhenFailed && m_FixedData.m_bClearAcquired;
|
||||
|
||||
// Clear children first (C++: pList->ClearChildrenOf(pTask, pEntry, !bFailedTaskDoNotTakeItem))
|
||||
pList.ClearChildrenOf(pTask, pEntry, !bFailedTaskDoNotTakeItem);
|
||||
if (pEntry.m_ulTemplAddr == 0) return; // must check it (matches C++)
|
||||
|
||||
bool success = pEntry.IsSuccess() && !pEntry.IsGiveUp();
|
||||
|
||||
|
||||
// Only record on top-level templates (same as C++: !m_pParent && m_bNeedRecord)
|
||||
if (m_pParent == null && m_FixedData.m_bNeedRecord)
|
||||
{
|
||||
if (pTask is CECTaskInterface cec)
|
||||
{
|
||||
cec.RecordFinishedTask(m_FixedData.m_ID, success);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove from active list (C++ RecursiveAward clears children + removes current entry)
|
||||
pList.RemoveEntry(pEntry);
|
||||
return;
|
||||
|
||||
// Mark empty + decrement count (C++ does this before realign / reuse)
|
||||
pEntry.m_ulTemplAddr = 0;
|
||||
pEntry.m_ID = 0;
|
||||
if (pList.m_uTaskCount > 0) pList.m_uTaskCount--;
|
||||
|
||||
// If has parent, unlink from sibling chain and handle parent propagation
|
||||
if (pEntry.m_ParentIndex != 0xff)
|
||||
{
|
||||
int parentIndex = (byte)pEntry.m_ParentIndex;
|
||||
if (parentIndex >= 0 && parentIndex < TaskInterfaceConstants.TASK_ACTIVE_LIST_MAX_LEN)
|
||||
{
|
||||
var parentEntry = pList.m_TaskEntries[parentIndex];
|
||||
if (parentEntry != null)
|
||||
{
|
||||
// unlink siblings (matches C++)
|
||||
if (pEntry.m_PrevSblIndex != 0xff)
|
||||
{
|
||||
var prev = pList.m_TaskEntries[(byte)pEntry.m_PrevSblIndex];
|
||||
if (prev != null) prev.m_NextSblIndex = pEntry.m_NextSblIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
parentEntry.m_ChildIndex = pEntry.m_NextSblIndex;
|
||||
}
|
||||
|
||||
if (pEntry.m_NextSblIndex != 0xff)
|
||||
{
|
||||
var next = pList.m_TaskEntries[(byte)pEntry.m_NextSblIndex];
|
||||
if (next != null) next.m_PrevSblIndex = pEntry.m_PrevSblIndex;
|
||||
}
|
||||
|
||||
// ParentAlsoFail
|
||||
if (!pEntry.IsSuccess() && m_FixedData.m_bParentAlsoFail && m_pParent != null)
|
||||
{
|
||||
pList.RealignTaskAtIndex(entryIndex, 0);
|
||||
parentEntry.ClearSuccess();
|
||||
parentEntry.SetFinished();
|
||||
m_pParent.RecursiveAward(pTask, pList, parentEntry, ulCurtime, -1, ref pSubTag);
|
||||
return;
|
||||
}
|
||||
// ParentAlsoSuccess
|
||||
else if (pEntry.IsSuccess() && m_FixedData.m_bParentAlsoSucc && m_pParent != null)
|
||||
{
|
||||
pList.RealignTaskAtIndex(entryIndex, 0);
|
||||
parentEntry.SetFinished();
|
||||
pList.ClearChildrenOf(pTask, parentEntry, true);
|
||||
if (m_pParent.m_FixedData.m_enumFinishType == (uint)TaskFinishType.enumTFTDirect)
|
||||
m_pParent.RecursiveAward(pTask, pList, parentEntry, ulCurtime, -1, ref pSubTag);
|
||||
return;
|
||||
}
|
||||
// Execute children in order: deliver next sibling into the freed slot (C++ reuse pEntry)
|
||||
else if (m_pParent != null && m_pParent.m_FixedData.m_bExeChildInOrder && m_pNextSibling != null)
|
||||
{
|
||||
// if parent still has children OR next sibling already exists, just realign
|
||||
if (parentEntry.m_ChildIndex != 0xff || pList.GetEntry(m_pNextSibling.m_FixedData.m_ID) != null)
|
||||
{
|
||||
pList.RealignTaskAtIndex(entryIndex, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// reuse current slot object; keep it in array at entryIndex
|
||||
m_pNextSibling.DeliverTask(
|
||||
pTask,
|
||||
pList,
|
||||
pEntry,
|
||||
0,
|
||||
pTask.GetTaskMask(),
|
||||
ulCurtime,
|
||||
null,
|
||||
ref pSubTag,
|
||||
new TaskGlobalData(),
|
||||
(byte)pEntry.m_ParentIndex);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// If this was the last child, mark parent finished
|
||||
else if (parentEntry.m_ChildIndex == 0xff && m_pParent != null)
|
||||
{
|
||||
pList.RealignTaskAtIndex(entryIndex, 0);
|
||||
parentEntry.SetFinished();
|
||||
if (m_pParent.m_FixedData.m_enumFinishType == (uint)TaskFinishType.enumTFTDirect)
|
||||
m_pParent.RecursiveAward(pTask, pList, parentEntry, ulCurtime, -1, ref pSubTag);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Default: just realign this freed slot away
|
||||
pList.RealignTaskAtIndex(entryIndex, 0);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Root node: realign and adjust counts similar to C++ (counters will also be recomputed by UI paths)
|
||||
pList.RealignTaskAtIndex(entryIndex, 0);
|
||||
pList.RecountTaskCounters();
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO : implement full logic when ActiveTaskList/ActiveTaskEntry and TaskInterface APIs are available
|
||||
/*{
|
||||
|
||||
@@ -137,12 +137,12 @@ namespace BrewMonster.Scripts.Task.UI
|
||||
|
||||
#region Unity METHODS
|
||||
|
||||
private void OnEnable()
|
||||
private new void OnEnable()
|
||||
{
|
||||
OnShowDialog();
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
private new void Awake()
|
||||
{
|
||||
EventBus.Subscribe<TaskItemClickEvent>(evt =>
|
||||
{
|
||||
@@ -770,7 +770,7 @@ namespace BrewMonster.Scripts.Task.UI
|
||||
return default(T);
|
||||
}
|
||||
|
||||
public CECHostPlayer GetHostPlayer()
|
||||
public new CECHostPlayer GetHostPlayer()
|
||||
{
|
||||
if(EC_Game.GetGameRun() == null)
|
||||
{
|
||||
@@ -828,6 +828,52 @@ namespace BrewMonster.Scripts.Task.UI
|
||||
child = child.m_pNextSibling;
|
||||
}
|
||||
}
|
||||
|
||||
// [中文] 仅插入“已接任务(Active)”中的子任务(基于 ActiveTaskList 的 Child/NextSbl 索引)
|
||||
// [English] Insert only active subtasks (based on ActiveTaskList Child/NextSbl indices)
|
||||
private void InsertActiveTaskChildren(TaskTreeViewItem pRoot, uint idTask)
|
||||
{
|
||||
var pTreeTask = m_pTv_Quest;
|
||||
var pMan = EC_Game.GetTaskTemplateMan();
|
||||
var pTask = GetHostPlayer()?.GetTaskInterface();
|
||||
if (pTreeTask == null || pMan == null || pTask == null) return;
|
||||
|
||||
ActiveTaskList pList = pTask.GetActiveTaskList();
|
||||
if (pList == null) return;
|
||||
|
||||
ActiveTaskEntry parentEntry = pList.GetEntry(idTask);
|
||||
if (parentEntry == null) return;
|
||||
|
||||
char idx = parentEntry.m_ChildIndex;
|
||||
while (idx != (char)0xff)
|
||||
{
|
||||
int childIndex = (byte)idx;
|
||||
if (childIndex < 0 || childIndex >= TaskInterfaceConstants.TASK_ACTIVE_LIST_MAX_LEN) break;
|
||||
|
||||
ActiveTaskEntry childEntry = pList.m_TaskEntries[childIndex];
|
||||
if (childEntry == null || childEntry.m_ID == 0) break;
|
||||
|
||||
uint childId = childEntry.m_ID;
|
||||
ATaskTempl childTempl = pMan.GetTaskTemplByID(childId);
|
||||
string text = childTempl != null ? GetTaskNameWithColor(childTempl) : $"Task {childId}";
|
||||
|
||||
var pItem = pTreeTask.InsertItem(text, pRoot, null);
|
||||
if (pItem != null)
|
||||
{
|
||||
pTreeTask.SetItemData(pItem, childId);
|
||||
if ((int)childId == m_idSelTask)
|
||||
{
|
||||
if (m_pBtn_Abandon != null) m_pBtn_Abandon.interactable = true;
|
||||
UpdateTask((int)childId);
|
||||
}
|
||||
|
||||
// recurse into active children
|
||||
InsertActiveTaskChildren(pItem, childId);
|
||||
}
|
||||
|
||||
idx = childEntry.m_NextSblIndex;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetTextItemText(string strNewTextItem, bool keepScrollPos, string strNewHintItem)
|
||||
{
|
||||
@@ -1469,7 +1515,11 @@ namespace BrewMonster.Scripts.Task.UI
|
||||
pItem.SetItemTextColor(GetTaskColor((int)ENUM_TASK_TYPE.enumTTLevel2));
|
||||
// pTreeTask.SetItemHint(pItem, pTemp->GetSignature()); // TODO
|
||||
pTreeTask.SetItemData(pItem, (uint)id);
|
||||
InsertTaskChildren(pItem, (uint)id, true, pTemp.IsKeyTask());
|
||||
// HaveQuest view: children should reflect ActiveTaskList, not template tree (otherwise they never disappear on completion)
|
||||
if (m_iType == 0)
|
||||
InsertActiveTaskChildren(pItem, (uint)id);
|
||||
else
|
||||
InsertTaskChildren(pItem, (uint)id, true, pTemp.IsKeyTask());
|
||||
|
||||
if( (int)id == m_idSelTask )
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user