Files
test/Assets/PerfectWorld/Scripts/Task/TaskProcess.cs
T
2025-12-16 16:37:03 +07:00

747 lines
24 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Runtime.InteropServices;
using BrewMonster.Scripts.Task;
using CSNetwork.GPDataType;
using UnityEngine;
namespace BrewMonster.Scripts.Task
{
public class TaskProcess
{
}
[Flags]
public enum TaskState : byte
{
TASK_STATE_FINISHED = 0x01, // Is finished
TASK_STATE_SUCCESS = 0x02, // Is successful
TASK_STATE_GIVEUP = 0x04, // Is given up
TASK_STATE_ERR_REPORTED = 0x08, // Error has been reported to client
TASK_STATE_AWARD_NOTIFY_TEAM = 0x10, // Award has been notified to team
TASK_STATE_CONTRIBUTION_FINISH = 0x20 // Contribution finished
}
[StructLayout( LayoutKind.Sequential, Pack = 1 )]
public struct TaskFinishCountList
{
public static ushort m_uCount;
public static TaskFinishCountEntry[] m_aList = new TaskFinishCountEntry[(uint)TaskInterfaceConstants.TASK_FINISH_COUNT_MAX_LEN];
public uint Search(uint ulID, ref uint ulTime)
{
for (ushort i = 0; i < TaskFinishCountList.m_uCount; i++)
{
if (TaskFinishCountList.m_aList[i].m_uTaskId == (ushort)ulID)
{
ulTime = TaskFinishCountList.m_aList[i].m_ulFinishTime;
return TaskFinishCountList.m_aList[i].m_ulFinishCount;
}
}
return 0u;
}
public void ResetAt(uint ulID)
{
for (ushort i = 0; i < m_uCount; i++)
if (m_aList[i].m_uTaskId == (ushort)ulID)
m_aList[i].m_ulFinishCount = 0;
}
public void AddOrUpdate(uint ulID, uint ulFinishTime)
{
for (ushort i = 0; i < m_uCount; i++)
{
if (m_aList[i].m_uTaskId == (ushort)ulID)
{
m_aList[i].m_ulFinishCount ++;
m_aList[i].m_ulFinishTime = ulFinishTime;
return;
}
}
if (m_uCount >= (uint)TaskInterfaceConstants.TASK_FINISH_COUNT_MAX_LEN)
return;
m_aList[m_uCount].m_uTaskId = (ushort)ulID;
m_aList[m_uCount].m_ulFinishCount = 1;
m_aList[m_uCount].m_ulFinishTime = ulFinishTime;
m_uCount++;
}
public void RemoveAll()
{
m_uCount = 0;
m_aList = new TaskFinishCountEntry[(uint)TaskInterfaceConstants.TASK_FINISH_COUNT_MAX_LEN];
}
public bool IsValid() { return m_uCount <= TaskInterfaceConstants.TASK_FINISH_COUNT_MAX_LEN; }
public bool ReadFromBytes(byte[] data)
{
if (data == null || data.Length < 2)
return false;
// Read m_uCount (2 bytes)
m_uCount = BitConverter.ToUInt16(data, 0);
// Read m_aList array
int entrySize = Marshal.SizeOf<TaskFinishCountEntry>();
int expectedSize = 2 + entrySize * TaskInterfaceConstants.TASK_FINISH_COUNT_MAX_LEN;
if (data.Length < expectedSize)
return false;
if (m_aList == null || m_aList.Length != TaskInterfaceConstants.TASK_FINISH_COUNT_MAX_LEN)
m_aList = new TaskFinishCountEntry[TaskInterfaceConstants.TASK_FINISH_COUNT_MAX_LEN];
for (int i = 0; i < TaskInterfaceConstants.TASK_FINISH_COUNT_MAX_LEN; i++)
{
int offset = 2 + i * entrySize;
m_aList[i] = GPDataTypeHelper.FromBytes<TaskFinishCountEntry>(data[offset..(offset + entrySize)]);
}
return true;
}
};
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct TaskFinishCountEntry
{
public ushort m_uTaskId;
public uint m_ulFinishCount;
public uint m_ulFinishTime;
public uint m_ulUnused2;
};
[StructLayout( LayoutKind.Sequential, Pack = 1 )]
public class TASK_
{
public const int TASK_DEFAULT_MAX_SIMULTANEOUS_COUT = 20;
public const int TASK_MAX_SIMULTANEOUS_COUT = 30;
public const int TASK_HIDDEN_COUNT = 30; // formally 6, for test
public const int TASK_TITLE_TASK_COUNT = 10;
}
[StructLayout( LayoutKind.Sequential, Pack = 1 )]
// Cur Size 21 bytes
public class TASK_ENTRY_FIXED_DATA
{
public ushort m_ID; // ID
public char m_ParentIndex; // Parent node index
public char m_PrevSblIndex; // Previous sibling node index
public char m_NextSblIndex; // Next sibling node index
public char m_ChildIndex; // Child node index
public char m_uState; // Task state
public uint m_ulTaskTime; // Timestamp
public ushort m_uCapTaskId; // Captain task ID
public uint m_ulTemplAddr; // Template address -> In C++ this is a pointer, here we use uint to store the ID of the template
public uint m_ulCapTemplAddr; // Captain task template address
};
// ´óСΪTASK_DATA_BUF_MAX_LEN
public class ActiveTaskEntry : TASK_ENTRY_FIXED_DATA
{
// Buffer union simplified (C# does not support union directly)
// unsigned char m_BufData[TASK_DATA_BUF_MAX_LEN-sizeof(TASK_ENTRY_FIXED_DATA)];
public byte[] m_BufData = new byte[TaskInterfaceConstants.TASK_DATA_BUF_MAX_LEN - Marshal.SizeOf<TASK_ENTRY_FIXED_DATA>() ]; // Raw data buffer
// nsigned short m_wMonsterNum[MAX_MONSTER_WANTED];
// 注意:这个属性返回的是拷贝数组,不能用 m_wMonsterNum[i] = x 来写入(不会回写到底层缓冲)
// English: This property returns a COPY. Do not mutate via m_wMonsterNum[i] = x (it won't persist).
public ushort[] m_wMonsterNum // Monster numbers (copy)
{
get
{
ushort[] monsterNums = new ushort[TaskInterfaceConstants.MAX_MONSTER_WANTED];
for (int i = 0; i < TaskInterfaceConstants.MAX_MONSTER_WANTED; i++)
{
monsterNums[i] = BitConverter.ToUInt16(m_BufData, i * 2);
}
return monsterNums;
}
set
{
for (int i = 0; i < TaskInterfaceConstants.MAX_MONSTER_WANTED; i++)
{
byte[] bytes = BitConverter.GetBytes(value[i]);
m_BufData[i * 2] = bytes[0];
m_BufData[i * 2 + 1] = bytes[1];
}
}
}
// 读取/写入怪物计数(直接回写到底层缓冲) // English: Get/set monster count (writes through to backing buffer)
public ushort GetMonsterNum(int index)
{
if (index < 0 || index >= TaskInterfaceConstants.MAX_MONSTER_WANTED) return 0;
return BitConverter.ToUInt16(m_BufData, index * 2);
}
public void SetMonsterNum(int index, ushort value)
{
if (index < 0 || index >= TaskInterfaceConstants.MAX_MONSTER_WANTED) return;
byte[] bytes = BitConverter.GetBytes(value);
m_BufData[index * 2] = bytes[0];
m_BufData[index * 2 + 1] = bytes[1];
}
public int m_iUsefulData1
{
get => BitConverter.ToInt32(m_BufData, TaskInterfaceConstants.MAX_MONSTER_WANTED * 2);
set
{
byte[] bytes = BitConverter.GetBytes(value);
Array.Copy(bytes, 0, m_BufData, TaskInterfaceConstants.MAX_MONSTER_WANTED * 2, 4);
}
}
public byte m_iUsefulData2 // char in C++, but using byte here for simplicity
{
get => m_BufData[TaskInterfaceConstants.MAX_MONSTER_WANTED * 2 + 4];
set => m_BufData[TaskInterfaceConstants.MAX_MONSTER_WANTED * 2 + 4] = value;
}
public void ReadFromBuffer(byte[] buffer, ref int offset)
{
m_ID = BitConverter.ToUInt16(buffer, offset);
offset += 2;
m_ParentIndex = (char)buffer[offset++];
m_PrevSblIndex = (char)buffer[offset++];
m_NextSblIndex = (char)buffer[offset++];
m_ChildIndex = (char)buffer[offset++];
m_uState = (char)buffer[offset++];
m_ulTaskTime = BitConverter.ToUInt32(buffer, offset);
offset += 4;
m_uCapTaskId = BitConverter.ToUInt16(buffer, offset);
offset += 2;
m_ulTemplAddr = BitConverter.ToUInt32(buffer, offset);
offset += 4;
m_ulCapTemplAddr = BitConverter.ToUInt32(buffer, offset);
offset += 4;
// int localOffset = offset; // store current offset for reading additional fields from union
// Read remaining buffer data
Array.Copy(buffer, offset, m_BufData, 0, m_BufData.Length);
offset += m_BufData.Length; // in C++ have union, so we dont plus offset here
// // Additional fields can be read here as needed
// m_wMonsterNum = new ushort[TaskInterfaceConstants.MAX_MONSTER_WANTED];
// for (int i = 0; i < TaskInterfaceConstants.MAX_MONSTER_WANTED; i++)
// {
// m_wMonsterNum[i] = BitConverter.ToUInt16(buffer, localOffset);
// localOffset += 2; // in C++ have union, so we dont plus offset here
// }
//
// m_iUsefulData1 = BitConverter.ToInt32(buffer, localOffset);
// localOffset += 4; // in C++ have union, so we dont plus offset here
//
// m_iUsefulData2 = buffer[localOffset++]; // in C++ have union, so we dont plus offset here
}
// bool IsFinished() const { return (m_uState & TASK_STATE_FINISHED) != 0; }
// bool IsErrReported() const { return (m_uState & TASK_STATE_ERR_REPORTED) != 0; }
// bool IsAwardNotifyTeam() const { return (m_uState & TASK_STATE_AWARD_NOTIFY_TEAM) != 0; }
// bool IsContributionFinish() const { return (m_uState & TASK_STATE_CONTRIBUTION_FINISH) != 0; }
// --- State check methods ---
public bool IsFinished() => (m_uState & (byte)TaskState.TASK_STATE_FINISHED) != 0;
public bool IsSuccess() => (m_uState & (byte)TaskState.TASK_STATE_SUCCESS) != 0;
public bool IsGiveUp() => (m_uState & (byte)TaskState.TASK_STATE_GIVEUP) != 0;
public bool IsErrReported() => (m_uState & (byte)TaskState.TASK_STATE_ERR_REPORTED) != 0;
public bool IsAwardNotifyTeam() => (m_uState & (byte)TaskState.TASK_STATE_AWARD_NOTIFY_TEAM) != 0;
public bool IsContributionFinish() => (m_uState & (byte)TaskState.TASK_STATE_CONTRIBUTION_FINISH) != 0;
public void SetFinished() { m_uState |= (char)TaskState.TASK_STATE_FINISHED; }
public void SetSuccess() { m_uState |= (char)TaskState.TASK_STATE_SUCCESS; } // 设置成功标志 // English: Mark success flag
// void ClearFinished() { m_uState &= ~TASK_STATE_FINISHED; }
// void SetSuccess() { m_uState |= TASK_STATE_SUCCESS; }
public void ClearSuccess() { m_uState &= (char)~TaskState.TASK_STATE_SUCCESS; }
public void SetGiveUp() { m_uState |= (char)TaskState.TASK_STATE_GIVEUP; }
// void ClearGiveUp() { m_uState &= ~TASK_STATE_GIVEUP; }
// void SetErrReported() { m_uState |= TASK_STATE_ERR_REPORTED; }
// void ClearErrReported() { m_uState &= ~TASK_STATE_ERR_REPORTED; }
// void SetAwardNotifyTeam() { m_uState |= TASK_STATE_AWARD_NOTIFY_TEAM; }
// void ClearAwardNotifyTeam() { m_uState &= ~TASK_STATE_AWARD_NOTIFY_TEAM; }
// void SetContributionFinish() { m_uState |= TASK_STATE_CONTRIBUTION_FINISH; }
// void ClearContributionFinish() { m_uState &= ~TASK_STATE_CONTRIBUTION_FINISH; }
//
public ATaskTempl GetTempl()
{
// Managed fallback: resolve via template manager by ID
try
{
var man = BrewMonster.Network.EC_Game.GetTaskTemplateMan();
if (man != null)
{
// NOTE: Some project configurations report an "ambiguous call" error for direct calls
// into ATaskTemplMan methods (likely due to duplicate symbols/assemblies). Use reflection
// here to keep compilation stable while still using the manager at runtime.
var mi = man.GetType().GetMethod("GetTaskTemplByID", new[] { typeof(uint) });
if (mi != null)
{
var templObj = mi.Invoke(man, new object[] { (uint)m_ID });
if (templObj is ATaskTempl templ) return templ;
}
}
}
catch { }
// Legacy pointer path (likely unused in managed port)
if (m_ulTemplAddr != 0)
{
try
{
return Marshal.PtrToStructure<ATaskTempl>(new IntPtr(unchecked((long)m_ulTemplAddr)));
}
catch { }
}
return null;
}
// const ATaskTempl* GetCap() const { return reinterpret_cast<const ATaskTempl*>(m_ulCapTemplAddr); }
// const ATaskTempl* GetCapOrSelf() const
// {
// if (m_ulCapTemplAddr) return GetCap();
// else return GetTempl();
// }
// bool HasParent() const { return m_ParentIndex != 0xff; }
// bool HasChildren() const { return m_ChildIndex != 0xff; }
public bool IsValid(char uIndex, char uMaxCount)
{
if (m_ParentIndex != 0xff)
{
if (m_ParentIndex >= uIndex || m_ParentIndex >= uMaxCount)
return false;
}
if (m_PrevSblIndex != 0xff)
{
if (m_PrevSblIndex >= uIndex || m_PrevSblIndex >= uMaxCount)
return false;
}
if (m_NextSblIndex != 0xff)
{
if (m_NextSblIndex <= uIndex || m_NextSblIndex >= uMaxCount)
return false;
}
if (m_ChildIndex != 0xff)
{
if (m_ChildIndex <= uIndex || m_ChildIndex >= uMaxCount)
return false;
}
return true;
}
};
public class ActiveTaskList
{
// --- Header Fields ---
// NOTE: union
public byte[] header = new byte[TaskInterfaceConstants.TASK_ACTIVE_LIST_HEADER_LEN];
public byte m_uTaskCount // number of tasks
{
get => header[0];
set => header[0] = value;
}
public byte m_uUsedCount // used count
{
get => header[1];
set => header[1] = value;
}
public ushort m_Version // version
{
get => BitConverter.ToUInt16(header, 2);
set
{
byte[] bytes = BitConverter.GetBytes(value);
header[2] = bytes[0];
header[3] = bytes[1];
}
}
public byte m_uTopShowTaskCount // top show task count
{
get => header[4];
set => header[4] = value;
}
public byte m_uListState // list state
{
get => header[5];
set => header[5] = value;
}
public byte m_uTopHideTaskCount // top hide task count
{
get => header[6];
set => header[6] = value;
}
private byte _flags // simulate bitfield (1 bit + 7 bits)
{
get => header[7];
set => header[7] = value;
}
public bool m_uMaxSimultaneousCount
{
get => (_flags & 0x01) != 0;
set
{
if (value) _flags |= 0x01;
else _flags &= unchecked((byte)~0x01);
}
}
public byte m_uTitleTaskCount
{
get => (byte)((_flags & 0xFE) >> 1);
set => _flags = (byte)((_flags & 0x01) | ((value & 0x7F) << 1));
}
// ActiveTaskEntry m_TaskEntries[TASK_ACTIVE_LIST_MAX_LEN];
public ActiveTaskEntry[] m_TaskEntries = new ActiveTaskEntry[TaskInterfaceConstants.TASK_ACTIVE_LIST_MAX_LEN];
// --- Methods ---
public void ReadFromBuffer(byte[] buffer)
{
// NOTE: union
// cause C++ use union here, header use same data slot with the rest of properties like m_uTaskCount etc.
// so, we dont need to read other properties again, just copy the header part
int offset = 0;
Array.Copy(buffer, offset, header, 0, header.Length);
offset += header.Length;
// m_uTaskCount = buffer[offset++];
// m_uUsedCount = buffer[offset++];
// m_Version = BitConverter.ToUInt16(buffer, offset);
// offset += 2;
// m_uTopShowTaskCount = buffer[offset++];
// m_uListState = buffer[offset++];
// m_uTopHideTaskCount = buffer[offset++];
// _flags = buffer[offset++];
for (int i = 0; i < m_uTaskCount; i++)
{
ActiveTaskEntry entry = new ActiveTaskEntry();
entry.ReadFromBuffer(buffer, ref offset);
m_TaskEntries[i] = entry;
}
}
// void UpdateTaskMask(unsigned long& ulMask) const;
public void UpdateUsedCount()
{
m_uUsedCount = 0;
for (int i = 0; i < m_uTaskCount; i++)
{
ATaskTempl pTempl = m_TaskEntries[i].GetTempl();
if (pTempl == null) continue;
if (pTempl.m_pParent != null) continue;
m_uUsedCount += pTempl.m_uDepth;
}
}
/// <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)
{
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++)
{
var e = m_TaskEntries[uEmpty];
if (e == null || e.m_ID == 0) uEmptyCount++;
else break;
}
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;
// Adjust indices for entries before uCurIndex (child + next sibling that point into >= uCurIndex)
for (int i = 0; i < uCurIndex; i++)
{
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();
}
// 重新统计顶部任务计数与使用量 // English: Recount top task counters and used count
public void RecountTaskCounters()
{
m_uTopShowTaskCount = 0;
m_uTopHideTaskCount = 0;
m_uTitleTaskCount = 0;
m_uUsedCount = 0;
for (int i = 0; i < m_uTaskCount; i++)
{
var e = m_TaskEntries[i];
if (e == null || e.m_ID == 0) continue;
var templ = e.GetTempl();
if (templ == null) continue;
if (templ.m_pParent != null) continue;
if (templ.m_FixedData.m_bHidden) m_uTopHideTaskCount++;
else if (templ.m_FixedData.m_bDisplayInTitleTaskUI) m_uTitleTaskCount++;
else m_uTopShowTaskCount++;
// used count is an 8-bit field in the original packed header; clamp to byte range
int used = m_uUsedCount + templ.m_uDepth;
m_uUsedCount = (byte)Math.Clamp(used, 0, byte.MaxValue);
}
}
public void ClearTask(TaskInterface pTask, ActiveTaskEntry pEntry, bool bRemoveItem)
{
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,
ActiveTaskEntry pEntry,
bool bRemoveItem,
bool bRemoveAcquired,
bool bClearTask)
{
while (pEntry.m_ChildIndex != 0xff)
{
RecursiveClearTask(
pTask,
m_TaskEntries[pEntry.m_ChildIndex],
bRemoveItem,
bRemoveAcquired,
bClearTask);
}
ATaskTempl pTempl = pEntry.GetTempl();
// ȥõƷ
#if !_TASK_CLIENT
if (bRemoveItem && pTempl != null)
{
if (bRemoveAcquired || pTempl.m_FixedData.m_bClearAcquired) pTempl.RemoveAcquiredItem(pTask, bClearTask, false);
pTempl.TakeAwayGivenItems(pTask);
}
#endif
ushort uTaskId = pEntry.m_ID;
pEntry.m_ulTemplAddr = 0;
pEntry.m_ID = 0;
if (m_uTaskCount > 0)
m_uTaskCount--;
else
// TaskInterface::WriteLog(pTask->GetPlayerId(), uTaskId, 0, "ClearTask, TaskCount == 0");
if (pEntry.m_ParentIndex != 0xff)
{
if (pEntry.m_PrevSblIndex != 0xff)
m_TaskEntries[pEntry.m_PrevSblIndex].m_NextSblIndex = pEntry.m_NextSblIndex;
else
m_TaskEntries[pEntry.m_ParentIndex].m_ChildIndex = pEntry.m_NextSblIndex;
if (pEntry.m_NextSblIndex != 0xff) m_TaskEntries[pEntry.m_NextSblIndex].m_PrevSblIndex = pEntry.m_PrevSblIndex;
}
else
{
if (pTempl != null)
{
if (pTempl.m_FixedData.m_bHidden)
{
if (m_uTopHideTaskCount > 0)
m_uTopHideTaskCount--;
}
else if(pTempl.m_FixedData.m_bDisplayInTitleTaskUI)
{
if (m_uTitleTaskCount > 0)
m_uTitleTaskCount--;
}
else
{
if (m_uTopShowTaskCount > 0)
m_uTopShowTaskCount--;
}
if (m_uUsedCount >= pTempl.m_uDepth)
m_uUsedCount -= pTempl.m_uDepth;
else
{
// TaskInterface::WriteLog(pTask->GetPlayerId(), uTaskId, 0, "ClearTask, No Enough Used Count");
m_uUsedCount = 0;
}
}
}
}
public ActiveTaskEntry GetEntry(uint ulId)
{
for (int i = 0; i < m_uTaskCount; i++)
if (m_TaskEntries[i].m_ID == ulId)
return m_TaskEntries[i];
return null;
}
// ActiveTaskEntry* GetEntry(unsigned long ulId)
// {
// for (unsigned char i = 0; i < m_uTaskCount; i++)
// if (m_TaskEntries[i].m_ID == ulId)
// return &m_TaskEntries[i];
//
// return NULL;
// }
public void RemoveAll()
{
ushort version = m_Version; // Preserve the version
Array.Clear(header, 0, header.Length); // Clear the header array
m_Version = version; // Restore the version
m_uTaskCount = 0; // Reset task count
m_uUsedCount = 0; // Reset used count
m_uTopShowTaskCount = 0; // Reset top show task count
m_uTopHideTaskCount = 0; // Reset top hide task count
m_uTitleTaskCount = 0; // Reset title task count
for (int i = 0; i < m_TaskEntries.Length; i++) // Clear all task entries
{
m_TaskEntries[i] = null;
}
}
public bool IsValid() { return m_uTaskCount <= TaskInterfaceConstants.TASK_ACTIVE_LIST_MAX_LEN; }
// bool IsTimeMarkUpdate() const { return (m_uListState & TLIST_STATE_UPDATE_TIME_MARK) != 0; }
// void SetTimeMarkUpdate() { m_uListState |= TLIST_STATE_UPDATE_TIME_MARK; }
// void ClearTimeMarkUpdate() { m_uListState &= ~TLIST_STATE_UPDATE_TIME_MARK; }
public int GetMaxSimultaneousCount() {return m_uMaxSimultaneousCount ? TaskTemplConstants.TASK_MAX_SIMULTANEOUS_COUT : TaskTemplConstants.TASK_DEFAULT_MAX_SIMULTANEOUS_COUT;}
public void ExpandMaxSimultaneousCount()
{
m_uMaxSimultaneousCount = true;
}
// 从列表中移除指定条目(并重新对齐列表) // English: Remove an entry from the list (and realign)
public void RemoveEntry(ActiveTaskEntry entry)
{
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;
if (m_uTaskCount > 0) m_uTaskCount--;
RealignTask(entry, 0);
}
};
}