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 ushort m_uCount; public TaskFinishCountEntry[] m_aList; public uint Search(uint ulID, ref uint ulTime) { if (m_aList == null) return 0u; for (ushort i = 0; i < m_uCount; i++) { if (m_aList[i].m_uTaskId == (ushort)ulID) { ulTime = m_aList[i].m_ulFinishTime; return m_aList[i].m_ulFinishCount; } } return 0u; } public void ResetAt(uint ulID) { for (ushort i = 0; i < m_uCount; i++) { if (m_aList != null && m_aList[i].m_uTaskId == (ushort)ulID) m_aList[i].m_ulFinishCount = 0; } } public void AddOrUpdate(uint ulID, uint ulFinishTime) { if (m_aList == null || m_aList.Length != TaskInterfaceConstants.TASK_FINISH_COUNT_MAX_LEN) m_aList = new TaskFinishCountEntry[TaskInterfaceConstants.TASK_FINISH_COUNT_MAX_LEN]; 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[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(); 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(data[offset..(offset + entrySize)]); } return true; } // Persist list back into a buffer returned by TaskInterface.GetFinishedCntList(). // Layout: ushort count + TASK_FINISH_COUNT_MAX_LEN * TaskFinishCountEntry bytes public void WriteToBuffer(byte[] data) { if (data == null) return; int entrySize = Marshal.SizeOf(); int expectedSize = 2 + entrySize * TaskInterfaceConstants.TASK_FINISH_COUNT_MAX_LEN; if (data.Length < expectedSize) return; Array.Copy(BitConverter.GetBytes(m_uCount), 0, data, 0, 2); if (m_aList == null || m_aList.Length != TaskInterfaceConstants.TASK_FINISH_COUNT_MAX_LEN) { Array.Clear(data, 2, data.Length - 2); return; } for (int i = 0; i < TaskInterfaceConstants.TASK_FINISH_COUNT_MAX_LEN; i++) { int offset = 2 + i * entrySize; // TaskFinishCountEntry is blittable with Pack=1, use helper byte[] entryBytes = GPDataTypeHelper.ToBytes(m_aList[i]); Array.Copy(entryBytes, 0, data, offset, Math.Min(entryBytes.Length, entrySize)); } } }; [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() ]; // 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(new IntPtr(unchecked((long)m_ulTemplAddr))); } catch { } } return null; } // const ATaskTempl* GetCap() const { return reinterpret_cast(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; } } /// /// 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. /// 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); } /// /// Index-based variant used by deliver/award flows where the "slot" matters (C++ passes an entry pointer). /// 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); } }; }