using System; using BrewMonster; using ModelRenderer.Scripts.GameData; using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; using PerfectWorld.Scripts.Task; using UnityEngine; namespace BrewMonster.Scripts.Task { /// /// contains and manages all task templates /// init in EC_Game /// public class ATaskTemplMan { public int TaskLoadedCount => m_TaskTemplMap.Count; public const ulong TASK_PACK_MAGIC = 0x93858361; public const ulong _task_templ_cur_version = 121; private ulong g_ulNewCount = 0;// do we need this? // MH: I think not, it look like a debug counter private Dictionary m_TaskTemplMap = new Dictionary(); private Dictionary m_AllTemplMap = new Dictionary(); private Dictionary m_DynTaskMap = new Dictionary(); private Dictionary m_TitleTaskMap = new Dictionary(); private Dictionary m_ExlusiveAwardTaskMap = new Dictionary(); private Dictionary m_ProtectNPCMap = new Dictionary(); private Dictionary m_AutoDelvMap = new Dictionary(); private Dictionary m_DeathTrigMap = new Dictionary(); private Dictionary m_PQTemplMap = new Dictionary(); private Dictionary m_StorageEssenseMap = new Dictionary(); private Dictionary m_WeightEssenseMap = new Dictionary(); private Dictionary m_StorageTaskMap = new Dictionary(); private List m_SkillTaskLst = new List(); private List m_TmLmtChkLst = new List(); private List m_TasksCanSeekOut = new List(); private elementdataman m_pEleDataMan; #if _TASK_CLIENT protected special_award m_SpecialAward; #endif public void Release() { } public void Init(elementdataman pMan) { m_pEleDataMan = pMan; } public bool LoadTasksFromPack(string szPackPath, bool bLoadDescript) { //TaskInterface::WriteLog(0, 0, 2, "LoadPack begin"); BMLogger.Log("[Dat]- szPackPath: " + szPackPath); if (!File.Exists(szPackPath)) { BMLogger.LogError("[Dat]- File not found: " + szPackPath); return false; } long readBytes = 0; FileStream fs = new FileStream(szPackPath, FileMode.Open, FileAccess.Read); TASK_PACK_HEADER tph = AAssit.ReadFromBinaryOf(fs, ref readBytes); if (tph.magic != TASK_PACK_MAGIC || tph.version != _task_templ_cur_version) return false; if (tph.item_count == 0) return true; uint[] pOffs = new uint[tph.item_count]; g_ulNewCount++; // fread(pOffs, sizeof(long), tph.item_count, fp); // read File and prepare offset array before loading tasks pOffs = AAssit.ReadArrayFromBinary(fs, (int)tph.item_count, ref readBytes); Debug.Log((int)tph.item_count); //BMLogger.Log($" [MH] Task File Lenght: {fs.Length}"); // for (int i = 2058; i < 2059; i++) //TODO: tph.item_count Debug.Log($" Starting to load {tph.item_count} task templates..."); for (int i = 0; i < tph.item_count; i++) { // mvoe file pointer to task offset fs.Seek(pOffs[i], SeekOrigin.Begin); // BMLogger.Log(" [MH] Loading Task Templ at offset: " + pOffs[i]); ATaskTempl pTempl = new ATaskTempl(); g_ulNewCount++; // Debug.Log($"Task Index {i}: Attempting to load task template..."); if (!pTempl.LoadFromBinFile(fs)) { CECTaskInterface.WriteLog(0, (int)pTempl.m_FixedData.m_ID, 0, "Cant Load Task"); // LOG_DELETE(pTempl); continue; } AddOneTaskTempl(pTempl); // TaskInterface::WriteLog(0, pTempl->m_ID, 2, "LoadTask"); } Debug.Log($" Finished loading {m_TaskTemplMap.Count} task templates."); // // char log[1024]; // // sprintf(log, "LoadTask, Count = %d", m_TaskTemplMap.size()); // // TaskInterface::WriteLog(0, 0, 2, log); // //todo: check // // LOG_DELETE_ARR(pOffs); fs.Close(); // UpdateTimeLimitCheckList(); #if _ELEMENTCLIENT _task_err.Release(); _task_err.Init("Configs\\task_err.txt", true); #endif return true; } // General method to read a struct from a FileStream private T ReadStruct(FileStream stream) where T : struct { int size = System.Runtime.InteropServices.Marshal.SizeOf(typeof(T)); byte[] buffer = new byte[size]; int bytesRead = stream.Read(buffer, 0, size); if (bytesRead != size) throw new EndOfStreamException("Could not read enough bytes for struct"); var handle = System.Runtime.InteropServices.GCHandle.Alloc(buffer, System.Runtime.InteropServices.GCHandleType.Pinned); try { return (T)System.Runtime.InteropServices.Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T)); } finally { handle.Free(); } } public bool LoadNPCInfoFromPack(string szPath) { return true; } public void VerifyDynTasksPack(string szPath) { } public ATaskTempl GetTopTaskByID(uint ulID) { if (m_TaskTemplMap.TryGetValue(ulID, out ATaskTempl task)) { return task; } return null; } public ATaskTempl GetTaskTemplByID(uint ulID) { if (m_AllTemplMap.TryGetValue((uint)ulID, out ATaskTempl task)) { return task; } return null; } public bool CanGiveUpTask(uint ulTaskId) { var pTempl = GetTaskTemplByID(ulTaskId); if (pTempl == null) return false; pTempl = pTempl.GetTopTask(); return pTempl.m_FixedData.m_bCanGiveUp; } private void AddOneTaskTempl(ATaskTempl pTask) { if (m_TaskTemplMap.ContainsKey(pTask.m_FixedData.m_ID)) { CECTaskInterface.WriteLog(0, (int)pTask.m_FixedData.m_ID, 0, "Dup Task Found"); // Optionally log duplicate task found, e.g.: // Debug.LogWarning($"Duplicate Task Found: {pTempl.m_ID}"); return; } m_TaskTemplMap[pTask.m_FixedData.m_ID] = pTask; if (pTask.m_FixedData.m_bDeathTrig) m_DeathTrigMap[pTask.m_FixedData.m_ID] = pTask; else if (pTask.m_FixedData.m_bAutoDeliver) m_AutoDelvMap[pTask.m_FixedData.m_ID] = pTask; if (pTask.m_FixedData.m_bPQTask) m_PQTemplMap[pTask.m_FixedData.m_ID] = pTask; if (pTask.m_FixedData.m_bSkillTask) m_SkillTaskLst.Add(pTask); //todo: recheck m_DynTaskType type if (pTask.m_FixedData.m_DynTaskType != '\0') { if (m_DynTaskMap.TryGetValue(pTask.m_FixedData.m_ID, out ATaskTempl task)) { CECTaskInterface.WriteLog(0, (int)pTask.m_FixedData.m_ID, 0, "Dup Dyn Task Found"); } m_DynTaskMap[pTask.m_FixedData.m_ID] = pTask; } if (pTask.m_FixedData.m_bDisplayInTitleTaskUI) m_TitleTaskMap[pTask.m_FixedData.m_ID] = pTask; if (pTask.m_FixedData.m_bAutoDeliver && pTask.m_FixedData.m_bDisplayInExclusiveUI) m_ExlusiveAwardTaskMap[pTask.m_FixedData.m_ID] = pTask; #if _TASK_CLIENT if (pTask.m_FixedData.m_ulDelvNPC != 0 && pTask.m_FixedData.m_bCanSeekOut) m_TasksCanSeekOut.Add(pTask); #endif AddTaskToMap(pTask); } private void AddTaskToMap(ATaskTempl pTempl) { if (pTempl.m_FixedData.m_enumMethod == (ulong)TaskCompletionMethod.enumTMProtectNPC && pTempl.m_FixedData.m_ulNPCToProtect > 0) m_ProtectNPCMap[pTempl.m_FixedData.m_ulNPCToProtect] = pTempl; m_AllTemplMap[pTempl.m_FixedData.m_ID] = pTempl; ATaskTempl pChild = pTempl.m_pFirstChild; while (pChild != null) { AddTaskToMap(pChild); pChild = pChild.m_pNextSibling; } } void UpdateTimeLimitCheckList() { m_TmLmtChkLst.Clear(); foreach (var entry in m_TaskTemplMap) { if (entry.Value.m_FixedData.m_ulMaxReceiver != 0) { m_TmLmtChkLst.Add(entry.Value); } } } public bool InitStorageTask() { m_StorageEssenseMap.Clear(); m_WeightEssenseMap.Clear(); DATA_TYPE dt; //ID_SPACE.ID_SPACE_ESSENCE foreach (var pair in m_pEleDataMan.essence_id_data_type_map) { if (pair.Value == DATA_TYPE.DT_NPC_TASK_OUT_SERVICE) { dt = pair.Value; NPC_TASK_OUT_SERVICE pData = (NPC_TASK_OUT_SERVICE)m_pEleDataMan.get_data_ptr(pair.Key, ID_SPACE.ID_SPACE_ESSENCE, ref dt); if (pData.storage_id == 0) continue; if (pData.storage_id > TaskTemplConstants.TASK_STORAGE_COUNT) return false; if (m_StorageEssenseMap.ContainsKey(pData.storage_id)) return false; m_StorageEssenseMap[pData.storage_id] = pData.id; for (var i = 0; i < pData.id_tasks.Length; i++) { if (pData.id_tasks[i] > 0) { m_StorageTaskMap[(int)pData.id_tasks[i]] = (int)pData.storage_id; } } } } // ID_SPACE_CONFIG foreach (var pair in m_pEleDataMan.config_id_data_type_map) { if (pair.Value == DATA_TYPE.DT_NPC_TASK_OUT_SERVICE) { dt = pair.Value; NPC_TASK_OUT_SERVICE pData = (NPC_TASK_OUT_SERVICE)m_pEleDataMan.get_data_ptr(pair.Key, ID_SPACE.ID_SPACE_ESSENCE, ref dt); if (pData.storage_id == 0) continue; if (pData.storage_id > TaskTemplConstants.TASK_STORAGE_COUNT) return false; if (m_StorageEssenseMap.ContainsKey(pData.storage_id)) return false; m_StorageEssenseMap[pData.storage_id] = pData.id; for (var i = 0; i < pData.id_tasks.Length; i++) { if (pData.id_tasks[i] > 0) { m_StorageTaskMap[(int)pData.id_tasks[i]] = (int)pData.storage_id; } } } } return true; } #if _TASK_CLIENT public bool IsTaskToPush(int id) { // TODO: Implement this method properly // int count = m_TasksToPush.size(); // for (size_t i = 0; i < count; ++i) { // ATaskTempl pTempl = m_TasksToPush[i].task; // if (pTempl && (int)pTempl->m_ID == id) return true; // } return false; } // 可接任务列表 // Available tasks list public void GetAvailableTasks(TaskInterface pPlayer, List lst) { if (lst == null) return; if (lst.Capacity < 256) lst.Capacity = 256; // 预留容量 // reserve capacity string log = ""; int count = m_TasksCanSeekOut.Count; for (int i = 0; i < count; i++) { ATaskTempl pTempl = m_TasksCanSeekOut[i]; if (pTempl == null) continue; // 如果等级条件不满足则跳过 // Skip if level requirements are not met if (!pTempl.CheckReachLevel(pPlayer)) continue; // 玩家可接此任务则加入列表 // If player can accept this task, add to list var failCode = pPlayer.CanDeliverTask(pTempl.m_FixedData.m_ID); if (failCode == 0) { lst.Add(pTempl); } else { log += $"Task ID {pTempl.m_FixedData.m_ID} Fail : {failCode} \n"; } // if (i % 1000 == 0) // { // Debug.Log($"--- {i % 1000} Find Available Task --- \n {log}"); // log = ""; // } } } public uint GetTaskStorageId(uint id) { // id��1��ʼ // abase::hash_map::iterator it = m_StorageTaskMap.find(id); // return it == m_StorageTaskMap.end() ? 0 : it->second; return m_StorageTaskMap.ContainsKey((int)id) ? (uint)m_StorageTaskMap[(int)id] : 0; } public void RemoveActiveStorageTask( StorageTaskList pLst, uint id) { // unsigned int set_id = GetTaskTemplMan()->GetTaskStorageId(id); uint set_id = GetTaskStorageId(id); if (set_id > 0) { // unsigned short* arr = pLst->m_Storages[set_id-1]; int start = ((int)set_id - 1) * TaskTemplConstants.TASK_STORAGE_LEN; ushort[] arr = new ushort[TaskTemplConstants.TASK_STORAGE_LEN]; Array.Copy(pLst.m_Storages, start, arr, 0, TaskTemplConstants.TASK_STORAGE_LEN); // int i; for (int i = 0; i < TaskTemplConstants.TASK_STORAGE_LEN; i++) { if (arr[i] == (ushort)id) { arr[i] = 0; break; } } } } // 占位:可接返回0,不可接返回非0 // Placeholder: return 0 if deliverable, non-zero otherwise // private int CanDeliverTask(TaskInterface pPlayer, uint templId) // { // // 后续可替换为正式逻辑 // Replace with real logic later // var impl = pPlayer; // return (impl != null && impl.IsDeliverLegal()) ? 0 : 1; // } public void OnSpecialAward(special_award p,TaskInterface pTask) { m_SpecialAward = p; CheckSpecialAwardMask(pTask); } public void CheckSpecialAwardMask(TaskInterface pTask) { ActiveTaskList pLst = pTask.GetActiveTaskList(); uint ulCurTime = pTask.GetCurTime(); for (int i = 0;i < TaskTemplConstants.NUM_SPECIAL_AWARD;i++) { if ((m_SpecialAward.special_mask & (1 << i)) != 0) { // const ATaskTempl* pTempl = GetTaskTemplByID(static_cast(TASK_SPECIAL_AWARD[i])); C++ var pTempl = GetTaskTemplByID((uint)TaskTemplConstants.TASK_SPECIAL_AWARD[i]); if (pTempl != null && pTempl.CheckPrerequisite(pTask,pLst,ulCurTime) == 0) { // _notify_svr(pTask, TASK_CLT_NOTIFY_SPECIAL_AWARD_MASK,static_cast(pTempl->m_ID)); C++ _notify_svr(pTask, ClientNotificationConstants.TASK_CLT_NOTIFY_SPECIAL_AWARD_MASK,(ushort)(pTempl.m_FixedData.m_ID)); } } } } private void _notify_svr(TaskInterface pPlayer, byte uiNotifyType, ushort idData) { ATaskTempl._notify_svr(pPlayer, uiNotifyType, idData); } public void CheckAutoDelv(TaskInterface pTask) { ATaskTempl pTempl = null; // TaskTemplMap::iterator it = m_AutoDelvMap.begin(); var it = m_AutoDelvMap[0]; uint ulCurTime = pTask.GetCurTime(); ActiveTaskList pLst = pTask.GetActiveTaskList(); #if _ELEMENTLOCALIZE #if _ELEMENTCLIENT // if (CECCommandLine::GetBriefConfig(_AL("noautodelivertask"))) return; #endif #endif // C++ // for (; it != m_AutoDelvMap.end(); ++it) // { // pTempl = it->second; // // if (!pTempl->IsValidState()) // continue; // // if (pTempl->CheckPrerequisite(pTask, pLst, ulCurTime) == 0) // { // pTempl->IncValidCount(); // _notify_svr(pTask, TASK_CLT_NOTIFY_AUTO_DELV, static_cast(pTempl->m_ID)); // } // } foreach (var key in m_AutoDelvMap.Keys) { pTempl = m_AutoDelvMap[key]; if (!pTempl.IsValidState()) continue; if (pTempl.CheckPrerequisite(pTask, pLst, ulCurTime) == 0) { pTempl.IncValidCount(); _notify_svr(pTask, ClientNotificationConstants.TASK_CLT_NOTIFY_AUTO_DELV, (ushort)(pTempl.m_FixedData.m_ID)); } } } public void UpdateStatus(TaskInterface pTask) { // ��ΪCheckTitle������ƺ����ݣ�����ֱ����ȡ���ƺ�����֮ǰ�����ܵ��������� if (!pTask.IsTitleDataReady()) return; CheckAutoDelv(pTask); // TODO: Implement other checks as needed // if (CECUIConfig::Instance().GetGameUI().bEnableTitle) // CheckTitleTask(pTask); // UpdateTasksSeekOutDiff(pTask); } // extarn from TaskServer private void OnTaskGiveUpOneTask(TaskInterface pTask, uint ulTaskId, bool bForce) { TaskServer.OnTaskGiveUpOneTask(pTask, ulTaskId, bForce); } #endif public void OnForgetLivingSkill(TaskInterface pTask) { // FinishedTaskList* pList = static_cast(pTask->GetFinishedTaskList()); // C++ FinishedTaskList pList = pTask.GetFinishedTaskList(); for (int i = 0; i < m_SkillTaskLst.Count; i++) { pList.RemoveTask(m_SkillTaskLst[i].GetID()); #if _TASK_CLIENT OnTaskGiveUpOneTask(pTask, m_SkillTaskLst[i].GetID(), false); #endif } #if _TASK_CLIENT task_notify_base notify = new task_notify_base(); notify.reason = TaskTemplConstants.TASK_SVR_NOTIFY_FORGET_SKILL; notify.task = 0; pTask.NotifyClient(notify, Marshal.SizeOf()); #endif } } }