using BrewMonster.Managers; using BrewMonster.Network; using BrewMonster.Scripts; // For CECNavigateCtrl using BrewMonster.UI; using CSNetwork; using CSNetwork.GPDataType; using Cysharp.Threading.Tasks; using PerfectWorld.Scripts.Task; using System; using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; using System.Threading; using UnityEngine; using UnityEngine.Networking; using static BrewMonster.TASKDICE_ESSENCE; namespace BrewMonster.Scripts.Task { [Serializable] [StructLayout(LayoutKind.Sequential, Pack = 1)] public class Kill_Player_Requirements { public const uint MAX_OCCPU_MASK = ((uint)1 << TaskInterfaceConstants.MAX_OCCUPATIONS) - 1u; public uint m_ulOccupations; public int m_iMinLevel; public int m_iMaxLevel; public int m_iGender; public int m_iForce; public Kill_Player_Requirements() { m_iMinLevel = 10; m_iMaxLevel = 100; m_iGender = 0; m_iForce = 0; m_ulOccupations = MAX_OCCPU_MASK; } public bool IsMeetAllOccupation() { return m_ulOccupations == MAX_OCCPU_MASK; } public bool CheckRequirements(int iOccupation, int iLevel, bool bGender, int iForce) { bool bForce = false; // �༭����1Ϊ�У�2ΪŮ // In editor: 1 is male, 2 is female int iGender = bGender ? 2 : 1; // �༭����0����û������Ҫ�� // In editor: 0 means no force requirement if (m_iForce == 0) { bForce = true; } // �����ʾ������Ҫ�� // Otherwise indicates there is a force requirement else { // �༭���ﻪ�⡢�����ͻ�ҹ�ֱ��õ�һ����������������λ��ʾ // Map force ids to bit positions int iForceMask = 0; if (iForce == 0) { return false; } else if (iForce == 1004) { iForceMask = 1 << 0; } else if (iForce == 1005) { iForceMask = 1 << 1; } else if (iForce == 1006) { iForceMask = 1 << 2; } bForce = (m_iForce & iForceMask) != 0; } return ((m_ulOccupations & (1u << iOccupation)) != 0) && m_iMinLevel <= iLevel && m_iMaxLevel >= iLevel && (m_iGender != 0 ? m_iGender == iGender : true) && bForce; } } #if _TASK_CLIENT [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct Task_State_info { public uint m_ulTimeLimit; public uint m_ulTimePassed; public uint m_ulNPCToProtect; public uint m_ulProtectTime; public uint m_ulWaitTime; public uint m_ulErrCode; public uint m_ulGoldWanted; public uint m_ulReachLevel; public uint m_ulReachReincarnation; public uint m_ulReachRealm; public uint m_ulPremLevelMin; [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct m_MonsterWanted_s { public uint m_ulMonsterId; public uint m_ulMonstersToKill; public uint m_ulMonstersKilled; } [MarshalAs(UnmanagedType.ByValArray, SizeConst = TaskInterfaceConstants.MAX_MONSTER_WANTED)] public m_MonsterWanted_s[] m_MonsterWanted; [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct m_ItemsWanted_s { public uint m_ulItemId; public uint m_ulItemsToGet; public uint m_ulItemsGained; public uint m_ulMonsterId; } [MarshalAs(UnmanagedType.ByValArray, SizeConst = TaskInterfaceConstants.MAX_ITEM_WANTED)] public m_ItemsWanted_s[] m_ItemsWanted; [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct TASK_INFO_PLAYER { public uint m_ulPlayersToKill; public uint m_ulPlayersKilled; public Kill_Player_Requirements m_Requirements; } [MarshalAs(UnmanagedType.ByValArray, SizeConst = TaskInterfaceConstants.MAX_PLAYER_WANTED)] public TASK_INFO_PLAYER[] m_PlayerWanted; // abase::vector m_TaskCharArr (assumed 3 pointers: start, finish, end_of_storage; 32-bit layout) [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct abase_vector_wchar_t_ptr { public uint _start; public uint _finish; public uint _end_of_storage; } public abase_vector_wchar_t_ptr m_TaskCharArr; } [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct Task_Award_Preview { public uint m_ulGold; public uint m_ulExp; public uint m_ulRealmExp; public uint m_ulSP; public bool m_bHasItem; public bool m_bItemKnown; public uint m_ulItemTypes; [MarshalAs(UnmanagedType.ByValArray, SizeConst = TaskInterfaceConstants.MAX_ITEM_AWARD)] public uint[] m_ItemsId; [MarshalAs(UnmanagedType.ByValArray, SizeConst = TaskInterfaceConstants.MAX_ITEM_AWARD)] public uint[] m_ItemsNum; public int m_iForceActivity; public int m_iForceContrib; public int m_iForceRepu; } #endif public class TaskInterfaceConstants { // Task Prerequisite Error Code public const int TASK_PREREQU_FAIL_INDETERMINATE = 1; public const int TASK_PREREQU_FAIL_NOT_ROOT = 2; public const int TASK_PREREQU_FAIL_SAME_TASK = 3; public const int TASK_PREREQU_FAIL_NO_SPACE = 4; public const int TASK_PREREQU_FAIL_FULL = 5; public const int TASK_PREREQU_FAIL_CANT_REDO = 6; public const int TASK_PREREQU_FAIL_BELOW_LEVEL = 7; public const int TASK_PREREQU_FAIL_ABOVE_LEVEL = 8; public const int TASK_PREREQU_FAIL_NO_ITEM = 9; public const int TASK_PREREQU_FAIL_BELOW_REPU = 10; public const int TASK_PREREQU_FAIL_CLAN = 11; public const int TASK_PREREQU_FAIL_WRONG_GENDER = 12; public const int TASK_PREREQU_FAIL_NOT_IN_OCCU = 13; public const int TASK_PREREQU_FAIL_WRONG_PERIOD = 14; public const int TASK_PREREQU_FAIL_PREV_TASK = 15; public const int TASK_PREREQU_FAIL_MAX_RCV = 16; public const int TASK_PREREQU_FAIL_NO_DEPOSIT = 17; public const int TASK_PREREQU_FAIL_NO_TASK = 18; public const int TASK_PREREQU_FAIL_NOT_CAPTAIN = 19; public const int TASK_PREREQU_FAIL_ILLEGAL_MEM = 20; public const int TASK_PREREQU_FAIL_WRONG_TIME = 21; public const int TASK_PREREQU_FAIL_NO_SUCH_SUB = 22; public const int TASK_PREREQU_FAIL_MUTEX_TASK = 23; public const int TASK_PREREQU_FAIL_NOT_IN_ZONE = 24; public const int TASK_PREREQU_FAIL_WRONG_SUB = 25; public const int TASK_PREREQU_FAIL_OUTOF_DIST = 26; public const int TASK_PREREQU_FAIL_GIVEN_ITEM = 27; public const int TASK_PREREQU_FAIL_LIVING_SKILL = 28; public const int TASK_PREREQU_FAIL_SPECIAL_AWARD = 29; public const int TASK_PREREQU_FAIL_GM = 30; public const int TASK_PREREQU_FAIL_GLOBAL_KEYVAL = 31; public const int TASK_PREREQU_FAIL_SHIELD_USER = 32; public const int TASK_PREREQU_FAIL_ALREADY_HAS_PQ = 33; public const int TASK_PREREQU_FAIL_MAX_ACC_CNT = 34; public const int TASK_PREREQU_FAIL_RMB_NOT_ENOUGH = 35; public const int TASK_PREREQU_FAIL_NOT_COUPLE = 36; public const int TASK_PREREQU_FAIL_ERR_CHAR_TIME = 37; public const int TASK_PREREQU_FAIL_NOT_IVTRSLOTNUM = 38; // version 81 public const int TASK_PREREQU_FAIL_BELOW_FACTION_CONTRIB = 39; // version 87 public const int TASK_PREREQU_FAIL_BELOW_RECORD_TASKS_NUM = 40; // version 91 public const int TASK_PREREQU_FAIL_OVER_RECEIVE_PER_DAY = 41; public const int TASK_PREREQU_FAIL_TRANSFORM_MASK = 42; public const int TASK_PREREQU_FAIL_FORCE = 43; public const int TASK_PREREQU_FAIL_FORCE_REPUTATION = 44; public const int TASK_PREREQU_FAIL_FORCE_CONTRIBUTION = 45; public const int TASK_PREREQU_FAIL_EXP = 46; public const int TASK_PREREQU_FAIL_SP = 47; public const int TASK_PREREQU_FAIL_FORCE_AL = 48; public const int TASK_PREREQU_FAIL_WEDDING_OWNER = 49; public const int TASK_PREREQU_FAIL_CROSSSERVER_NO_ACOUNT_LIMIT = 50; public const int TASK_PREREQU_FAIL_CROSSSERVER_NO_MARRIAGE = 51; public const int TASK_PREREQU_FAIL_CROSSSERVER_NO_FORCE = 52; public const int TASK_PREREQU_FAIL_KING = 53; public const int TASK_PREREQU_FAIL_IN_TEAM = 54; public const int TASK_PREREQU_FAIL_TITLE = 55; public const int TASK_PREREQU_FAIL_HISTORYSTAGE = 56; public const int TASK_PREREQU_FAIL_NO_GIFTCARD_TASK = 57; public const int TASK_PREREQU_FAIL_BELOW_REINCARNATION = 57; public const int TASK_PREREQU_FAIL_ABOVE_REINCARNATION = 58; public const int TASK_PREREQU_FAIL_BELOW_REALMLEVEL = 59; public const int TASK_PREREQU_FAIL_ABOVE_REALMLEVEL = 60; public const int TASK_PREREQU_FAIL_REALM_EXP_FULL = 61; public const int TASK_PREREQU_FAIL_CARD_COUNT_COLLECTION = 62; public const int TASK_PREREQU_FAIL_MAX_ROLE_CNT = 63; public const int TASK_PREREQU_FAIL_CARD_COUNT_RANK = 64; public const int TASK_PREREQU_FAIL_TASK_FORBID = 65; public const int TASK_PREREQU_FAIL_NO_NAVIGATE_INSHPAED = 66; public const int TASK_AWARD_FAIL_GIVEN_ITEM = 150; public const int TASK_AWARD_FAIL_NEW_TASK = 151; public const int TASK_AWARD_FAIL_REPUTATION = 152; public const int TASK_AWARD_FAIL_GLOBAL_KEYVAL = 153; public const int TASK_AWARD_FAIL_CROSSSERVER_NO_ACOUNT_LIMIT = 154; public const int TASK_AWARD_FAIL_CROSSSERVER_NO_ACOUNT_STORAGE = 155; public const int TASK_AWARD_FAIL_CROSSSERVER_NO_DIVORCE = 156; public const int TASK_AWARD_FAIL_CROSSSERVER_NO_FACTION_RALATED = 157; public const int TASK_AWARD_FAIL_CROSSSERVER_NO_FORCE_RALATED = 158; public const int TASK_AWARD_FAIL_CROSSSERVER_NO_DIVIEND = 159; public const int TASK_AWARD_FAIL_LEVEL_CHECK = 160; // Task messages public const int TASK_MSG_NEW = 1; public const int TASK_MSG_SUCCESS = 2; public const int TASK_MSG_FAIL = 3; public const int TASK_ACTIVE_LIST_HEADER_LEN = 8; public const int TASK_ACTIVE_LIST_MAX_LEN = 175; public const int TASK_FINISHED_LIST_MAX_LEN = 2040; public const int TASK_DATA_BUF_MAX_LEN = 32; public const int TASK_FINISH_TIME_MAX_LEN = 1700; public const int TASK_FINISH_COUNT_MAX_LEN = 730; // 库任务 // Library tasks public const int TASK_MAX_DELIVER_COUNT = 5; public const int TASK_STORAGE_COUNT = 32; public const int TASK_STORAGE_LEN = 10; public const int TASK_STORAGE_WHELL_SCALE = 10000; // 10000.f originally // 当前激活的任务列表缓冲区大小 // Current active task list buffer size public const int TASK_ACTIVE_LIST_BUF_SIZE = (TASK_ACTIVE_LIST_MAX_LEN * TASK_DATA_BUF_MAX_LEN + TASK_ACTIVE_LIST_HEADER_LEN); // 已完成的任务列表缓冲区大小 // Completed task list buffer size public const int TASK_FINISHED_LIST_BUF_SIZE = 8192; public const int TASK_FINISHED_LIST_BUF_SIZE_OLD = 4096; // 任务全局数据大小 // Task global data size public const int TASK_GLOBAL_DATA_SIZE = 256; // 任务完成时间 // Task completion time public const int TASK_FINISH_TIME_LIST_BUF_SIZE = 10240; //任务完成次数 // Task completion count public const int TASK_FINISH_COUNT_LIST_BUF_SIZE = 10240; // 库任务 // Library tasks public const int TASK_STORAGE_LIST_BUF_SIZE = 1024; // Masks public const int TASK_MASK_KILL_MONSTER = 0x00000001; public const int TASK_MASK_COLLECT_ITEM = 0x00000002; public const int TASK_MASK_TALK_TO_NPC = 0x00000004; public const int TASK_MASK_REACH_SITE = 0x00000008; public const int TASK_MASK_ANSWER_QUESTION = 0x00000010; public const int TASK_MASK_TINY_GAME = 0x00000020; public const int TASK_MASK_KILL_PQ_MONSTER = 0x00000040; public const int TASK_MASK_KILL_PLAYER = 0x00000080; public const int MAX_MONSTER_WANTED = 3; // 受ActiveTaskEntry大小限制,最大3 // Limited by ActiveTaskEntry size, max 3 public const int MAX_PLAYER_WANTED = MAX_MONSTER_WANTED; public const int MAX_ITEM_WANTED = 10; public const int MAX_ITEM_AWARD = 64; public const int MAX_MONSTER_SUMMONED = 32; // 最大召唤出的怪物数量 // Maximum number of summoned monsters public const int MAX_OCCUPATIONS = 12; // 职业 // Occupations public const int TASK_MSG_CHANNEL_LOCAL = 0; public const int TASK_MSG_CHANNEL_WORLD = 1; public const int TASK_MSG_CHANNEL_BROADCAST = 9; public const int TASK_TEAM_RELATION_MARRIAGE = 1; public const int TASK_AWARD_MAX_CHANGE_VALUE = 255; public const int TASK_AWARD_MAX_DISPLAY_VALUE = 64; public const int TASK_AWARD_MAX_DISPLAY_EXP_CNT = 32; // 表达式的个数 // Number of expressions public const int TASK_AWARD_MAX_DISPLAY_CHAR_LEN = 64; // 表达式的长度 // Length of expression public const int TASK_WORLD_CONTRIBUTION_SPEND_PER_DAY = 30; // 免费玩家每日消费贡献度上限 // Daily contribution spend cap for free players } public class CECTaskInterface : TaskInterface { private CancellationTokenSource _cts; public void Despose() { _cts?.Cancel(); _cts?.Dispose(); } public int GetCurHistoryStageIndex() { return EC_Game.GetGameRun().GetCurStageIndex() + 1; } public uint GetObtainedGeneralCardCount() { return 0; //return m_pHost ? m_pHost.GetGeneralCardData().GetObtainedCount() : 0; }public uint GetObtainedGeneralCardCountByRank(int rank) { return 0; //return m_pHost ? m_pHost.GetGeneralCardData().GetObtainedCount() : 0; } public bool HaveGotTitle(uint id_designation) { return true; } // public const int TASK_MAX_DELIVER_COUNT = 5; // public const int TASK_STORAGE_COUNT = 32; // public const int TASK_STORAGE_LEN = 10; // public const float TASK_STORAGE_WHELL_SCALE = 10000; // // public const int TASK_ACTIVE_LIST_HEADER_LEN = 8; // public const int TASK_ACTIVE_LIST_MAX_LEN = 175; // public const int TASK_FINISHED_LIST_MAX_LEN = 2040; // public const int TASK_DATA_BUF_MAX_LEN = 32; // public const int TASK_FINISH_TIME_MAX_LEN = 1700; // public const int TASK_FINISH_COUNT_MAX_LEN = 730; // µ±Ç°¼¤»îµÄÈÎÎñÁÐ±í»º³åÇø´óС public int TASK_ACTIVE_LIST_BUF_SIZE => (TaskInterfaceConstants.TASK_ACTIVE_LIST_MAX_LEN * TaskInterfaceConstants.TASK_DATA_BUF_MAX_LEN + TaskInterfaceConstants.TASK_ACTIVE_LIST_HEADER_LEN); CECHostPlayer m_pHost; ActiveTaskList m_pActiveListBuf; // Active task list buffer // private byte[] m_pActiveListRawBuf; // raw buffer backing for active list (converted from C++) byte[] m_pFinishedListBuf; // Finished task list buffer byte[] m_pFinishedTimeListBuf; // Finished time list buffer byte[] m_pFinishedCountListBuf;// Finished count list buffer byte[] m_pStorageTaskListBuf; // Storage tasks list buffer private Dictionary m_TasksToConfirm = new Dictionary(); private readonly Dictionary m_emotionTask = new System.Collections.Generic.Dictionary(); private bool m_bForceNavigateFinish; private int m_tmFinishDlgShown; public CECTaskInterface() { } public CECTaskInterface(CECHostPlayer pHost) { m_pHost = pHost; m_pActiveListBuf = null; m_pFinishedListBuf = null; m_pFinishedTimeListBuf = null; m_pFinishedCountListBuf = null; m_pStorageTaskListBuf = null; } // Initialize object public async UniTask Init(byte[] pActiveListBuf, int iActiveListLen, byte[] pFinishedListBuf, int iFinishedListLen, byte[] pFinishedTimeListBuf, int iFinishedTimeListLen, byte[] pFinishedCountListBuf, int iFinishedCountListLen, byte[] pStorageTaskListBuf, int iStorageTaskListLen) { _cts = new CancellationTokenSource(); // basic argument check (converted from ASSERT) if (pActiveListBuf == null || pFinishedListBuf == null || pFinishedTimeListBuf == null || pFinishedCountListBuf == null) { return false; } // SceneLoader.SceneLoadProcess = SceneLoadProcess.Loading; // SceneLoader.LoadingProgress = 0; LoadingSceneController.Instance.ShowLoadingScene(true); m_pActiveListBuf = new ActiveTaskList(); m_pActiveListBuf.ReadFromBuffer(pActiveListBuf); m_pFinishedListBuf = new byte[TaskInterfaceConstants.TASK_FINISHED_LIST_BUF_SIZE]; { int copy = Mathf.Min(iFinishedListLen, TaskInterfaceConstants.TASK_FINISHED_LIST_BUF_SIZE); if (copy > 0) System.Buffer.BlockCopy(pFinishedListBuf, 0, m_pFinishedListBuf, 0, copy); } m_pFinishedTimeListBuf = new byte[TaskInterfaceConstants.TASK_FINISH_TIME_LIST_BUF_SIZE]; { int copy = Mathf.Min(iFinishedTimeListLen, TaskInterfaceConstants.TASK_FINISH_TIME_LIST_BUF_SIZE); if (copy > 0) System.Buffer.BlockCopy(pFinishedTimeListBuf, 0, m_pFinishedTimeListBuf, 0, copy); } m_pFinishedCountListBuf = new byte[TaskInterfaceConstants.TASK_FINISH_COUNT_LIST_BUF_SIZE]; { int copy = Mathf.Min(iFinishedCountListLen, TaskInterfaceConstants.TASK_FINISH_COUNT_LIST_BUF_SIZE); if (copy > 0) System.Buffer.BlockCopy(pFinishedCountListBuf, 0, m_pFinishedCountListBuf, 0, copy); } m_pStorageTaskListBuf = new byte[TaskInterfaceConstants.TASK_STORAGE_LIST_BUF_SIZE]; { int copy = Mathf.Min(iStorageTaskListLen, TaskInterfaceConstants.TASK_STORAGE_LIST_BUF_SIZE); if (copy > 0) System.Buffer.BlockCopy(pStorageTaskListBuf, 0, m_pStorageTaskListBuf, 0, copy); } // Clear rest buffer // 清理剩余缓冲区 // No-op in C# because arrays are zero-initialized. ATaskTemplMan pTaskMan = GetTaskTemplMan(); pTaskMan.Release(); LoadingSceneController.Instance.ShowLoadingScene(true); LoadingSceneController.Instance.UpdateUI(0f); LoadingSceneController.Instance.SetLoadingText("Loading Tasks From Pack"); //string task_data_path = Path.Combine(Application.streamingAssetsPath, "data/tasks.data"); string task_data_path = "Assets/PerfectWorld/Data/tasks.txt"; await pTaskMan.LoadTasksFromPack(task_data_path, true, (x) => { LoadingSceneController.Instance.SetProgress(x); }, _cts.Token); //var task_npc_path = Path.Combine(Application.streamingAssetsPath, "data/task_npc.data"); var task_npc_path = "Assets/PerfectWorld/Data/task_npc.txt"; await pTaskMan.LoadNPCInfoFromPack(task_npc_path); //var dyn_tasks_path = Path.Combine(Application.streamingAssetsPath, "data/dyn_tasks.data"); var dyn_tasks_path = "Assets/PerfectWorld/Data/dyn_tasks.txt"; await pTaskMan.VerifyDynTasksPack(dyn_tasks_path); InitActiveTaskList(); SceneLoader.SceneLoadProcess = SceneLoadProcess.EndLoading; LoadingSceneController.Instance.ShowLoadingScene(false); m_bForceNavigateFinish = false; return true; } public async UniTask MoveShopDataToPersistentPath(string shopDataPath) { BMLogger.Log($"CECTaskInterface: Moving shop data to persistent path: {shopDataPath}"); var destinationPath = Path.Combine(UnityEngine.Application.persistentDataPath, shopDataPath); var sourcePath = Path.Combine(UnityEngine.Application.streamingAssetsPath, shopDataPath); if (File.Exists(destinationPath)) { return true; } var destinationDirectory = Path.GetDirectoryName(destinationPath); Directory.CreateDirectory(destinationDirectory); UnityWebRequest request = UnityWebRequest.Get(sourcePath); await request.SendWebRequest(); if (request.result != UnityWebRequest.Result.Success) { BMLogger.LogError($"CECTaskInterface: Failed to move element file to persistent path: {request.error}"); return false; } File.WriteAllBytes(destinationPath, request.downloadHandler.data); BMLogger.Log($"CECTaskInterface: Successfully moved element file to persistent path: {destinationPath}"); return true; } public void CheckPQEnterWorldInit() { // TODO: implement PQ enter-world init if needed } public static void WriteLog(int nPlayerId, int nTaskId, int nType, string szLog) { //do something? } public bool IsDeliverLegal() { return !m_pHost.IsTrading() && m_pHost.GetBoothState() == 0 && !m_pHost.IsDead(); } public int GetCommonItemCount(uint ulCommonItem) { // CECInventory pPack = m_pHost.GetPack(); // return pPack != null ? pPack.GetItemTotalNum((int)ulCommonItem) : 0; var inv = m_pHost?.GetInventory(InventoryConst.IVTRTYPE_PACK); return inv != null ? inv.GetItemTotalNum((int)ulCommonItem) : 0; } public int GetTaskItemCount(uint ulTaskItem) { // CECInventory* pPack = m_pHost.GetTaskPack(); // return pPack ? pPack.GetItemTotalNum((int)ulTaskItem) : 0; var inv = m_pHost?.GetInventory(InventoryConst.IVTRTYPE_TASKPACK); return inv != null ? inv.GetItemTotalNum((int)ulTaskItem) : 0; } public ATaskTemplMan GetTaskTemplMan() { return EC_Game.GetTaskTemplateMan(); } public ActiveTaskList GetActiveTaskList() { return m_pActiveListBuf; } // Trong file CECTaskInterface.cs public bool HasTaskRelatedToNPC(int npcID) { // Check tasks is active ActiveTaskList activeList = GetActiveTaskList(); if (activeList != null && activeList.m_TaskEntries != null) { for (int i = 0; i < activeList.m_uTaskCount; i++) { var entry = activeList.m_TaskEntries[i]; if (entry == null) continue; ATaskTempl templ = entry.GetTempl(); if (templ == null) continue; // Check if this NPC is the target of the task if (IsNPCTargetOfTask(templ, entry, npcID)) { return true; } } } // Check tasks new ATaskTemplMan taskMan = GetTaskTemplMan(); if (taskMan != null) { List npcTasks = taskMan.GetTasksFromNPC(npcID); if (npcTasks != null) { uint ulCurTime = GetCurTime(); foreach (var templ in npcTasks) { // Check prerequisite if (templ.CheckPrerequisite(this, activeList, ulCurTime) == 0) { return true; } } } } return false; } // Check NPC is target of the task private bool IsNPCTargetOfTask(ATaskTempl templ, ActiveTaskEntry entry, int npcID) { var data = templ.m_FixedData; // Task completed case if (entry.IsFinished()) { if (data.m_ulAwardNPC == npcID) return true; } else { // NPC target if (data.m_ulNPCMoving == npcID) return true; // Pos target if (data.m_ulNPCDestSite == npcID) return true; // Security target if (data.m_ulNPCToProtect == npcID) return true; if (data.m_enumMethod == (ulong)TaskCompletionMethod.enumTMTalkToNPC) { if (data.m_ulNPCMoving == 0 && data.m_ulAwardNPC == npcID) { return true; } } } return false; } // private void InitActiveTaskList() // { // ActiveTaskList pLst = GetActiveTaskList(); // if (pLst == null) return; // // ATaskTemplMan pMan = GetTaskTemplMan(); // if (pMan == null) return; // // // reset counters // pLst.m_uTopShowTaskCount = 0; // pLst.m_uTopHideTaskCount = 0; // pLst.m_uTitleTaskCount = 0; // // byte i = 0; // while (i < pLst.m_uTaskCount) // { // ActiveTaskEntry entry = pLst.m_TaskEntries[i]; // if (entry == null) // { // i++; // continue; // } // // // repair sibling linkage // if (entry.m_NextSblIndex != (char)0xff) // { // ActiveTaskEntry entryNextSbl = pLst.m_TaskEntries[entry.m_NextSblIndex]; // if (entryNextSbl == null || entryNextSbl.m_PrevSblIndex != (char)i) // { // entry.m_NextSblIndex = (char)0xff; // } // } // // // resolve template for top-level entries; children left unresolved in C# // if (entry.m_ParentIndex == (char)0xff) // { // // entry.m_ulTemplAddr = 0u; // entry.m_ulTemplAddr = pMan.GetTopTaskByID(entry.m_ID) != null ? 1u : 0u; // // ATaskTempl topTempl = pMan.GetTopTaskByID(entry.m_ID); // if (topTempl != null) // { // if (topTempl.m_FixedData.m_bHidden) // pLst.m_uTopHideTaskCount++; // else if (topTempl.m_FixedData.m_bDisplayInTitleTaskUI) // pLst.m_uTitleTaskCount++; // else // pLst.m_uTopShowTaskCount++; // } // } // else // { // entry.m_ulTemplAddr = 0u; // } // // // cap template best-effort (no pointer in managed) // if (entry.m_uCapTaskId != 0) // { // ATaskTempl cap = pMan.GetTopTaskByID(entry.m_uCapTaskId); // entry.m_ulCapTemplAddr = 0u; // if (cap == null) // { // entry.m_uCapTaskId = 0; // } // } // else // { // entry.m_ulCapTemplAddr = 0u; // } // // i++; // } // // // approximate used count // pLst.m_uUsedCount = pLst.m_uTaskCount; // } public void InitActiveTaskList() { ActiveTaskList pLst = GetActiveTaskList(); FinishedTaskList pFnsh = GetFinishedTaskList(); // TaskFinishTimeList* pFnshTime = static_cast(GetFinishedTimeList()); TaskFinishTimeList pFnshTime = new TaskFinishTimeList(GetFinishedTimeList()); TaskFinishCountList pFnshCount = new TaskFinishCountList(); pFnshCount.ReadFromBytes(GetFinishedCntList()); ActiveTaskEntry[] pEntries = pLst.m_TaskEntries; ATaskTemplMan pMan = GetTaskTemplMan(); if (!CheckVersion() || !pLst.IsValid() || !pFnsh.IsValid() || !pFnshTime.IsValid()) { pLst.RemoveAll(); pFnsh.RemoveAll(); pFnshTime.RemoveAll(); // TaskInterface::WriteLog(0, 0, 0, "InitLst, list is invalid"); } if(!pFnshCount.IsValid()) { pFnshCount.RemoveAll(); // TaskInterface::WriteLog(0, 0, 0, "InitLst, finish count list is invalid"); } if (pFnsh.m_FnshHeader.m_Version == 0){ List list_old = new (); // list_old.reserve(pFnsh->m_FnshHeader.m_uTaskCount); list_old.Capacity = pFnsh.m_FnshHeader.m_uTaskCount; // C# equivalent of reserve // FnshedTaskListOld* pListOld = (FnshedTaskListOld*)pFnsh; FnshedTaskListOld pListOld = new FnshedTaskListOld(pFnsh.m_Buf); for (int i = 0; i < pFnsh.m_FnshHeader.m_uTaskCount; i++) { list_old.Add(pListOld.m_aTaskList[i]); } var FnshHeader = pFnsh.m_FnshHeader; FnshHeader.m_Version = 1; pFnsh.m_FnshHeader = FnshHeader; for (int i = 0; i < pFnsh.m_FnshHeader.m_uTaskCount; i++) { pFnsh.m_aTaskList[i].m_uTaskId = (ushort)(list_old[i].m_uTaskId & 0x7fff); pFnsh.m_aTaskList[i].m_Mask = (byte)(list_old[i].m_uTaskId >> 15); } } #if _TASK_CLIENT // Debug output of finished tasks, for developer use -> not converted to C# // FILE* fp = fopen("logs\\Tasks.log", "wb"); // // if (fp) // { // unsigned short magic = 0xfeff; // fwrite(&magic, sizeof(magic), 1, fp); // // for (unsigned long n = 0; n < pFnsh->m_FnshHeader.m_uTaskCount; n++) // { // ATaskTempl* pTempl = GetTaskTemplMan()->GetTaskTemplByID(pFnsh->m_aTaskList[n].m_uTaskId); // // fwprintf( // fp, // L"task = %d, name = %s\r\n", // pFnsh->m_aTaskList[n].m_uTaskId, // pTempl ? pTempl->GetName() : L""); // } // // for (unsigned short m = 0; m < pFnshTime->m_uCount; m++) // { // ATaskTempl* pTempl = GetTaskTemplMan()->GetTaskTemplByID(pFnshTime->m_aList[m].m_uTaskId); // unsigned long his_time = pFnshTime->m_aList[m].m_ulTimeMark; // his_time -= unsigned long(TaskInterface::GetTimeZoneBias() * 60); // // if ((long)(his_time) < 0) // his_time = 0; // // tm t = *gmtime((time_t*)&his_time); // wchar_t buf[256]; // swprintf(buf, L"%d-%02d-%02d-%02d-%02d-%02d", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); // // fwprintf( // fp, // L"task = %d, deliver time = %s, name = %s\r\n", // pFnshTime->m_aList[m].m_uTaskId, // buf, // pTempl ? pTempl->GetName() : L""); // } // // fclose(fp); // } if (!GetTaskTemplMan().IsDynTasksVerified()) { // ��ȡ��̬�����ʱ���ǩ _notify_svr(this, ClientNotificationConstants.TASK_CLT_NOTIFY_DYN_TIMEMARK, 0); } else { // ������⽱����Ϣ GetTaskTemplMan().ClearSpecailAward(); _notify_svr(this, ClientNotificationConstants.TASK_CLT_NOTIFY_SPECIAL_AWARD, 0); // ��ȡ�ֿ����� _notify_svr(this, ClientNotificationConstants.TASK_CLT_NOTIFY_STORAGE, 0); } #else uint ulCurTime = GetCurTime(); const ATaskTempl pTempl; pLst.m_Version = TASK_ENTRY_DATA_CUR_VER; bool bTimeMarkUpdated = pLst.IsTimeMarkUpdate(); pLst.m_uTopShowTaskCount = 0; pLst.m_uTopHideTaskCount = 0; pLst.m_uTitleTaskCount = 0; #endif // unsigned char i = 0; for (int i=0; i < pLst.m_uTaskCount; i++) { ActiveTaskEntry entry = pEntries[i]; if (!entry.IsValid((char)i, (char)pLst.m_uTaskCount)) { pLst.RemoveAll(); // TaskInterface::WriteLog(0, 0, 0, "InitLst, active list is invalid"); break; } } int i1 = 0; while (i1 < pLst.m_uTaskCount) { ActiveTaskEntry entry = pEntries[i1]; if (entry.m_NextSblIndex != 0xff) { ActiveTaskEntry entryNextSbl = pEntries[entry.m_NextSblIndex]; if (entryNextSbl.m_PrevSblIndex != i1) entry.m_NextSblIndex = (char)0xff; } if (entry.m_ParentIndex == 0xff) entry.m_ulTemplAddr = pMan.GetTopTaskByID(entry.m_ID).m_FixedData.m_ID; else { ATaskTempl pParent = pLst.m_TaskEntries[entry.m_ParentIndex].GetTempl(); if (pParent != null) entry.m_ulTemplAddr = pParent.GetConstSubById(entry.m_ID).m_FixedData.m_ID; else entry.m_ulTemplAddr = 0; } #if !_TASK_CLIENT // if (entry.m_ulTemplAddr != 0) // { // // TaskInterface::WriteLog(0, entry.m_ID, 0, "InitLst, Cant Find Task"); // // pLst.ClearTask(this, entry, false); // continue; // } // // // ������������û��ɣ������ // if (entry.m_ChildIndex == 0xff // && entry.GetTempl().m_FixedData.m_enumMethod == (uint)TaskCompletionMethod.enumTMNone // && !entry.IsFinished()) // { // // TaskInterface::WriteLog(0, entry.m_ID, 0, "InitLst, Task is Impossible"); // pLst.ClearTask(this, entry, false); // continue; // } #endif if (entry.m_uCapTaskId != 0) { entry.m_ulCapTemplAddr = GetTaskTemplMan().GetTopTaskByID(entry.m_uCapTaskId).m_FixedData.m_ID; if (entry.m_ulCapTemplAddr != 0) { entry.m_uCapTaskId = 0; // TaskInterface::WriteLog(0, entry.m_uCapTaskId, 0, "InitLst, Cant Find CapTask"); } } else entry.m_ulCapTemplAddr = 0; #if !_TASK_CLIENT // if (bTimeMarkUpdated != 0) // { // var pTempl = entry.GetTempl(); // // if (!pTempl.m_FixedData.m_bAbsTime && !pTempl.m_FixedData.m_bPQTask && !pTempl.m_FixedData.m_bPQSubTask) // entry.m_ulTaskTime = ulCurTime - entry.m_ulTaskTime; // } #endif #if !_TASK_CLIENT // ��ʼ������������е����ء���ʾ������� // if (entry.m_ParentIndex == 0xff) // { // if (entry.GetTempl()->m_bHidden) // pLst->m_uTopHideTaskCount++; // else if (entry.GetTempl()->m_bDisplayInTitleTaskUI) // pLst->m_uTitleTaskCount++; // else pLst->m_uTopShowTaskCount++; // // } #endif i1++; } #if !_TASK_CLIENT // pLst->SetTimeMarkUpdate(); // pLst->UpdateTaskMask(*GetTaskMask()); #endif pLst.UpdateUsedCount(); } public bool CheckTaskForbid(uint task_id){ return false; } public bool IsAtCrossServer() { // TODO: cross server // return CECCrossServer.Instance().IsOnSpecialServer(); return false; } public uint GetPlayerLevel() { return (uint)m_pHost.GetBasicProps().iLevel; } public uint GetGoldNum() { return (uint)m_pHost.GetMoneyAmount(); } public int GetGlobalValue(int lKey) { return EC_Game.GetGameRun().GetCommonData(lKey); } // C++: long CECTaskInterface::GetGlobalValue(long lKey) public long GetGlobalValue(long key) { // NOTE: Engine exposes only int common data; cast to long return EC_Game.GetGameRun().GetCommonData((int)key); } public byte GetShapeMask() { return m_pHost.GetShapeMask(); } public int GetPos(float[] pos) { A3DVECTOR3 vPos = m_pHost.GetPos(); if (pos != null && pos.Length >= 3) { pos[0] = vPos.x; pos[1] = vPos.y; pos[2] = vPos.z; } var world = World.CECWorld.Instance; return world != null ? world.GetInstanceID() : 0; } public bool CheckSimpleTaskFinshConditon(uint task_id) { ATaskTempl pTempl = GetTaskTemplMan().GetTaskTemplByID(task_id); if (pTempl == null) return false; if (pTempl.m_FixedData.m_enumMethod == (uint)TaskCompletionMethod.enumTMSimpleClientTask && pTempl.m_FixedData.m_uiEmotion != 0) { if (m_emotionTask != null && m_emotionTask.TryGetValue(task_id, out bool finished)) return finished; return false; } else if (pTempl.m_FixedData.m_enumMethod == (uint)TaskCompletionMethod.enumTMSimpleClientTaskForceNavi) { return GetForceNavigateFinishFlag(); } return true; } public int GetFactionConsumeContrib() { return m_pHost.GetContribInfo().consume_contrib; } public int GetFactionExpContrib() { return m_pHost.GetContribInfo().exp_contrib; } public bool HasTask(uint ulTaskId) { ActiveTaskList pLst = GetActiveTaskList(); if (pLst == null) return false; for (int i = 0; i < pLst.m_uTaskCount; i++) { ActiveTaskEntry entry = pLst.m_TaskEntries[i]; if ((uint)entry.m_ID == ulTaskId && entry.GetTempl() != null) return true; } return false; } // ===== Additional TaskInterface methods ported from C++ ===== public uint GetMaxHistoryLevel() { // TODO: Use reincarnation tome when available (GetReincarnationTome().max_level) return (uint)m_pHost.GetBasicProps().iLevel; } public uint GetReincarnationCount() { // TODO: Hook to host reincarnation count when available return 0u; } public bool IsRealmExpFull() { // TODO: Implement via host API when available return false; } public uint GetReputation() { // TODO: Expose reputation on host; return 0 for now return 0u; } public int GetFactionRole() { // TODO: Expose faction role id on host return 0; } public bool IsInFaction(uint factionId) { // TODO: Expose faction id on host; cannot verify now return false; } public bool IsMale() { return m_pHost.m_iGender == GENDER.GENDER_MALE; } public uint GetPlayerOccupation() { return (uint)m_pHost.m_iProfession; } public uint GetCurPeriod() { // Maps to "second level" in basic props (realm level) return (uint)m_pHost.GetBasicProps().iLevel2; } public bool IsGM() { // TODO: Expose GM flag on host return false; } public bool IsShieldUser() { // TODO: Expose ShieldUser flag on host return false; } public bool IsMarried() { // TODO: Expose spouse id on host return false; } public bool IsWeddingOwner() { // TODO: Expose wedding scene info and compare with host id return false; } public uint GetInvEmptySlot() { var inv = m_pHost?.GetInventory(InventoryConst.IVTRTYPE_PACK); return inv != null ? (uint)inv.GetEmptySlotNum() : 0u; } public int GetFactionContrib() { return m_pHost.GetContribInfo().cumulate_contrib; } public int GetForce() { // TODO: Expose force id on host return 0; } public int GetForceReputation() { // TODO: Expose force reputation on host return 0; } public int GetForceContribution() { // TODO: Expose force contribution on host return 0; } public int GetExp() { return m_pHost.GetBasicProps().iExp; } public int GetSP() { return m_pHost.GetBasicProps().iSP; } public int GetForceActivityLevel() { // TODO: Expose force activity level on host return -1; } public bool IsKing() { // TODO: Expose king status on host return false; } public bool IsInTeam() { // TODO: Implement team system and check membership return false; } public uint GetAccountTotalCash() { // TODO: Expose account total cash on host/session return 0u; } #if _TASK_CLIENT // Prepare award preview based on task and state public void GetTaskAwardPreview(uint ulTaskId, ref Task_Award_Preview p, bool bActiveTask=true) { // Zero and init output p = default; if (p.m_ItemsId == null) p.m_ItemsId = new uint[TaskInterfaceConstants.MAX_ITEM_AWARD]; if (p.m_ItemsNum == null) p.m_ItemsNum = new uint[TaskInterfaceConstants.MAX_ITEM_AWARD]; // Gather context ActiveTaskList pLst = GetActiveTaskList(); uint ulCurTime = GetCurTime(); ATaskTempl pTempl = null; AWARD_DATA ad = default; uint ulMulti = 1u; // Resolve template and dynamic award when active if (bActiveTask && pLst != null) { for (int i = 0; i < pLst.m_uTaskCount; i++) { ActiveTaskEntry CurEntry = pLst.m_TaskEntries[i]; if (CurEntry.m_ID != ulTaskId || CurEntry.m_ulTemplAddr == 0) continue; pTempl = CurEntry.GetTempl(); // TODO: CalcAwardData/CalcAwardMulti not implemented yet in C#; use fixed success award and multiplier 1 if (pTempl != null) { ad = pTempl.m_FixedData.m_Award_S; ulMulti = 1u; } if (ulMulti == 0u) return; break; } } // Fallback to top template when inactive else { pTempl = GetTaskTemplMan().GetTopTaskByID(ulTaskId); if (pTempl == null) return; ad = pTempl.m_FixedData.m_Award_S; } // Fill basic award fields unchecked { p.m_ulGold = ad.m_ulGoldNum * ulMulti; p.m_ulExp = ad.m_ulExp * ulMulti; p.m_ulSP = ad.m_ulSP * ulMulti; } p.m_iForceActivity = ad.m_iForceActivity; p.m_iForceContrib = ad.m_iForceContribution; p.m_iForceRepu = ad.m_iForceReputation; p.m_ulRealmExp = ad.m_ulRealmExp; // Apply level coefficient if configured (coefficient table not available; skip scaling) if (ad.m_bUseLevCo) { uint ulLev = GetPlayerLevel(); if (ulLev == 0) ulLev = 1; uint ulUpper = 0; if (pTempl != null && pTempl.GetTopTask() != null) ulUpper = pTempl.GetTopTask().m_FixedData.m_ulPremise_Lev_Max; if (ulUpper != 0 && ulLev > ulUpper) ulLev = ulUpper; // NOTE: Original code multiplies by _lev_co[ulLev-1]. Not available here; keep as-is. } // Candidate items handling if (ad.m_ulCandItems == 1 && ad.m_CandItems != null && ad.m_CandItems.Length > 0) { p.m_bHasItem = true; p.m_bItemKnown = true; AWARD_ITEMS_CAND ic = ad.m_CandItems[0]; if (ic.m_bRandChoose) { p.m_bItemKnown = false; } else { for (int j = 0; j < ic.m_ulAwardItems; j++) { ITEM_WANTED wi = ic.m_AwardItems[j]; if (!wi.m_bCommonItem) continue; else if (wi.m_fProb != 1.0f) { p.m_bItemKnown = false; break; } else { // Period conversion not implemented; assume valid if (p.m_ulItemTypes < TaskInterfaceConstants.MAX_ITEM_AWARD) { p.m_ItemsId[p.m_ulItemTypes] = wi.m_ulItemTemplId; p.m_ItemsNum[p.m_ulItemTypes] = wi.m_ulItemNum; p.m_ulItemTypes++; } } } } } else if (ad.m_ulCandItems > 1) { p.m_bHasItem = true; } // Done return; } public uint GetTaskCount() { ActiveTaskList pLst = GetActiveTaskList(); uint ulCount = 0; if (pLst == null) return 0u; for (int i = 0; i < pLst.m_uTaskCount; i++) { ActiveTaskEntry CurEntry = pLst.m_TaskEntries[i]; ATaskTempl pTempl = CurEntry.GetTempl(); if (pTempl != null && !pTempl.m_FixedData.m_bDisplayInTitleTaskUI && CurEntry.m_ParentIndex == (char)0xff) { if (!pTempl.m_FixedData.m_bHidden || pTempl.m_FixedData.m_bShowPrompt) { ulCount++; } } } return ulCount; } public uint GetTaskId(uint ulIndex) { ActiveTaskList pLst = GetActiveTaskList(); byte uTopCount = 0; int uCount = 0; if (pLst == null) return 0u; while (uCount < pLst.m_uTaskCount) { ActiveTaskEntry CurEntry = pLst.m_TaskEntries[uCount]; ATaskTempl pTempl = CurEntry.GetTempl(); if (pTempl != null && !pTempl.m_FixedData.m_bDisplayInTitleTaskUI && CurEntry.m_ParentIndex == (char)0xff) { if (!pTempl.m_FixedData.m_bHidden || pTempl.m_FixedData.m_bShowPrompt) { if (ulIndex == uTopCount) return CurEntry.m_ID; else uTopCount++; } } uCount++; } return 0u; } public uint CanDeliverTask(uint ulTaskId) { ATaskTempl pTempl = GetTaskTemplMan().GetTopTaskByID(ulTaskId); if (pTempl == null) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_NO_TASK; if (!IsDeliverLegal()) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_INDETERMINATE; // return pTempl.CheckPrerequisite(this, static_cast(GetActiveTaskList()), GetCurTime(), true, true, false); return pTempl.CheckPrerequisite(this, GetActiveTaskList(), GetCurTime(), true, true, false); } public bool CanDeliverCommonItem(uint ulTypes) { return m_pHost.GetPack().GetEmptySlotNum() >= (int)(ulTypes); } public bool CanDeliverTaskItem(uint ulTypes) { return m_pHost.GetTaskPack().GetEmptySlotNum() >= (int)ulTypes; } public void ShowPunchBagMessage(bool bSucced,bool bMax,uint MonsterTemplID,int dps,int dph) { // TODO : implement UI message box // CECGameUIMan* pGameUI = g_pGame->GetGameRun()->GetUIManager()->GetInGameUIMan(); // // if (pGameUI == NULL) return; // // elementdataman *pDataMan = g_pGame->GetElementDataMan(); // // DATA_TYPE dt; // MONSTER_ESSENCE *pMonster = (MONSTER_ESSENCE *)pDataMan->get_data_ptr(MonsterTemplID, ID_SPACE_ESSENCE, dt); // // ACString strNpcName; // if( dt == DT_MONSTER_ESSENCE ) // { // if( pMonster) // strNpcName = pMonster->name; // } // // ACString str; // if (!bSucced) // { // str.Format(pGameUI->GetStringFromTable(303),strNpcName); // pGameUI->MessageBox("",str,MB_OK,A3DCOLORRGBA(255, 255, 255, 160)); // } // // else if (bMax) // { // str.Format(pGameUI->GetStringFromTable(304),dph); // pGameUI->MessageBox("",str,MB_OK,A3DCOLORRGBA(255, 255, 255, 160)); // } // else // { // str.Format(pGameUI->GetStringFromTable(305),dph); // pGameUI->MessageBox("",str,MB_OK,A3DCOLORRGBA(255, 255, 255, 160)); // } } #endif private bool GetForceNavigateFinishFlag() { return m_bForceNavigateFinish; } public void GetTaskStateInfo(uint ulTaskId, ref Task_State_info pInfo, bool bActiveTask=true) { // 清零并准备输出结构 // Zero and prepare output struct pInfo = default; if (pInfo.m_MonsterWanted == null) pInfo.m_MonsterWanted = new Task_State_info.m_MonsterWanted_s[TaskInterfaceConstants.MAX_MONSTER_WANTED]; if (pInfo.m_ItemsWanted == null) pInfo.m_ItemsWanted = new Task_State_info.m_ItemsWanted_s[TaskInterfaceConstants.MAX_ITEM_WANTED]; if (pInfo.m_PlayerWanted == null) pInfo.m_PlayerWanted = new Task_State_info.TASK_INFO_PLAYER[TaskInterfaceConstants.MAX_PLAYER_WANTED]; ActiveTaskList pLst = GetActiveTaskList(); uint ulCurTime = GetCurTime(); // 当前时间 // current time ATaskTempl pTempl = null; int foundEntryIndex = -1; // Keep track of entry index like C++ code does if (bActiveTask) { // 在激活任务列表中查找 // Search in active task list for (int i = 0; i < pLst.m_uTaskCount; i++) { ActiveTaskEntry CurEntry = pLst.m_TaskEntries[i]; if (CurEntry.m_ID != ulTaskId || CurEntry.m_ulTemplAddr == 0) continue; pTempl = CurEntry.GetTempl(); foundEntryIndex = i; // Store index to access entry later // 检查任务是否可以完成 // Check if task can be completed if (pTempl != null && pTempl.CanFinishTask(this, CurEntry, ulCurTime)) { pInfo.m_ulErrCode = pTempl.RecursiveCheckAward(this, pLst, CurEntry, ulCurTime, -1); if (pInfo.m_ulErrCode == TaskInterfaceConstants.TASK_AWARD_FAIL_LEVEL_CHECK) { ATaskTempl pParent = pTempl; while (pParent != null && pParent.m_FixedData.m_ulPremise_Lev_Min == 0) pParent = pParent.m_pParent; if (pParent != null) pInfo.m_ulPremLevelMin = pParent.m_FixedData.m_ulPremise_Lev_Min; } } pInfo.m_ulTimePassed = ulCurTime > CurEntry.m_ulTaskTime ? (ulCurTime - CurEntry.m_ulTaskTime) : 0; if (pTempl != null && pTempl.m_FixedData.m_ulPremise_Lev_Min != 0) { if (pTempl.m_FixedData.m_bPremCheckMaxHistoryLevel != 0 && GetPlayerLevel() < pTempl.m_FixedData.m_ulPremise_Lev_Min) { pInfo.m_ulErrCode = TaskInterfaceConstants.TASK_AWARD_FAIL_LEVEL_CHECK; pInfo.m_ulPremLevelMin = pTempl.m_FixedData.m_ulPremise_Lev_Min; } } break; } } else { pTempl = GetTaskTemplMan().GetTopTaskByID(ulTaskId); } if (pTempl == null) return; // 基本任务要求 // Basic task requirements if (pTempl.m_FixedData.m_ulTimeLimit != 0) pInfo.m_ulTimeLimit = pTempl.m_FixedData.m_ulTimeLimit; if (pTempl.m_FixedData.m_ulGoldWanted != 0) pInfo.m_ulGoldWanted = pTempl.m_FixedData.m_ulGoldWanted; // 任务类型分支 // Task method branches if (pTempl.m_FixedData.m_enumMethod == (uint)TaskCompletionMethod.enumTMCollectNumArticle) { for (int j = 0; j < pTempl.m_FixedData.m_ulItemsWanted; j++) { var iw = pTempl.m_FixedData.m_ItemsWanted[j]; pInfo.m_ItemsWanted[j].m_ulItemId = iw.m_ulItemTemplId; pInfo.m_ItemsWanted[j].m_ulItemsToGet = iw.m_ulItemNum; pInfo.m_ItemsWanted[j].m_ulItemsGained = ATaskTempl._get_item_count(this, iw.m_ulItemTemplId, iw.m_bCommonItem); } } else if (pTempl.m_FixedData.m_enumMethod == (uint)TaskCompletionMethod.enumTMKillNumMonster) { int ulItemCount = 0; int ulMonsterCount = 0; for (int j = 0; j < pTempl.m_FixedData.m_ulMonsterWanted; j++) { var mw = pTempl.m_FixedData.m_MonsterWanted[j]; if (mw.m_ulDropItemId != 0) { pInfo.m_ItemsWanted[ulItemCount].m_ulMonsterId = mw.m_ulMonsterTemplId; pInfo.m_ItemsWanted[ulItemCount].m_ulItemId = mw.m_ulDropItemId; pInfo.m_ItemsWanted[ulItemCount].m_ulItemsToGet = mw.m_ulDropItemCount; pInfo.m_ItemsWanted[ulItemCount].m_ulItemsGained = ATaskTempl._get_item_count(this, mw.m_ulDropItemId, mw.m_bDropCmnItem); ulItemCount++; } else { pInfo.m_MonsterWanted[ulMonsterCount].m_ulMonsterId = mw.m_ulMonsterTemplId; pInfo.m_MonsterWanted[ulMonsterCount].m_ulMonstersToKill = mw.m_ulMonsterNum; // Access the entry directly from the list using stored index, like C++ code does if (bActiveTask && foundEntryIndex >= 0) { ActiveTaskEntry CurEntry = pLst.m_TaskEntries[foundEntryIndex]; pInfo.m_MonsterWanted[ulMonsterCount].m_ulMonstersKilled = CurEntry.m_wMonsterNum[j]; } ulMonsterCount++; } } } else if (pTempl.m_FixedData.m_enumMethod == (uint)TaskCompletionMethod.enumTMKillPlayer) { int ulItemCount = 0; int ulPlayerCount = 0; for (int j = 0; j < pTempl.m_FixedData.m_ulPlayerWanted; j++) { var pw = pTempl.m_FixedData.m_PlayerWanted[j]; if (pw.m_ulDropItemId != 0) { pInfo.m_ItemsWanted[ulItemCount].m_ulItemId = pw.m_ulDropItemId; pInfo.m_ItemsWanted[ulItemCount].m_ulItemsToGet = pw.m_ulDropItemCount; pInfo.m_ItemsWanted[ulItemCount].m_ulItemsGained = ATaskTempl._get_item_count(this, pw.m_ulDropItemId, pw.m_bDropCmnItem); ulItemCount++; } else { pInfo.m_PlayerWanted[ulPlayerCount].m_ulPlayersToKill = pw.m_ulPlayerNum; // Access the entry directly from the list using stored index, like C++ code does if (bActiveTask && foundEntryIndex >= 0) { ActiveTaskEntry CurEntry = pLst.m_TaskEntries[foundEntryIndex]; pInfo.m_PlayerWanted[ulPlayerCount].m_ulPlayersKilled = CurEntry.m_wMonsterNum[j]; } pInfo.m_PlayerWanted[ulPlayerCount].m_Requirements = pw.m_Requirements; ulPlayerCount++; } } } else if (pTempl.m_FixedData.m_enumMethod == (uint)TaskCompletionMethod.enumTMProtectNPC) { pInfo.m_ulNPCToProtect = pTempl.m_FixedData.m_ulNPCToProtect; pInfo.m_ulProtectTime = pTempl.m_FixedData.m_ulProtectTimeLen; } else if (pTempl.m_FixedData.m_enumMethod == (uint)TaskCompletionMethod.enumTMWaitTime) { pInfo.m_ulWaitTime = pTempl.m_FixedData.m_ulWaitTime; } else if (pTempl.m_FixedData.m_enumMethod == (uint)TaskCompletionMethod.enumTMGlobalValOK) { pTempl.GetGlobalTaskChar(this, pInfo.m_TaskCharArr); } else if (pTempl.m_FixedData.m_enumMethod == (uint)TaskCompletionMethod.enumTMReachLevel) { pInfo.m_ulReachLevel = pTempl.m_FixedData.m_ulReachLevel; pInfo.m_ulReachReincarnation = pTempl.m_FixedData.m_ulReachReincarnationCount; pInfo.m_ulReachRealm = pTempl.m_FixedData.m_ulReachRealmLevel; } return; } public uint GetCurTime() { // use this to avoid task hack by changing the system time return (uint)EC_Game.GetServerAbsTime(); } private const string SYMBOL_HOSTNAME = "$name"; public string FormatTaskTalk(string taskTalk) { string ret = taskTalk ?? string.Empty; if (taskTalk == null) return ret; string strName = m_pHost.GetName(); // assumes string; use ToString() if needed return ret.Replace(SYMBOL_HOSTNAME, $"&{strName}&"); } public bool GetAwardCandidates(uint ulTaskId, ref AWARD_DATA pAward) { ActiveTaskList pLst = GetActiveTaskList() as ActiveTaskList; ActiveTaskEntry pEntry = pLst.GetEntry(ulTaskId); if (pEntry == null || pEntry.m_ulTemplAddr == 0) return false; uint ulCurTime = GetCurTime(); pEntry.GetTempl().CalcAwardData( this, ref pAward, pEntry, pEntry.m_ulTaskTime, ulCurTime); return true; } public uint GetTaskFinishedTime(uint value) { return 0; } bool IsCaptain() { // TO DO: fix later //CECTeam pTeam = m_pHost.GetTeam(); //if (!pTeam) // return false; //return pTeam.GetLeaderID() == m_pHost.GetCharacterID(); return true; } public bool CanFinishTask(uint ulTaskId) { ActiveTaskEntry pEntry = (GetActiveTaskList() as ActiveTaskList).GetEntry(ulTaskId); if (pEntry == null) return false; ATaskTempl pTempl = pEntry.GetTempl(); if (pTempl == null) return false; if (pTempl.m_FixedData.m_bMarriage && !IsCaptain()) return false; return pTempl.CanFinishTask(this, pEntry, GetCurTime()); } public bool CanShowTask(uint ulTaskId) { ATaskTempl pTempl = GetTaskTemplMan().GetTopTaskByID(ulTaskId); return pTempl != null && pTempl.CanShowTask(this); } public void SetFinishDlgShowTime(int t) { m_tmFinishDlgShown = t; } public void OnUIDialogEnd(uint ulTask) { SetFinishDlgShowTime(0); ActiveTaskList pLst = GetActiveTaskList() as ActiveTaskList; ActiveTaskEntry pEntry = pLst.GetEntry(ulTask); if (pEntry == null || pEntry.m_ulTemplAddr == 0) return; //ATaskTempl pTempl = (pEntry.m_ulTemplAddr) as ATaskTempl; ATaskTempl pTempl = GetTaskTemplMan().GetTaskTemplByID(pEntry.m_ulTemplAddr); switch (pTempl.m_FixedData.m_enumMethod) { case (uint)TaskCompletionMethod.enumTMReachSite: pTempl.IncValidCount(); _notify_svr(this, (byte)ClientNotificationConstants.TASK_CLT_NOTIFY_REACH_SITE, (ushort)ulTask); break; case (uint)TaskCompletionMethod.enumTMLeaveSite: pTempl.IncValidCount(); _notify_svr(this, (byte)ClientNotificationConstants.TASK_CLT_NOTIFY_LEAVE_SITE, (ushort)ulTask); break; } } public void GiveUpTask(uint ulTaskId) { ActiveTaskEntry pEntry = (GetActiveTaskList() as ActiveTaskList).GetEntry(ulTaskId); if (pEntry == null || pEntry.GetTempl() == null) return; _notify_svr(this, 2, (ushort)(pEntry.GetTempl().GetTopTask().GetID())); //TASK_CLT_NOTIFY_CHECK_GIVEUP = 2 } void _notify_svr(TaskInterface pTask, byte uReason, ushort uTaskID) { TaskClient._notify_svr(pTask, uReason, uTaskID); } public bool IsTaskReadyToConfirm(int iTaskID) { if (m_TasksToConfirm == null) return false; bool ret; if (m_TasksToConfirm.TryGetValue(iTaskID, out ret)) return ret; return false; } public bool CheckVersion() { // return static_cast(GetActiveTaskList())->m_Version == TASK_ENTRY_DATA_CUR_VER; return GetActiveTaskList().m_Version == TaskTemplConstants.TASK_ENTRY_DATA_CUR_VER; } public StorageTaskList GetStorageTaskList() { StorageTaskList ret = new StorageTaskList(); // Initialize arrays before use ret.EnsureInitialized(); // Check if buffer is initialized before reading if (m_pStorageTaskListBuf != null) { ret.ReadByte(m_pStorageTaskListBuf); } // If buffer is null, return empty/default StorageTaskList // This can happen if Init() hasn't been called yet or failed return ret; } // 设置存储任务列表缓冲区 // Set storage task list buffer public void SetStorageTaskListBuffer(byte[] data) { if (data == null || m_pStorageTaskListBuf == null) return; int copy = Mathf.Min(data.Length, m_pStorageTaskListBuf.Length); if (copy > 0) { System.Buffer.BlockCopy(data, 0, m_pStorageTaskListBuf, 0, copy); } } public uint GetTaskMask() { return 0; } public byte[] GetFinishedTimeList() { return m_pFinishedTimeListBuf; } public byte[] GetFinishedCntList() { return m_pFinishedCountListBuf; } public void SetForceNavigateFinishFlag(bool bFinish) { m_bForceNavigateFinish = bFinish;} // Set force navigate finish flag // 设置强制导航完成标志 public void OnNewTask(int iTaskID) { UnityEngine.Debug.Log($"[CECTaskInterface] OnNewTask: TaskID={iTaskID}"); ATaskTempl pTempl = GetTaskTemplMan().GetTaskTemplByID((uint)iTaskID); if (pTempl != null && pTempl.m_FixedData.m_enumMethod== (uint)TaskCompletionMethod.enumTMSimpleClientTaskForceNavi) { UnityEngine.Debug.Log($"[CECTaskInterface] OnNewTask: Task {iTaskID} is force navigate task, triggering EM_PREPARE"); SetForceNavigateFinishFlag(false); // Trigger navigation event // 触发导航事件 m_pHost.OnNaviageEvent(iTaskID, (int)CECNavigateCtrl.NavigateEvent.EM_PREPARE); } else { UnityEngine.Debug.Log($"[CECTaskInterface] OnNewTask: Task {iTaskID} is not a force navigate task (pTempl={pTempl != null}, method={pTempl?.m_FixedData.m_enumMethod})"); } } public void OnTaskConfirmUpdate() { // TODO: update task confirm UI // CECGameUIMan* pGameUI = g_pGame->GetGameRun()->GetUIManager()->GetInGameUIMan(); // if (pGameUI) pGameUI->UpdateTaskConfirm(); } public void UpdateConfirmTasksMap() { m_TasksToConfirm.Clear(); TaskClient.OnTaskCheckStatus(this); OnTaskConfirmUpdate(); } public void OnCompleteTask(int iTaskID) { UnityEngine.Debug.Log($"[CECTaskInterface] OnCompleteTask: TaskID={iTaskID}"); ATaskTempl pTempl = GetTaskTemplMan().GetTaskTemplByID((uint)iTaskID); if (pTempl != null && pTempl.m_FixedData.m_enumMethod == (uint)TaskCompletionMethod.enumTMSimpleClientTaskForceNavi) { UnityEngine.Debug.Log($"[CECTaskInterface] OnCompleteTask: Task {iTaskID} is force navigate task, triggering EM_END"); // Trigger navigation end event // 触发导航结束事件 m_pHost.OnNaviageEvent(iTaskID, (int)CECNavigateCtrl.NavigateEvent.EM_END); SetForceNavigateFinishFlag(false); } else { UnityEngine.Debug.Log($"[CECTaskInterface] OnCompleteTask: Task {iTaskID} is not a force navigate task"); } } public void TakeAwayCommonItem(uint ulTemplId, uint ulNum) {} public void TakeAwayTaskItem(uint ulTemplId, uint ulNum) {} public void TakeAwayGold(uint ulNum) {} public void TakeAwayFactionConsumeContrib(int ulNum){} public void OnGiveupTask(int iTaskID) { UnityEngine.Debug.Log($"[CECTaskInterface] OnGiveupTask: TaskID={iTaskID}"); ATaskTempl pTempl = GetTaskTemplMan().GetTaskTemplByID((uint)iTaskID); if (pTempl != null && pTempl.m_FixedData.m_enumMethod == (uint)TaskCompletionMethod.enumTMSimpleClientTaskForceNavi) { UnityEngine.Debug.Log($"[CECTaskInterface] OnGiveupTask: Task {iTaskID} is force navigate task, triggering EM_END"); // Trigger navigation end event // 触发导航结束事件 m_pHost.OnNaviageEvent(iTaskID, (int)CECNavigateCtrl.NavigateEvent.EM_END); SetForceNavigateFinishFlag(false); } else { UnityEngine.Debug.Log($"[CECTaskInterface] OnGiveupTask: Task {iTaskID} is not a force navigate task"); } } // Handle task text click in UI - trigger navigation if it's a force navigate task // 处理任务UI文本点击 - 如果是强制导航任务则触发导航 // This is called when user clicks on task name/link in the task UI // 当用户在任务UI中点击任务名称/链接时调用 public void OnTaskTextClick(int iTaskID) { UnityEngine.Debug.Log($"[CECTaskInterface] OnTaskTextClick: TaskID={iTaskID}"); // Check if task exists and is a force navigate task // 检查任务是否存在且为强制导航任务 ATaskTempl pTempl = GetTaskTemplMan().GetTaskTemplByID((uint)iTaskID); if (pTempl != null && pTempl.m_FixedData.m_enumMethod == (uint)TaskCompletionMethod.enumTMSimpleClientTaskForceNavi) { UnityEngine.Debug.Log($"[CECTaskInterface] OnTaskTextClick: Task {iTaskID} is force navigate task, triggering navigation"); // Check if navigation is already prepared // 检查导航是否已准备 CECHostNavigatePlayer pNavigatePlayer = m_pHost.GetNavigatePlayer(); if (pNavigatePlayer != null && pNavigatePlayer.GetNavigateCtrl() != null) { // If already prepared, trigger begin // 如果已准备,触发开始 if (pNavigatePlayer.GetNavigateCtrl().IsInForceNavigateState()) { UnityEngine.Debug.Log($"[CECTaskInterface] OnTaskTextClick: Navigation already prepared, triggering EM_BEGIN"); m_pHost.OnNaviageEvent(iTaskID, (int)CECNavigateCtrl.NavigateEvent.EM_BEGIN); } else { // Prepare first, then begin // 先准备,然后开始 UnityEngine.Debug.Log($"[CECTaskInterface] OnTaskTextClick: Preparing navigation first, then beginning"); m_pHost.OnNaviageEvent(iTaskID, (int)CECNavigateCtrl.NavigateEvent.EM_PREPARE); // Note: EM_BEGIN will be triggered after preparation is complete // 注意:EM_BEGIN 将在准备完成后触发 // For now, trigger it immediately after a short delay // 目前,在短暂延迟后立即触发 // TODO: Implement proper async handling if needed // TODO: 如果需要,实现适当的异步处理 m_pHost.OnNaviageEvent(iTaskID, (int)CECNavigateCtrl.NavigateEvent.EM_BEGIN); } } else { // Prepare and begin navigation // 准备并开始导航 UnityEngine.Debug.Log($"[CECTaskInterface] OnTaskTextClick: Preparing and beginning navigation"); m_pHost.OnNaviageEvent(iTaskID, (int)CECNavigateCtrl.NavigateEvent.EM_PREPARE); m_pHost.OnNaviageEvent(iTaskID, (int)CECNavigateCtrl.NavigateEvent.EM_BEGIN); } } else { UnityEngine.Debug.Log($"[CECTaskInterface] OnTaskTextClick: Task {iTaskID} is not a force navigate task, will use normal auto-move"); // TODO: Implement normal auto-move behavior here // TODO: 在此处实现正常的自动移动行为 } } public void UpdateTaskUI(uint idTask, int reason) { // TODO: update task UI CECGameUIMan pGameUI = CECUIManager.Instance.GetInGameUIMan(); if (pGameUI != null ) { pGameUI.UpdateTask(idTask, reason); } } public bool IsTitleDataReady() { return m_pHost.IsTitleDataReady(); } public FinishedTaskList GetFinishedTaskList() { FinishedTaskList ret = new FinishedTaskList(); ret.ReadFromBytes(m_pFinishedListBuf); return ret; } // 记录任务完成/失败到已完成列表(用于前置任务/可重复接任务判断) // English: Record task finish/fail into FinishedTaskList (used by prerequisite checks) public void RecordFinishedTask(uint taskId, bool success) { if (m_pFinishedListBuf == null) return; FinishedTaskList lst = new FinishedTaskList(); lst.ReadFromBytes(m_pFinishedListBuf); lst.AddOneTask(taskId, success); // Persist back into buffer if (lst.m_Buf != null && lst.m_Buf.Length == m_pFinishedListBuf.Length) { global::System.Buffer.BlockCopy(lst.m_Buf, 0, m_pFinishedListBuf, 0, m_pFinishedListBuf.Length); } } // Persist an updated FinishedTaskList back into the underlying buffer. public void WriteFinishedTaskList(FinishedTaskList lst) { if (m_pFinishedListBuf == null) return; if (lst.m_Buf == null || lst.m_Buf.Length != m_pFinishedListBuf.Length) return; global::System.Buffer.BlockCopy(lst.m_Buf, 0, m_pFinishedListBuf, 0, m_pFinishedListBuf.Length); } // Reset role-based finish counter for a task when period rolls over (used by CheckDeliverTime). public void ResetRoleFinishCount(uint taskId) { if (m_pFinishedListBuf == null) return; FinishedTaskList lst = new FinishedTaskList(); lst.ReadFromBytes(m_pFinishedListBuf); lst.ResetFinishCount(taskId); WriteFinishedTaskList(lst); } public int GetPlayerId() { return m_pHost.GetCharacterID(); } public void PopChatMessage(int iIndex,int dwNum=0) { switch((FixedMsg)iIndex) { case FixedMsg.FIXMSG_TASK_LIMIT_INCREASED: // TODO: show chat message // g_pGame->GetGameRun()->AddFixedChannelMsg(FIXMSG_TASK_LIMIT_INCREASED,GP_CHAT_SYSTEM); // EC_Game.GetGameRun().AddFixedChannelMsg(FIXMSG_TASK_LIMIT_INCREASED,GP_CHAT_SYSTEM); break; default: break; } } public void NotifyServer(byte[] pBuf, uint sz) { // g_pGame->GetGameSession()->c2s_CmdTaskNotify(pBuf, sz); UnityGameSession.c2s_CmdTaskNotify(pBuf, sz); } #region Emote public void SetEmotion(int emotion) { ActiveTaskList pList = GetActiveTaskList(); List aEntries = new List(pList.m_TaskEntries); ATaskTempl pTempl; if (emotion < (int)TaskInterface.CommandTaskAction.CMD_EMOTION_BINDBUDDY)// pTempl->m_uiEmotion ֵΪ0±íʾ²»¼ì²é£¬ËùÒÔËùÓбíÇéÐòºÅ¶¼ºóÒÆ1 emotion += 1; for (int i = 0; i < pList.m_uTaskCount; i++) { ActiveTaskEntry curEntry = aEntries[i]; pTempl = curEntry.GetTempl(); if (pTempl != null && pTempl.m_FixedData.m_enumMethod == (uint)TaskCompletionMethod.enumTMSimpleClientTask && pTempl.m_FixedData.m_uiEmotion > 0) { uint id = pTempl.GetID(); // Check if map does not contain OR value == false if (!m_emotionTask.TryGetValue(id, out bool exists) || !exists) { m_emotionTask[id] = (pTempl.m_FixedData.m_uiEmotion == (uint)emotion); } } } } // void CECTaskInterface::UpdateTaskEmotionAction(unsigned int task_id) // { // if (m_emotionTask.find(task_id)!=m_emotionTask.end()) // { // m_emotionTask[task_id] = false; // } // } // void CECTaskInterface::UpdateEmotionDlg(unsigned int task) // { // ActiveTaskList* pList = static_cast(GetActiveTaskList()); // ActiveTaskEntry* aEntries = pList->m_TaskEntries; // unsigned char i; // const ATaskTempl* pTempl, *pTarget = NULL; // ActiveTaskEntry* pTargetEntry = NULL; // // for (i = 0; i < pList->m_uTaskCount; i++) // { // ActiveTaskEntry& CurEntry = aEntries[i]; // pTempl = CurEntry.GetTempl(); // // if (!pTempl || pTempl->GetID() == task) // continue; // // if (pTempl && pTempl->m_enumMethod==enumTMSimpleClientTask && pTempl->m_uiEmotion) // { // pTarget = pTempl; // pTargetEntry = &CurEntry; // break; // } // } // if (pTarget && pTargetEntry && !pTempl->CanFinishTask(this, pTargetEntry, GetCurTime())) // { // PopEmotionUI(pTarget->GetID(),pTarget->m_uiEmotion,true); // } // } // void TaskInterface::PopEmotionUI(unsigned int task_id,unsigned int uiEmotion,bool bShow) // { // CECGameUIMan* pGameUI = g_pGame->GetGameRun()->GetUIManager()->GetInGameUIMan(); // // if (pGameUI) // { // pGameUI->PopTaskEmotionDlg(task_id,uiEmotion,bShow); // } // } #endregion } }