using System; using System.Runtime.InteropServices; using UnityEngine; using BrewMonster.Network; using BrewMonster.Scripts.Task; using CSNetwork.GPDataType; using PerfectWorld.Scripts.Task; namespace BrewMonster.Scripts.Task { public partial class ATaskTempl { public uint GetID() { return m_FixedData.m_ID; } // Process Part public uint CheckPrerequisite( TaskInterface pTask, ActiveTaskList pList, uint ulCurTime, bool bCheckPrevTask = true, bool bCheckTeam = true, bool bCheckBudge = true, bool bCheckLevel = true) { uint ulRet = 0u; // �����Ǹ��ڵ� // English: Not the root node if (m_pParent != null) { ulRet = (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_NOT_ROOT; goto ret_here; } if (bCheckBudge) { ulRet = CheckBudget(pList); if (ulRet != 0u) goto ret_here; } // else if (pList->GetEntry(m_ID)) // English: Already has the same task else if (pList.GetEntry(m_FixedData.m_ID) != null) { ulRet = (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_SAME_TASK; goto ret_here; } /*else if (pTask.HasTask(m_FixedData.m_ID)) { ulRet = (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_SAME_TASK; goto ret_here; }*/ // �Ƿ����� // English: Is task forbidden if (pTask.CheckTaskForbid(m_FixedData.m_ID)) { ulRet = (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_TASK_FORBID; goto ret_here; } // ��ΪPQ�����ж������Ƿ��Ѿ���PQ���� // English: If current is PQ task, ensure no existing PQ task if (m_FixedData.m_bPQTask) { for (byte i = 0; i < pList.m_uTaskCount; i++) { ActiveTaskEntry CurEntry = pList.m_TaskEntries[i]; if (CurEntry == null) continue; ATaskTempl pTempl = CurEntry.GetTempl(); if (pTempl != null && pTempl.m_FixedData.m_bPQTask) { ulRet = (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_ALREADY_HAS_PQ; goto ret_here; } } } ulRet = CheckGivenItems(pTask); if (ulRet != 0u) goto ret_here; // �Ƿ����㷢��ʱ�� // English: Check if within delivery timetable ulRet = CheckTimetable(ulCurTime); if (ulRet != 0u) goto ret_here; // ����ʱ���� // English: Check deliver time ulRet = CheckDeliverTime(pTask, ulCurTime); if (ulRet != 0u) goto ret_here; ulRet = CheckFnshLst(pTask, ulCurTime); if (ulRet != 0u) goto ret_here; // �Ƿ�ﵽ�˺�������ɴ��� // English: Check daily deliver count limit ulRet = CheckDeliverCount(pTask); if (ulRet != 0u) goto ret_here; // �Ƿ�ﵽ��ֵ��� // English: Check RMB/account balance ulRet = CheckAccountRMB(pTask); if (ulRet != 0u) goto ret_here; // �Ƿ������ɫ������¼ʱ������ // English: Check character time requirements ulRet = CheckCharTime(pTask); if (ulRet != 0u) goto ret_here; // Check Level // English: Level constraints if (bCheckLevel) { ulRet = CheckLevel(pTask); if (ulRet != 0u) goto ret_here; } // ת������ // English: Reincarnation constraints ulRet = CheckReincarnation(pTask); if (ulRet != 0u) goto ret_here; // ����ȼ� // English: Realm level constraints ulRet = CheckRealmLevel(pTask); if (ulRet != 0u) goto ret_here; // ���羭���Ƿ����� // English: Realm exp full check ulRet = CheckRealmExpFull(pTask); if (ulRet != 0u) goto ret_here; // ���� // English: Reputation check ulRet = CheckRepu(pTask); if (ulRet != 0u) goto ret_here; // Ѻ�� // English: Deposit check ulRet = CheckDeposit(pTask); if (ulRet != 0u) goto ret_here; // ������Ʒ // English: Items check ulRet = CheckItems(pTask); if (ulRet != 0u) goto ret_here; // ���� // English: Faction check ulRet = CheckFaction(pTask); if (ulRet != 0u) goto ret_here; // �Ա� // English: Gender check ulRet = CheckGender(pTask); if (ulRet != 0u) goto ret_here; // ְҵ // English: Occupation check ulRet = CheckOccupation(pTask); if (ulRet != 0u) goto ret_here; // �����ض�ʱ�� // English: Specific period check ulRet = CheckPeriod(pTask); if (ulRet != 0u) goto ret_here; // �Ƿ�GM // English: GM check ulRet = CheckGM(pTask); if (ulRet != 0u) goto ret_here; // �Ƿ���������û� // English: Shielded user check ulRet = CheckShieldUser(pTask); if (ulRet != 0u) goto ret_here; // �������� // English: Previous task check if (bCheckPrevTask) { ulRet = CheckPreTask(pTask); if (ulRet != 0u) goto ret_here; } // �������� // English: Mutex task check ulRet = CheckMutexTask(pTask, ulCurTime); if (ulRet != 0u) goto ret_here; // ���򴥷� // English: In-zone trigger check ulRet = CheckInZone(pTask); if (ulRet != 0u) goto ret_here; // ������� // English: Team task check if (bCheckTeam) { ulRet = CheckTeamTask(pTask); if (ulRet != 0u) goto ret_here; } // ���� // English: Spouse check ulRet = CheckSpouse(pTask); if (ulRet != 0u) goto ret_here; ulRet = CheckWeddingOwner(pTask); if (ulRet != 0u) goto ret_here; // ���� // English: Marriage check ulRet = CheckMarriage(pTask); if (ulRet != 0u) goto ret_here; // ����������� // English: Living skill check ulRet = CheckLivingSkill(pTask); if (ulRet != 0u) goto ret_here; // �������� // English: Special award activity check ulRet = CheckSpecialAward(pTask); if (ulRet != 0u) goto ret_here; // ȫ��key/value // English: Global key/value ulRet = CheckGlobalKeyValue(pTask, false); if (ulRet != 0u) goto ret_here; if (m_FixedData.m_bCompareItemAndInventory) { ulRet = CheckIvtrEmptySlot(pTask); if (ulRet != 0u) goto ret_here; } // ���ɹ��׶� // English: Faction contribution check ulRet = CheckFactionContrib(pTask); if (ulRet != 0u) goto ret_here; // ��¼��ɽ��������ɸ��� // English: Recorded tasks number check ulRet = CheckRecordTasksNum(pTask); if (ulRet != 0u) goto ret_here; // ����״̬ // English: Transform mask check ulRet = CheckTransform(pTask); if (ulRet != 0u) goto ret_here; // ���� // English: Force check ulRet = CheckForce(pTask); if (ulRet != 0u) goto ret_here; // �������� // English: Force reputation check ulRet = CheckForceReputation(pTask); if (ulRet != 0u) goto ret_here; // ����ս�� // English: Force contribution check ulRet = CheckForceContribution(pTask); if (ulRet != 0u) goto ret_here; // ����ֵ // English: EXP check ulRet = CheckExp(pTask); if (ulRet != 0u) goto ret_here; // Ԫ��ֵ // English: SP check ulRet = CheckSP(pTask); if (ulRet != 0u) goto ret_here; // ������Ծ�ȼ� // English: Force activity level check ulRet = CheckForceActivityLevel(pTask); if (ulRet != 0u) goto ret_here; // ���� // English: King check ulRet = CheckKing(pTask); if (ulRet != 0u) goto ret_here; // ������� // English: Not in team check ulRet = CheckNotInTeam(pTask); if (ulRet != 0u) goto ret_here; // �ƺ� // English: Title check ulRet = CheckTitle(pTask); if (ulRet != 0u) goto ret_here; // ��ʷ�ƽ��׶� // English: History stage check ulRet = CheckHistoryStage(pTask); if (ulRet != 0u) goto ret_here; // �ռ����� // English: Card collection count check ulRet = CheckCardCollection(pTask); if (ulRet != 0u) goto ret_here; // ����ijƷ��ӵ������ // English: Specific card rank count check ulRet = CheckCardRankCount(pTask); if (ulRet != 0u) goto ret_here; // �����󣬲��ܽ�ȡǿ���ƶ����� // English: No navigate when in transform shape ulRet = CheckInTransformShape(pTask); if (ulRet != 0u) goto ret_here; ret_here: // TODO: pTask.GetPlayerId() not available in managed interface; use 0 // Only log failures to reduce noise if (ulRet != 0) { string log = $"CheckPrerequisite Task {m_FixedData.m_ID}: FAIL code {ulRet}"; CECTaskInterface.WriteLog(0, (int)m_FixedData.m_ID, 1, log); } return ulRet; } #if _TASK_CLIENT // bool CanShowTask (TaskInterface* pTask) const; // bool HasShowCond() const; public void GetGlobalTaskChar(TaskInterface pTask, Task_State_info.abase_vector_wchar_t_ptr TaskCharArr) { // Build display strings for global expressions // 生成用于显示的全局表达式字符串 for (int i = 0; i < (int)m_FixedData.m_ulTaskCharCnt; i++) { // Read one UTF-16 row from m_pTaskChar and convert to C# string // 从 m_pTaskChar 读取一行 UTF-16 并转换为 C# 字符串 int colCount = BrewMonster.Scripts.Task.TaskTemplConstants.TASK_AWARD_MAX_DISPLAY_CHAR_LEN; ushort[] row = new ushort[colCount]; for (int j = 0; j < colCount; j++) { row[j] = m_FixedData.m_pTaskChar[i, j]; } string src = ModelRenderer.Scripts.Common.ByteToStringUtils.UshortArrayToUnicodeString(row); if (string.IsNullOrEmpty(src)) { continue; } // Parse '%' placeholders like %1, %2... and replace with computed values // 解析形如 %1, %2 的占位符并替换为计算结果 var sb = new System.Text.StringBuilder(); int idx = 0; char percent = '%'; // Percent sign // ����% (percent sign) while (idx < src.Length) { int pos = src.IndexOf(percent, idx); if (pos >= 0) { // Append text before '%' // 追加 '%' 之前的文本 sb.Append(src, idx, pos - idx); int j = pos + 1; int startDigits = j; while (j < src.Length && char.IsDigit(src[j])) j++; string digits = (j > startDigits) ? src.Substring(startDigits, j - startDigits) : string.Empty; int nVal; // If there's a valid index after '%', compute the global expression value // ��ȡ���õ�ȫ�ֱ���ʽ�к� (get referenced global expression index) if (!string.IsNullOrEmpty(digits) && int.TryParse(digits, out nVal) && nVal != 0) { // Compute one global expression // ����ȫ�ֱ���ʽ int nRet = (int)CalcOneGlobalExp(pTask, nVal - 1); // Convert number to string and append // ת�����ַ��������ַ��� sb.Append(nRet.ToString()); // Skip all digits after '%' // 跳过 '%' 后面的所有数字 idx = j; continue; } else { // No valid number found; keep the '%' // 未找到合法数字;保留 '%' sb.Append(percent); idx = pos + 1; } } else { // No more '%' found; append the rest // δ�ҵ�ȫ�ֱ�������ʽ����ֱ����ʾ�������� sb.Append(src, idx, src.Length - idx); break; } } string result = sb.ToString(); if (!string.IsNullOrEmpty(result)) { // Original: TaskCharArr.push_back(pszNewchar); // 原逻辑:将结果指针压入向量(此处托管环境不直接推入原生向量) // NOTE: If needed, map 'result' into UI/managed list here. } } } private float CalcOneGlobalExp(TaskInterface pTask, int nIndex) { // Evaluate one global expression row // 计算一行全局表达式 if (nIndex < 0 || nIndex >= (int)m_FixedData.m_ulExpCnt) { return 0f; } try { int colCount = BrewMonster.Scripts.Task.TaskTemplConstants.TASK_AWARD_MAX_DISPLAY_CHAR_LEN; float result = 0f; for (int i = 0; i < colCount; i++) { TASK_EXPRESSION expr = m_FixedData.m_pExpArr[nIndex, i]; if (expr.type == -1) break; // Sentinel terminator // 结束标记 // Fallback evaluation: accumulate values // 回退求值:累加表达式值 result += expr.value; } return result; } catch (System.Exception ex) { CECTaskInterface.WriteLog(0, (int)m_FixedData.m_ID, 0, $"CalcOneGlobalExp, Expression run err: {ex.Message}"); return 0f; } } // bool CanShowInExclusiveUI (TaskInterface* pTask, unsigned long ulCurTime) const; #if !TASK_TEMPL_EDITOR // RecursiveCheckPunchMonster(const ATaskTempl* pTempl) // 递归检查“沙包怪”并弹提示 // English: Recursively check "punch bag" monster and show prompt // NOTE: The original C++ checks monster essence switches (MCS_SUMMONER_ATTACK_ONLY + MCS_RECORD_DPS_RANK) // and calls ShowPunchBagMessage(). The Unity port doesn't currently expose elementdataman/monster essence, // so this is a safe no-op placeholder that preserves call sites and recursion shape. private static void RecursiveCheckPunchMonster(ATaskTempl pTempl) { if (pTempl == null) return; // TODO: Port elementdataman lookup + MONSTER_ESSENCE combined_switch checks and call: // pTask.ShowPunchBagMessage(false,false,monsterId,0,0); var child = pTempl.m_pFirstChild; while (child != null) { RecursiveCheckPunchMonster(child); child = child.m_pNextSibling; } } #endif public void OnServerNotify( TaskInterface pTask, ActiveTaskEntry pEntry, task_notify_base pNotify, uint sz, byte[] pBuf) // In C++ pNotify is a pointer to raw data; here we pass the full byte array for parsing { uint ulTime = 0, ulCaptainTask = 0; ActiveTaskList pLst = null; ATaskTempl pSub = new ATaskTempl(); task_sub_tags sub_tags = new task_sub_tags(); // memset(&sub_tags, 0, sizeof(sub_tags)); uint i=0; svr_monster_killed pKilled = new svr_monster_killed(); svr_player_killed pKilledPlayer = new svr_player_killed(); StorageTaskList pStorage = pTask.GetStorageTaskList(); svr_treasure_map pTreasure = new svr_treasure_map(); var m_enumMethod = m_FixedData.m_enumMethod; switch (pNotify.reason) { case TaskTemplConstants.TASK_SVR_NOTIFY_PLAYER_KILLED: { if (sz != Marshal.SizeOf()) break; if (m_enumMethod != (uint)TaskCompletionMethod.enumTMKillPlayer) break; pKilledPlayer = GPDataTypeHelper.FromBytes(pBuf); int iIndex = pKilledPlayer.index; if (iIndex < TaskInterfaceConstants.MAX_MONSTER_WANTED) { pEntry.SetMonsterNum(iIndex, pKilledPlayer.player_num); } } break; case TaskTemplConstants.TASK_SVR_NOTIFY_TREASURE_MAP: if (m_enumMethod == (uint)TaskCompletionMethod.enumTMReachTreasureZone) { pTreasure = GPDataTypeHelper.FromBytes(pBuf); pEntry.m_iUsefulData1 = pTreasure.treasure_index; } break; case TaskTemplConstants.TASK_SVR_NOTIFY_MONSTER_KILLED: if (sz != Marshal.SizeOf()) break; if (m_enumMethod != (uint)TaskCompletionMethod.enumTMKillNumMonster) break; pKilled = GPDataTypeHelper.FromBytes(pBuf); for (i = 0; i < m_FixedData.m_ulMonsterWanted; i++) { MONSTER_WANTED mw = m_FixedData.m_MonsterWanted[i]; if (mw.m_ulMonsterTemplId == pKilled.monster_id) { pEntry.SetMonsterNum((int)i, pKilled.monster_num); if (pKilled.dps > 0 && pKilled.dph > 0) { pTask.ShowPunchBagMessage(true,pKilled.dps >= mw.m_iDPS && pKilled.dph >= mw.m_iDPH,pKilled.monster_id,pKilled.dps,pKilled.dph); } break; } } break; case TaskTemplConstants.TASK_SVR_NOTIFY_NEW: // Follow C++ logic: valid_size checks the buffer size before deserializing // In C++, pNotify is a pointer to the structure, so valid_size can read sub_tags.sz directly // In C#, we need to read sub_tags.sz from the buffer first, then validate // Calculate base size: task_notify_base (3) + cur_time (4) + cap_task (4) = 11 int baseSzNew = Marshal.SizeOf() + 8; if (sz <= (uint)baseSzNew) { Debug.Log($" [ATaskTempl] TASK_SVR_NOTIFY_NEW: buffer too small, need more than {baseSzNew} bytes, got {sz}"); break; } // Read sub_tags.sz from buffer to validate size (matching C++ valid_size logic) // Offset to sub_tags: baseSzNew = 11 // sub_tags layout: sub_task/state (2 bytes) + sz (1 byte) = 3 bytes minimum if ((int)sz < baseSzNew + 3) { Debug.Log($" [ATaskTempl] TASK_SVR_NOTIFY_NEW: buffer too small to read sub_tags.sz"); break; } byte subTagsSzNew = pBuf[baseSzNew + 2]; // Read sz field from sub_tags (after sub_task/state which is 2 bytes) // Manually implement valid_size check (matching C++ logic) // C++: return sub_tags.valid_size(sz - base_sz); // sub_tags.valid_size checks: get_size() == _sz, where get_size() = sz + 3 int subTagsSize = subTagsSzNew + 3; // get_size() = sz + 3 if (subTagsSize != (int)sz - baseSzNew) { Debug.Log($" [TASK_SVR_NOTIFY_NEW] the size of byte not meet !!! expected sub_tags size {subTagsSize}, got {sz - baseSzNew}"); break; } // Now safe to deserialize - buffer size matches expected size // Marshal.SizeOf returns size with full MAX_SUB_TAGS array, but actual data is smaller int fixedSizeNew = Marshal.SizeOf(); svr_new_task svr_new_task; if (sz < (uint)fixedSizeNew) { // Buffer is smaller than fixed structure size, create padded buffer for safe deserialization byte[] paddedBufNew = new byte[fixedSizeNew]; Array.Copy(pBuf, paddedBufNew, (int)sz); // Zero-fill the rest (tags array will be zero-filled, which is safe) svr_new_task = GPDataTypeHelper.FromBytes(paddedBufNew); // Restore the original sub_tags.sz value since padding might have corrupted it svr_new_task.sub_tags.sz = subTagsSzNew; } else { // Buffer is large enough for fixed structure size svr_new_task = GPDataTypeHelper.FromBytes(pBuf); } pLst = pTask.GetActiveTaskList(); svr_new_task.get_data( ref ulTime, ref ulCaptainTask, ref sub_tags ); // NOTE: Disabled for now due to an ambiguous call error being reported by the Unity compiler in this project setup. // TODO: Re-enable once the underlying duplicate/assembly ambiguity is resolved. // GetTaskTemplMan().RemoveActiveStorageTask(pStorage, m_FixedData.m_ID); if (sub_tags.sub_task > 0) { pSub = GetConstSubById(sub_tags.sub_task); if (pSub == null) break; } else pSub = null; if (CheckBudget(pLst) > 0) break; DeliverTask( pTask, pLst, null, ulCaptainTask, pTask.GetTaskMask(), ulTime, pSub, ref sub_tags, new TaskGlobalData(), 0xFF); if (m_FixedData.m_lAvailFrequency != (int)TaskAwardFreq.enumTAFNormal && !m_FixedData.m_bAccountTaskLimit && !m_FixedData.m_bRoleTaskLimit) { // static_cast(pTask->GetFinishedTimeList())->AddOrUpdate( // m_ID, // ulTime); var TaskFinishTimeList = new TaskFinishTimeList(); TaskFinishTimeList.ReadFromBuffer(pTask.GetFinishedTimeList()); TaskFinishTimeList.AddOrUpdate(m_FixedData.m_ID, ulTime); } // TODO: Log new task acceptance // if (CanShowPrompt() && !m_bDisplayInTitleTaskUI) TaskInterface::ShowTaskMessage(m_ID, TASK_MSG_NEW); // �������׷�ٷ��������������ʱ�� if (!m_FixedData.m_bHidden && !m_FixedData.m_bDisplayInTitleTaskUI) // TaskInterface::TraceTask(m_ID); // TODO: Log task trace // pTask.TraceTask(m_FixedData.m_ID); if (m_FixedData.m_bDisplayInTitleTaskUI) // TODO: Update title UI // TaskInterface::UpdateTitleUI(m_ID); if ((m_enumMethod == (uint)TaskCompletionMethod.enumTMSimpleClientTask) && m_FixedData.m_uiEmotion > 0) // TODO: Pop emotion UI // TaskInterface::PopEmotionUI(m_ID,m_uiEmotion,true); pTask.OnNewTask((int)m_FixedData.m_ID); break; case TaskTemplConstants.TASK_SVR_NOTIFY_COMPLETE: // Follow C++ logic: valid_size checks the buffer size before deserializing // In C++, pNotify is a pointer to the structure, so valid_size can read sub_tags.sz directly // In C#, we need to read sub_tags.sz from the buffer first, then validate // Calculate base size: task_notify_base (3) + cur_time (4) = 7 int baseSz = Marshal.SizeOf() + 4; if (sz <= (uint)baseSz) { Debug.Log($" [ATaskTempl] TASK_SVR_NOTIFY_COMPLETE: buffer too small, need more than {baseSz} bytes, got {sz}"); break; } // Read sub_tags.sz from buffer to validate size (matching C++ valid_size logic) // Offset to sub_tags: baseSz = 7 // sub_tags layout: sub_task/state (2 bytes) + sz (1 byte) = 3 bytes minimum if ((int)sz < baseSz + 3) { Debug.Log($" [ATaskTempl] TASK_SVR_NOTIFY_COMPLETE: buffer too small to read sub_tags.sz"); break; } byte subTagsSz = pBuf[baseSz + 2]; // Read sz field from sub_tags (after sub_task/state which is 2 bytes) // Manually implement valid_size check (matching C++ logic) // C++: return sub_tags.valid_size(sz - base_sz); // sub_tags.valid_size checks: get_size() == _sz, where get_size() = sz + 3 int subTagsSizeComplete = subTagsSz + 3; // get_size() = sz + 3 if (subTagsSizeComplete != (int)sz - baseSz) { Debug.Log($" [TASK_SVR_NOTIFY_COMPLETE] the size of byte not meet !!! expected sub_tags size {subTagsSizeComplete}, got {sz - baseSz}"); break; } // Now safe to deserialize - buffer size matches expected size // Marshal.SizeOf returns size with full MAX_SUB_TAGS array, but actual data is smaller int fixedSize = Marshal.SizeOf(); svr_task_complete svr_task_complete; if (sz < (uint)fixedSize) { // Buffer is smaller than fixed structure size, create padded buffer for safe deserialization byte[] paddedBuf = new byte[fixedSize]; Array.Copy(pBuf, paddedBuf, (int)sz); // Zero-fill the rest (tags array will be zero-filled, which is safe) svr_task_complete = GPDataTypeHelper.FromBytes(paddedBuf); // Restore the original sub_tags.sz value since padding might have corrupted it svr_task_complete.sub_tags.sz = subTagsSz; } else { // Buffer is large enough for fixed structure size svr_task_complete = GPDataTypeHelper.FromBytes(pBuf); } svr_task_complete.get_data( ref ulTime, ref sub_tags ); pEntry.m_uState = (char)svr_task_complete.sub_tags.state; if (!pEntry.IsSuccess()) { #if !TASK_TEMPL_EDITOR RecursiveCheckPunchMonster(this); #endif } // TODO: Log task completion // if (CanShowPrompt() && !m_bDisplayInTitleTaskUI) TaskInterface::ShowTaskMessage( // m_ID, // (pEntry->IsSuccess() && !pEntry->IsGiveUp()) ? TASK_MSG_SUCCESS : TASK_MSG_FAIL); RecursiveAward( pTask, pTask.GetActiveTaskList(), pEntry, ulTime, -1, ref sub_tags); pTask.UpdateConfirmTasksMap(); // TODO: Show task completion trace // pTask.UpdateTaskEmotionAction(m_FixedData.m_ID); if ((m_enumMethod == (uint)TaskCompletionMethod.enumTMSimpleClientTask) && m_FixedData.m_uiEmotion > 0) // TODO: Pop emotion UI // PopEmotionUI(m_ID,m_uiEmotion,false); if (m_FixedData.m_bDisplayInTitleTaskUI) // TODO: Update title UI // UpdateTitleUI(m_ID); pTask.OnCompleteTask((int)m_FixedData.m_ID); break; case TaskTemplConstants.TASK_SVR_NOTIFY_GIVE_UP: pLst = pTask.GetActiveTaskList(); // Use simplified removal to keep list consistent in the managed port pLst.RemoveEntry(pEntry); // TODO: Log task give up // if (m_bDisplayInTitleTaskUI) TaskInterface::UpdateTitleUI(m_ID); // if ((m_enumMethod == enumTMSimpleClientTask) && m_uiEmotion) // TaskInterface::PopEmotionUI(m_ID,m_uiEmotion,false); pTask.OnGiveupTask((int)m_FixedData.m_ID); break; case TaskTemplConstants.TASK_SVR_NOTIFY_FINISHED: pEntry.SetFinished(); break; case TaskTemplConstants.TASK_SVR_NOTIFY_DIS_GLOBAL_VAL: // DisplayTaskCharInfo(pTask, pEntry); break; default: // assert(false); break; } // Update task UI after processing server notification pTask.UpdateTaskUI(pNotify.task, pNotify.reason); } public static void _notify_svr(TaskInterface pTask, byte uReason, ushort uTaskID) { task_notify_base notify = new task_notify_base(); notify.reason = uReason; notify.task = uTaskID; // pTask.NotifyServer( notify, (uint)Marshal.SizeOf()); pTask.NotifyServer( GPDataTypeHelper.ToBytes(notify), (uint)Marshal.SizeOf()); } #else // void NotifyClient (TaskInterface* pTask, const ActiveTaskEntry* pEntry, unsigned char uReason, unsigned long ulCurTime, unsigned long ulParam = 0, int dps = 0, int dph = 0) const; // bool CheckGlobalRequired (TaskInterface* pTask, unsigned long ulSubTaskId, const TaskPreservedData* pPreserve, const TaskGlobalData* pGlobal, unsigned short reason) const; // bool CheckKillMonster (TaskInterface* pTask, ActiveTaskList* pList, ActiveTaskEntry* pEntry, unsigned long ulTemplId, unsigned long ulLev, bool bTeam, float fRand, int dps, int dph) const; // bool CheckKillPlayer (TaskInterface* pTask, ActiveTaskList* pList, ActiveTaskEntry* pEntry, int iOccupation, int iLevel, bool bGender, int iForce, float fRand) const; // void CheckCollectItem (TaskInterface* pTask, ActiveTaskList* pList, ActiveTaskEntry* pEntry, bool bAtNPC, int nChoice) const; // void CheckMonsterKilled (TaskInterface* pTask, ActiveTaskList* pList, ActiveTaskEntry* pEntry, bool bAtNPC, int nChoice) const; // void CheckMining (TaskInterface* pTask, ActiveTaskList* pList, ActiveTaskEntry* pEntry) const; // void CheckWaitTime (TaskInterface* pTask, ActiveTaskList* pList, ActiveTaskEntry* pEntry, unsigned long ulCurTime, bool bAtNPC, int nChoice) const; // void GiveUpOneTask (TaskInterface* pTask, ActiveTaskList* pList, ActiveTaskEntry* pEntry, bool bForce) const; // void OnSetFinished (TaskInterface* pTask, ActiveTaskList* pList, ActiveTaskEntry* pEntry, bool bNotifyMem = true) const; // bool DeliverAward (TaskInterface* pTask, ActiveTaskList* pList, ActiveTaskEntry* pEntry, int nChoice, bool bNotifyTeamMem = true, TaskGlobalData* pGlobal = NULL) const; // void RemoveAcquiredItem (TaskInterface* pTask, bool bClearTask, bool bSuccess) const; // void TakeAwayGivenItems (TaskInterface* pTask) const; // bool OnDeliverTeamMemTask (TaskInterface* pTask, TaskGlobalData* pGlobal) const; // unsigned long CheckDeliverTask (TaskInterface* pTask, unsigned long ulSubTaskId, TaskGlobalData* pGlobal, bool bNotifyErr = true, bool bMemTask = false, unsigned long ulCapId = 0) const; // bool HasGlobalData() const; #endif #if _TASK_CLIENT // void SyncTaskType(); // ʹ����������������游���� // bool GetShowGfxFlag() { return m_bShowGfxFinished;} // const wchar_t* GetDescription() const { assert(m_pwstrDescript); return (wchar_t*)m_pwstrDescript; } // const wchar_t* GetOkText() const { assert(m_pwstrOkText); return (wchar_t*)m_pwstrOkText; } // const wchar_t* GetNoText() const { assert(m_pwstrNoText); return (wchar_t*)m_pwstrNoText; } public ushort[] GetTribute() { return m_pwstrTribute; } // public talk_proc GetDeliverTaskTalk() { return m_DelvTaskTalk; } public talk_proc GetUnqualifiedTalk() { return m_UnqualifiedTalk; } public talk_proc GetDeliverItemTalk() { return m_DelvItemTalk; } public talk_proc GetUnfinishedTalk() { return m_ExeTalk; } public talk_proc GetAwardTalk() { return m_AwardTalk; } // public uint GetDeliverNPC() { return m_FixedData.m_ulDelvNPC; } public uint GetAwardNPC() { return m_FixedData.m_ulAwardNPC; } // // void SaveToTextFile(FILE* fp); // bool SaveToTextFile(const char* szPath); // void SaveToBinFile(FILE* fp) { SaveBinary(fp); } // void SaveDescription(FILE* fp); // void SaveDescriptionBin(FILE* fp); // void SaveTribute(FILE* fp); // void SaveTributeBin(FILE* fp); // void SaveAllText(FILE* fp); // int MarshalKillMonster(char* pData); // int MarshalCollectItems(char* pData); // int MarshalDynTask(char* pData); // int MarshalSpecialAwardData(char* pData); // // ATaskTempl& operator= (const ATaskTempl& src); // bool operator == (const ATaskTempl& src) const // { // return *(ATaskTemplFixedData*)this == *(const ATaskTemplFixedData*)&src; // } #endif public bool _compare_key_value(TaskInterface pTask, COMPARE_KEY_VALUE CompKeyVal) { long lleftValue = CompKeyVal.lLeftNum; if (CompKeyVal.nLeftType == 0) { lleftValue = pTask.GetGlobalValue(CompKeyVal.lLeftNum); } long lRightValue = CompKeyVal.lRightNum; if (CompKeyVal.nRightType == 0) { lRightValue = pTask.GetGlobalValue(CompKeyVal.lRightNum); } switch(CompKeyVal.nCompOper) { case 0: { if (lleftValue > lRightValue) return true; } break; case 1: { if (lleftValue == lRightValue) return true; } break; case 2: { if (lleftValue < lRightValue) return true; } break; default: break; } return false; } // Early out: skip if this phase does not require key-value comparison public uint CheckGlobalKeyValue(TaskInterface pTask, bool bFinCheck) { if ((bFinCheck && !m_FixedData.m_bFinNeedComp) || (!bFinCheck && !m_FixedData.m_bPremNeedComp)) { return 0; } // Initialize comparison flags bool bFlag1 = false; bool bFlag2 = false; if (bFinCheck) { // Finish-conditions branch: evaluate two key-value comparisons bFlag1 = _compare_key_value(pTask, m_FixedData.m_Fin1KeyValue); bFlag2 = _compare_key_value(pTask, m_FixedData.m_Fin2KeyValue); // Evaluate logical mode: 0 = OR, 1 = AND if ((m_FixedData.m_nFinExp1AndOrExp2 == 0 && (bFlag1 || bFlag2)) || (m_FixedData.m_nFinExp1AndOrExp2 == 1 && (bFlag1 && bFlag2))) { return 0; } } else { // Premise-conditions branch: evaluate two key-value comparisons bFlag1 = _compare_key_value(pTask, m_FixedData.m_Prem1KeyValue); bFlag2 = _compare_key_value(pTask, m_FixedData.m_Prem2KeyValue); // Evaluate logical mode: 0 = OR, 1 = AND if ((m_FixedData.m_nPremExp1AndOrExp2 == 0 && (bFlag1 || bFlag2)) || (m_FixedData.m_nPremExp1AndOrExp2 == 1 && (bFlag1 && bFlag2))) { return 0; } } // Failure: global key-value prerequisite not satisfied return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_GLOBAL_KEYVAL; } public bool CheckReachLevel(BrewMonster.Scripts.Task.TaskInterface pTask) { bool bLevel = true, bReincarnationCount = true, bRealmLevel = true; if (m_FixedData.m_ulReachLevel != 0) bLevel = pTask.GetPlayerLevel() >= m_FixedData.m_ulReachLevel; if (m_FixedData.m_ulReachReincarnationCount != 0) bReincarnationCount = GetReincarnationCount(pTask) >= m_FixedData.m_ulReachReincarnationCount; if (m_FixedData.m_ulReachRealmLevel != 0) bRealmLevel = GetRealmLevel(pTask) >= m_FixedData.m_ulReachRealmLevel; return bLevel && bReincarnationCount && bRealmLevel; } private static uint GetReincarnationCount(BrewMonster.Scripts.Task.TaskInterface pTask) { return 0u; } private static uint GetRealmLevel(BrewMonster.Scripts.Task.TaskInterface pTask) { var host = BrewMonster.Network.EC_Game.GetGameRun()?.GetHostPlayer(); if (host != null) { var bp = host.GetBasicProps(); return (uint)bp.iLevel2; } return 0u; } public bool IsKeyTask() { ATaskTempl m = GetTopTask(); return m.m_FixedData.m_bKeyTask; } // ===== Missing methods converted from C++ (TaskTempl.inl) ===== // 保留原中文注释,并在旁加入英文翻译 // inline unsigned long _get_item_count(TaskInterface* pTask, unsigned long ulItemId, bool bCommon) // 获取道具数量(通用/任务) // English: Get item count (common/task) // public static uint _get_item_count(TaskInterface pTask, uint ulItemId, bool bCommon) // { // return bCommon ? pTask.GetCommonItemCount(ulItemId) : pTask.GetTaskItemCount(ulItemId); // } // inline unsigned long ATaskTempl::CheckBudget(ActiveTaskList* pList) const // 检查任务栏容量与空间 // English: Check task list budget and space public uint CheckBudget(ActiveTaskList pList) { // Convert full logic with TASK_HIDDEN_COUNT/TASK_TITLE_TASK_COUNT/TASK_ACTIVE_LIST_MAX_LEN and list counters when constants and fields are available // // 占位返回通过 // English: Placeholder pass // return 0u; var m_bHidden = m_FixedData.m_bHidden; var m_bDisplayInTitleTaskUI = m_FixedData.m_bDisplayInTitleTaskUI; var m_ID = m_FixedData.m_ID; // ����ﵽ���� bool bReachLimit = false; if (m_bHidden) bReachLimit = pList.m_uTopHideTaskCount >= TASK_.TASK_HIDDEN_COUNT; else if (m_bDisplayInTitleTaskUI) bReachLimit = bReachLimit || pList.m_uTitleTaskCount >= TASK_.TASK_TITLE_TASK_COUNT; else bReachLimit = bReachLimit || pList.m_uTopShowTaskCount >= pList.GetMaxSimultaneousCount(); if (bReachLimit) return TaskInterfaceConstants.TASK_PREREQU_FAIL_FULL; // Check Task List Empty Space if (pList.m_uUsedCount + m_uDepth > TaskInterfaceConstants.TASK_ACTIVE_LIST_MAX_LEN) return TaskInterfaceConstants.TASK_PREREQU_FAIL_NO_SPACE; // �Ƿ�������ͬ���� if (pList.GetEntry(m_ID) != null) return TaskInterfaceConstants.TASK_PREREQU_FAIL_SAME_TASK; return 0; } // inline unsigned long ATaskTempl::CheckGivenItems(TaskInterface* pTask) const // 检查交付所需道具容量合法性 // English: Check deliverable item capacity public uint CheckGivenItems(TaskInterface pTask) { if (m_FixedData.m_ulGivenItems != 0) { if (!pTask.IsDeliverLegal()) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_GIVEN_ITEM; if (m_FixedData.m_ulGivenCmnCount != 0 && !pTask.CanDeliverCommonItem(m_FixedData.m_ulGivenCmnCount) || m_FixedData.m_ulGivenTskCount != 0 && !pTask.CanDeliverTaskItem(m_FixedData.m_ulGivenTskCount)) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_GIVEN_ITEM; } return 0u; } // inline unsigned long ATaskTempl::CheckTimetable(unsigned long ulCurTime) const // 检查任务可接时间表 // English: Check task timetable window public uint CheckTimetable(uint ulCurTime) { if (m_FixedData.m_ulTimetable == 0) return 0; // TODO: Implement judge_time_date function to properly check time windows // C++ logic: if ANY timetable entry matches current time, return 0; else return TASK_PREREQU_FAIL_WRONG_TIME // For now, since judge_time_date is not implemented, we allow the task to pass // This is a temporary workaround - proper implementation should check each timetable entry // WARNING: This may allow tasks to show when they shouldn't, but prevents blocking valid tasks return 0u; } // inline unsigned long ATaskTempl::CheckDeliverTime(TaskInterface* pTask, unsigned long ulCurTime) const // 检查任务发放频率限制(日/周/月/年) // English: Check deliver frequency limits (day/week/month/year) public uint CheckDeliverTime(TaskInterface pTask, uint ulCurTime) { // C++: if (m_lAvailFrequency == enumTAFNormal) return 0; if (m_FixedData.m_lAvailFrequency == (int)TaskAwardFreq.enumTAFNormal) return 0u; // Basic implementation: Check if task was never completed // If task was never completed (Search returns 0), allow it // TODO: Full implementation should also check time periods (daily/weekly/monthly/yearly) // and compare last completion time with current time byte[] finishedTimeListBuf = pTask.GetFinishedTimeList(); if (finishedTimeListBuf == null || finishedTimeListBuf.Length == 0) { // No finished time list, task was never completed, allow it return 0u; } TaskFinishTimeList pTimeList = new TaskFinishTimeList(); pTimeList.ReadFromBuffer(finishedTimeListBuf); // Check if list is full if (pTimeList.m_uCount >= TaskInterfaceConstants.TASK_FINISH_TIME_MAX_LEN) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_FULL; // Search for task completion time uint ulTaskTime = pTimeList.Search(m_FixedData.m_ID); // If task was never completed (Search returns 0), allow it if (ulTaskTime == 0) return 0u; // Task was completed - TODO: Check if within same period based on frequency // For now, return error to prevent showing tasks that were already completed // This is a temporary fix - proper implementation should check time periods return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_WRONG_TIME; } // inline unsigned long ATaskTempl::CheckFnshLst(TaskInterface* pTask, unsigned long ulCurTime) const // 检查完成与失败记录是否允许重复领取 // English: Check finished/failed list for redo permissions public uint CheckFnshLst(TaskInterface pTask, uint ulCurTime) { // Get top task ID - finished task list stores top-level task IDs // 获取顶层任务ID - 已完成任务列表存储的是顶层任务ID ATaskTempl pTopTempl = GetTopTask(); uint taskIdToCheck = pTopTempl != null ? pTopTempl.m_FixedData.m_ID : m_FixedData.m_ID; // Use top task's redo flags // 使用顶层任务的重做标志 bool bCanRedo = pTopTempl != null ? pTopTempl.m_FixedData.m_bCanRedo : m_FixedData.m_bCanRedo; bool bCanRedoAfterFailure = pTopTempl != null ? pTopTempl.m_FixedData.m_bCanRedoAfterFailure : m_FixedData.m_bCanRedoAfterFailure; // If both redo flags are true, task can always be redone, skip check // 如果两个重做标志都为true,任务总是可以重做,跳过检查 if (bCanRedo && bCanRedoAfterFailure) return 0u; FinishedTaskList pFinished = (FinishedTaskList)pTask.GetFinishedTaskList(); int nRet = pFinished.SearchTask(taskIdToCheck); // nRet: -1 = not found, 0 = successfully completed, 1 = failed // 成功完成不能接任务,或失败后不能重新接任务 // If successfully completed and can't redo, or failed and can't redo after failure if ((nRet == 0 && !bCanRedo) || (nRet == 1 && !bCanRedoAfterFailure)) { // Debug: Log why task is blocked string taskName = GetTaskName(pTopTempl ?? this); UnityEngine.Debug.Log($"[CheckFnshLst] Task {taskIdToCheck} '{taskName}' blocked: nRet={nRet}, bCanRedo={bCanRedo}, bCanRedoAfterFailure={bCanRedoAfterFailure}"); return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_CANT_REDO; } return 0u; } // Helper to get task name for debugging private string GetTaskName(ATaskTempl pTempl) { if (pTempl == null || pTempl.m_FixedData.m_szName == null) return "Unknown"; try { return ModelRenderer.Scripts.Common.ByteToStringUtils.UshortArrayToUnicodeString(pTempl.m_FixedData.m_szName); } catch { return "Error"; } } // inline unsigned long ATaskTempl::CheckDeliverCount(TaskInterface* pTask) const // 检查周期内角色/账号次数限制 // English: Check period deliver counts for role/account public uint CheckDeliverCount(TaskInterface pTask) { if (m_FixedData.m_bAccountTaskLimit && m_FixedData.m_lPeriodLimit != 0) { if (pTask.IsAtCrossServer()) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_CROSSSERVER_NO_ACOUNT_LIMIT; byte[] finishedCntListBuf = pTask.GetFinishedCntList(); TaskFinishCountList pFnshList = new TaskFinishCountList(); pFnshList.ReadFromBytes(finishedCntListBuf); uint ulTemp = 0; uint nRet = pFnshList.Search(m_FixedData.m_ID, ref ulTemp); if ((uint)nRet >= m_FixedData.m_lPeriodLimit) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_MAX_ACC_CNT; } else if (m_FixedData.m_bRoleTaskLimit && m_FixedData.m_lPeriodLimit != 0) { FinishedTaskList pFnshList = (FinishedTaskList)pTask.GetFinishedTaskList(); long finish_count = pFnshList.SearchTaskFinishCount(m_FixedData.m_ID); if (finish_count >= m_FixedData.m_lPeriodLimit) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_MAX_ROLE_CNT; } return 0u; } // inline unsigned long ATaskTempl::CheckAccountRMB(TaskInterface* pTask) const // 检查账号现金(RMB)范围 // English: Check account total cash (RMB) range public uint CheckAccountRMB(TaskInterface pTask) { if (m_FixedData.m_ulPremRMBMin != 0u && m_FixedData.m_ulPremRMBMax != 0u) { uint total = pTask.GetAccountTotalCash(); if (total < m_FixedData.m_ulPremRMBMin || total > m_FixedData.m_ulPremRMBMax) { return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_RMB_NOT_ENOUGH; } } return 0u; } // inline unsigned long ATaskTempl::CheckCharTime(TaskInterface* pTask) const // 检查角色创建/登录时间窗口 // English: Check character time window public uint CheckCharTime(TaskInterface pTask) { // TODO: Implement using m_FixedData.m_bCharTime, m_iCharStartTime/m_iCharEndTime, m_tmCharEndTime and pTask time APIs return 0u; } // inline unsigned long ATaskTempl::CheckLevel(TaskInterface* pTask) const // 等级上下限 // English: Level min/max public uint CheckLevel(TaskInterface pTask) { uint level = m_FixedData.m_bPremCheckMaxHistoryLevel > 0 ? pTask.GetMaxHistoryLevel() : pTask.GetPlayerLevel(); if (m_FixedData.m_ulPremise_Lev_Min != 0u && level < m_FixedData.m_ulPremise_Lev_Min) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_BELOW_LEVEL; if (m_FixedData.m_ulPremise_Lev_Max != 0u && level > m_FixedData.m_ulPremise_Lev_Max) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_ABOVE_LEVEL; return 0u; } // inline unsigned long ATaskTempl::CheckReincarnation(TaskInterface* pTask) const // 转生次数上下限 // English: Reincarnation count min/max public uint CheckReincarnation(TaskInterface pTask) { if (m_FixedData.m_bPremCheckReincarnation) { uint count = pTask.GetReincarnationCount(); if (count < m_FixedData.m_ulPremReincarnationMin) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_BELOW_REINCARNATION; if (count > m_FixedData.m_ulPremReincarnationMax) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_ABOVE_REINCARNATION; } return 0u; } // inline unsigned long ATaskTempl::CheckRealmLevel(TaskInterface* pTask) const // 修真境界等级 // English: Realm level public uint CheckRealmLevel(TaskInterface pTask) { if (m_FixedData.m_bPremCheckRealmLevel) { uint level = GetRealmLevel(pTask); if (level < m_FixedData.m_ulPremRealmLevelMin) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_BELOW_REALMLEVEL; if (level > m_FixedData.m_ulPremRealmLevelMax) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_ABOVE_REALMLEVEL; } return 0u; } // inline unsigned long ATaskTempl::CheckRealmExpFull(TaskInterface* pTask) const // 修真经验是否已满 // English: Realm EXP full check public uint CheckRealmExpFull(TaskInterface pTask) { if (m_FixedData.m_bPremCheckRealmExpFull) { if (!pTask.IsRealmExpFull()) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_REALM_EXP_FULL; } return 0u; } // inline unsigned long ATaskTempl::CheckRepu(TaskInterface* pTask) const // 声望上下限 // English: Reputation min/max public uint CheckRepu(TaskInterface pTask) { if (m_FixedData.m_lPremise_Reputation != 0 && pTask.GetReputation() < (uint)m_FixedData.m_lPremise_Reputation) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_BELOW_REPU; if (m_FixedData.m_lPremise_RepuMax != 0 && pTask.GetReputation() > (uint)m_FixedData.m_lPremise_RepuMax) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_BELOW_REPU; return 0u; } // inline unsigned long ATaskTempl::CheckDeposit(TaskInterface* pTask) const // 寄存金(金币) // English: Deposit (gold) public uint CheckDeposit(TaskInterface pTask) { if (m_FixedData.m_ulPremise_Deposit != 0u && pTask.GetGoldNum() < m_FixedData.m_ulPremise_Deposit) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_NO_DEPOSIT; return 0u; } // inline unsigned long ATaskTempl::CheckItems(TaskInterface* pTask) const // 前置道具检查(任一/全部) // English: Prerequisite item check (any/ALL) public uint CheckItems(TaskInterface pTask) { uint i = 0; uint ret = m_FixedData.m_bPremItemsAnyOne ? (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_NO_ITEM : 0; for (; i < m_FixedData.m_ulPremItems; i++) { ITEM_WANTED wi = m_FixedData.m_PremItems[i]; if (m_FixedData.m_bPremItemsAnyOne) { if (_get_item_count(pTask, wi.m_ulItemTemplId, wi.m_bCommonItem) >= wi.m_ulItemNum) { ret = 0; break; } } else { if (_get_item_count(pTask, wi.m_ulItemTemplId, wi.m_bCommonItem) < wi.m_ulItemNum) { ret = (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_NO_ITEM; break; } } } return ret; } // inline unsigned long ATaskTempl::CheckFaction(TaskInterface* pTask) const // 家族/帮派及职位 // English: Faction/clan and role public uint CheckFaction(TaskInterface pTask) { int role = pTask.GetFactionRole(); bool roleOk = role <= m_FixedData.m_iPremise_FactionRole; if (m_FixedData.m_ulPremise_Faction != 0u && !(pTask.IsInFaction(m_FixedData.m_ulPremise_Faction) && roleOk)) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_CLAN; return 0u; } // inline unsigned long ATaskTempl::CheckGender(TaskInterface* pTask) const // 性别 // English: Gender public uint CheckGender(TaskInterface pTask) { bool isMale = pTask.IsMale(); if (m_FixedData.m_ulGender == TaskTemplConstants.TASK_GENDER_MALE && !isMale) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_WRONG_GENDER; if (m_FixedData.m_ulGender == TaskTemplConstants.TASK_GENDER_FEMALE && isMale) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_WRONG_GENDER; return 0u; } // inline unsigned long ATaskTempl::CheckOccupation(TaskInterface* pTask) const // 职业 // English: Occupation public uint CheckOccupation(TaskInterface pTask) { if (m_FixedData.m_ulOccupations == 0u) return 0u; uint current = pTask.GetPlayerOccupation(); for (uint i = 0; i < m_FixedData.m_ulOccupations; i++) { if (m_FixedData.m_Occupations[i] == current) return 0u; } return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_NOT_IN_OCCU; } // inline unsigned long ATaskTempl::CheckPeriod(TaskInterface* pTask) const // 历练阶段(区间) // English: Period check (interval) public uint CheckPeriod(TaskInterface pTask) { uint cur = pTask.GetCurPeriod(); if (cur < m_FixedData.m_ulPremise_Period) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_WRONG_PERIOD; if (m_FixedData.m_ulPremise_Period < 20u) return 0u; if (m_FixedData.m_ulPremise_Period < 30u) return cur < 30u ? 0u : (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_WRONG_PERIOD; if (m_FixedData.m_ulPremise_Period < 40u) return cur < 40u ? 0u : (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_WRONG_PERIOD; return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_WRONG_PERIOD; } // inline unsigned long ATaskTempl::CheckGM(TaskInterface* pTask) const // GM 限制 // English: GM check public uint CheckGM(TaskInterface pTask) { return m_FixedData.m_bGM ? (pTask.IsGM() ? 0u : (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_GM) : 0u; } // inline unsigned long ATaskTempl::CheckShieldUser(TaskInterface* pTask) const // 屏蔽用户 // English: Shielded user public uint CheckShieldUser(TaskInterface pTask) { return m_FixedData.m_bShieldUser ? (pTask.IsShieldUser() ? 0u : (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_SHIELD_USER) : 0u; } // inline unsigned long ATaskTempl::CheckPreTask(TaskInterface* pTask) const // 前置任务 // English: Previous tasks public uint CheckPreTask(TaskInterface pTask) { uint i; FinishedTaskList pFinished = (FinishedTaskList)pTask.GetFinishedTaskList(); uint iPremTaskFinishedCount = 0; for (i = 0; i < m_FixedData.m_ulPremise_Task_Count; i++) { if (m_FixedData.m_ulPremise_Task_Least_Num == 0) { if (pFinished.SearchTask(m_FixedData.m_ulPremise_Tasks[i]) != 0) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_PREV_TASK; } else if (pFinished.SearchTask(m_FixedData.m_ulPremise_Tasks[i]) == 0) { iPremTaskFinishedCount++; } } return m_FixedData.m_ulPremise_Task_Least_Num != 0 ? (iPremTaskFinishedCount < m_FixedData.m_ulPremise_Task_Least_Num ? (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_PREV_TASK : 0) : 0; } // (No inline in provided snippet) 互斥任务 // English: Mutex task public uint CheckMutexTask(TaskInterface pTask, uint ulCurTime) { uint i; FinishedTaskList pFinished = (FinishedTaskList)pTask.GetFinishedTaskList(); uint iPremTaskFinishedCount = 0; for (i = 0; i < m_FixedData.m_ulPremise_Task_Count; i++) { if (m_FixedData.m_ulPremise_Task_Least_Num == 0) { if (pFinished.SearchTask(m_FixedData.m_ulPremise_Tasks[i]) != 0) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_PREV_TASK; } else if (pFinished.SearchTask(m_FixedData.m_ulPremise_Tasks[i]) == 0) { iPremTaskFinishedCount++; } } return m_FixedData.m_ulPremise_Task_Least_Num != 0 ? (iPremTaskFinishedCount < m_FixedData.m_ulPremise_Task_Least_Num ? (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_PREV_TASK : 0) : 0; } // inline unsigned long ATaskTempl::CheckInZone(TaskInterface* pTask) const // 区域检查 // English: In-zone check public uint CheckInZone(TaskInterface pTask) { if (m_FixedData.m_bDelvInZone) { float[] pos = new float[3]; uint ulWorldId = (uint)pTask.GetPos(pos); /* if (ulWorldId != m_ulDelvWorld || !is_in_zone( m_DelvMinVert, m_DelvMaxVert, pos)) return TASK_PREREQU_FAIL_NOT_IN_ZONE;*/ if(ulWorldId != m_FixedData.m_ulDelvWorld) return (uint) TaskInterfaceConstants.TASK_PREREQU_FAIL_NOT_IN_ZONE; for (uint i=0; i < m_FixedData.m_ulDelvRegionCnt; i++) { Task_Region t = m_FixedData.m_pDelvRegion[i]; if(is_in_zone(t.zvMin,t.zvMax,pos)) return 0; } return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_NOT_IN_ZONE; } return 0u; } // inline unsigned long ATaskTempl::CheckTeamTask(TaskInterface* pTask) const // 组队接任务要求 // English: Team-task requirements public uint CheckTeamTask(TaskInterface pTask) { // if (m_FixedData.m_bTeamwork && m_FixedData.m_bRcvByTeam) // ��ӽ��� // { // if (!pTask.IsCaptain()) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_NOT_CAPTAIN; // return HasAllTeamMemsWanted(pTask, true); // } return 0u; } // inline unsigned long ATaskTempl::CheckSpouse(TaskInterface* pTask) const // 配偶要求 // English: Spouse requirement public uint CheckSpouse(TaskInterface pTask) { if (m_FixedData.m_bPremise_Spouse && !pTask.IsMarried()) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_INDETERMINATE; return 0u; } // inline unsigned long ATaskTempl::CheckWeddingOwner(TaskInterface* pTask) const // 婚礼举办者检查 // English: Wedding owner check public uint CheckWeddingOwner(TaskInterface pTask) { if (m_FixedData.m_bPremiseWeddingOwner) { if (pTask.IsAtCrossServer()) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_CROSSSERVER_NO_MARRIAGE; if (!pTask.IsWeddingOwner()) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_WEDDING_OWNER; } return 0u; } // inline unsigned long ATaskTempl::CheckMarriage(TaskInterface* pTask) const // 结婚任务条件 // English: Marriage task requirements public uint CheckMarriage(TaskInterface pTask) { //TODO: /*if (m_FixedData.m_bMarriage) { if (pTask.IsAtCrossServer()) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_CROSSSERVER_NO_MARRIAGE; if (pTask.IsMarried()) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_ILLEGAL_MEM; if (!pTask.IsInTeam()) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_ILLEGAL_MEM; if (pTask.GetTeamMemberNum() != 2) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_ILLEGAL_MEM; task_team_member_info m1, m2; pTask.GetTeamMemberInfo(0, &m1); pTask.GetTeamMemberInfo(1, &m2); if (m1.m_bMale == m2.m_bMale) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_ILLEGAL_MEM; }*/ return 0u; } // inline unsigned long ATaskTempl::CheckLivingSkill(TaskInterface* pTask) const // 生活技能等级 // English: Living skill level public uint CheckLivingSkill(TaskInterface pTask) { // TODO: Loop m_FixedData.m_lSkillLev[MAX_LIVING_SKILLS] with skill ids and pTask.HasLivingSkill/GetLivingSkillLevel // unsigned long i; // // for (i = 0; i < MAX_LIVING_SKILLS; i++) // { // if (m_lSkillLev[i] == 0) // continue; // // if (!pTask.HasLivingSkill(_living_skill_ids[i]) // || pTask->GetLivingSkillLevel(_living_skill_ids[i]) < m_lSkillLev[i]) // return TASK_PREREQU_FAIL_LIVING_SKILL; // } return 0u; } // inline unsigned long ATaskTempl::CheckIvtrEmptySlot(TaskInterface* pTask) const // 背包空位检查 // English: Inventory empty slot check public uint CheckIvtrEmptySlot(TaskInterface pTask) { if (!m_FixedData.m_bCompareItemAndInventory) return 0u; if (pTask.GetInvEmptySlot() < m_FixedData.m_ulInventorySlotNum) { return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_NOT_IVTRSLOTNUM; } return 0u; } // inline unsigned long ATaskTempl::CheckFactionContrib(TaskInterface* pTask) const // 家族贡献 // English: Faction contribution public uint CheckFactionContrib(TaskInterface pTask) { int contrib = pTask.GetFactionContrib(); if (m_FixedData.m_iPremiseFactionContrib != 0 && contrib < m_FixedData.m_iPremiseFactionContrib) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_BELOW_FACTION_CONTRIB; return 0u; } // inline unsigned long ATaskTempl::CheckRecordTasksNum(TaskInterface* pTask) const // 完成记录任务数量 // English: Finished-record tasks count public uint CheckRecordTasksNum(TaskInterface pTask) { // TODO: Implement using FinishedTaskList scanning for recorded tasks count return 0u; } // inline unsigned long ATaskTempl::CheckTransform(TaskInterface* pTask) const // 变身形态 // English: Transform mask public uint CheckTransform(TaskInterface pTask) { byte playerShapeType = pTask.GetShapeMask(); // 0xFF 不限制 // English: 0xFF = no restriction if (m_FixedData.m_ucPremiseTransformedForm == 0xFF) return 0u; // 职业变身 // English: Occupation transform if (m_FixedData.m_ucPremiseTransformedForm == 0x80) { if ((playerShapeType >> 6) != 2) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_TRANSFORM_MASK; return 0u; } // 指定形态 // English: Specific form if (m_FixedData.m_ucPremiseTransformedForm != playerShapeType) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_TRANSFORM_MASK; return 0u; } // inline unsigned long ATaskTempl::CheckForce(TaskInterface* pTask) const // 势力检查 // English: Force check public uint CheckForce(TaskInterface pTask) { if (m_FixedData.m_bPremCheckForce) { if (pTask.IsAtCrossServer()) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_CROSSSERVER_NO_FORCE; if (m_FixedData.m_iPremForce == -1) { if (pTask.GetForce() == 0) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_FORCE; } else if (pTask.GetForce() != m_FixedData.m_iPremForce) { return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_FORCE; } } return 0u; } // inline unsigned long ATaskTempl::CheckForceReputation(TaskInterface* pTask) const // 势力声望 // English: Force reputation public uint CheckForceReputation(TaskInterface pTask) { if (m_FixedData.m_iPremForceReputation != 0) { if (pTask.IsAtCrossServer()) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_CROSSSERVER_NO_FORCE; if (pTask.GetForceReputation() < m_FixedData.m_iPremForceReputation) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_FORCE_REPUTATION; } return 0u; } // inline unsigned long ATaskTempl::CheckForceContribution(TaskInterface* pTask) const // 势力贡献 // English: Force contribution public uint CheckForceContribution(TaskInterface pTask) { if (m_FixedData.m_iPremForceContribution != 0) { if (pTask.IsAtCrossServer()) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_CROSSSERVER_NO_FORCE; if (pTask.GetForceContribution() < m_FixedData.m_iPremForceContribution) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_FORCE_REPUTATION; } return 0u; } // inline unsigned long ATaskTempl::CheckExp(TaskInterface* pTask) const // 势力经验 // English: Force EXP public uint CheckExp(TaskInterface pTask) { if (m_FixedData.m_iPremForceExp != 0) { if (pTask.IsAtCrossServer()) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_CROSSSERVER_NO_FORCE; if (pTask.GetExp() < m_FixedData.m_iPremForceExp) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_EXP; } return 0u; } // inline unsigned long ATaskTempl::CheckSP(TaskInterface* pTask) const // 势力SP // English: Force SP public uint CheckSP(TaskInterface pTask) { if (m_FixedData.m_iPremForceSP != 0) { if (pTask.IsAtCrossServer()) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_CROSSSERVER_NO_FORCE; if (pTask.GetSP() < m_FixedData.m_iPremForceSP) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_SP; } return 0u; } // inline unsigned long ATaskTempl::CheckForceActivityLevel(TaskInterface* pTask) const // 势力活跃等级 // English: Force activity level public uint CheckForceActivityLevel(TaskInterface pTask) { if (m_FixedData.m_iPremForceActivityLevel != -1) { if (pTask.IsAtCrossServer()) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_CROSSSERVER_NO_FORCE; if (m_FixedData.m_iPremForceActivityLevel != pTask.GetForceActivityLevel()) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_FORCE_AL; } return 0u; } // inline unsigned long ATaskTempl::CheckKing(TaskInterface* pTask) const // 王检查 // English: King check public uint CheckKing(TaskInterface pTask) { if (m_FixedData.m_bPremIsKing && !pTask.IsKing()) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_KING; return 0u; } // inline unsigned long ATaskTempl::CheckNotInTeam(TaskInterface* pTask) const // 不在队伍 // English: Not in team public uint CheckNotInTeam(TaskInterface pTask) { if (m_FixedData.m_bPremNotInTeam && pTask.IsInTeam()) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_IN_TEAM; return 0u; } // inline unsigned long ATaskTempl::CheckTitle(TaskInterface* pTask) const // 称号检查 // English: Title check public uint CheckTitle(TaskInterface pTask) { if (m_FixedData.m_iPremTitleNumTotal != 0) { int iNumRequired = (int)m_FixedData.m_iPremTitleNumTotal; if (m_FixedData.m_iPremTitleNumRequired > 0 && m_FixedData.m_iPremTitleNumRequired < m_FixedData.m_iPremTitleNumTotal) iNumRequired = (int)m_FixedData.m_iPremTitleNumRequired; int iTitleCount = 0; for (uint i = 0; i < m_FixedData.m_iPremTitleNumTotal; ++i) { if (pTask.HaveGotTitle((uint)m_FixedData.m_PremTitles[i])) iTitleCount++; } if (iTitleCount < iNumRequired) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_TITLE; } if (m_Award_S.m_ulTitleNum != 0) { for (uint i = 0; i < m_Award_S.m_ulTitleNum; ++i) { if (pTask.HaveGotTitle(m_Award_S.m_pTitleAward[i].m_ulTitleID)) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_TITLE; } } if (m_Award_F.m_ulTitleNum != 0) { for (uint i = 0; i < m_Award_F.m_ulTitleNum; ++i) { if (pTask.HaveGotTitle(m_Award_F.m_pTitleAward[i].m_ulTitleID)) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_TITLE; } } return 0u; } // inline unsigned long ATaskTempl::CheckHistoryStage(TaskInterface* pTask) const // 历史阶段 // English: History stage public uint CheckHistoryStage(TaskInterface pTask) { int index = pTask.GetCurHistoryStageIndex(); if (m_FixedData.m_iPremHistoryStageIndex[0] != 0 && (index <=0 || index < m_FixedData.m_iPremHistoryStageIndex[0])) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_HISTORYSTAGE; if (m_FixedData.m_iPremHistoryStageIndex[1] != 0 && (index <=0 || index > m_FixedData.m_iPremHistoryStageIndex[1])) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_HISTORYSTAGE; return 0u; } // inline unsigned long ATaskTempl::CheckCardCollection(TaskInterface* pTask) const // 将星卡收集数量 // English: General card collection count public uint CheckCardCollection(TaskInterface pTask) { uint count = pTask.GetObtainedGeneralCardCount(); if (m_FixedData.m_ulPremGeneralCardCount != 0 && (count < m_FixedData.m_ulPremGeneralCardCount)) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_CARD_COUNT_COLLECTION; return 0u; } // inline unsigned long ATaskTempl::CheckCardRankCount(TaskInterface* pTask) const // 指定品级将星卡数量 // English: Specific rank general card count public uint CheckCardRankCount(TaskInterface pTask) { if (m_FixedData.m_iPremGeneralCardRank >= 0 && m_FixedData.m_ulPremGeneralCardRankCount != 0) { uint count = pTask.GetObtainedGeneralCardCountByRank(m_FixedData.m_iPremGeneralCardRank); if (count < m_FixedData.m_ulPremGeneralCardRankCount) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_CARD_COUNT_RANK; } return 0u; } // inline unsigned long ATaskTempl::CheckInTransformShape(TaskInterface* pTask) const // 变身中禁止导航 // English: No navigate while in shaped public uint CheckInTransformShape(TaskInterface pTask) { if (m_FixedData.m_enumMethod == (uint)TaskCompletionMethod.enumTMSimpleClientTaskForceNavi && pTask.GetShapeMask() != 0) { return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_NO_NAVIGATE_INSHPAED; } return 0u; } // 特殊活动奖励检查 // English: Special award activity check public uint CheckSpecialAward(TaskInterface pTask) { // 非“特殊活动奖励”类型则直接通过 // English: Pass if not a special-award dynamic task if (m_FixedData.m_DynTaskType != (byte)DynTaskType.enumDTTSpecialAward) return 0u; // 未配置特殊奖励标识则失败 // English: Fail if special award id not configured if (m_FixedData.m_ulSpecialAward == 0u) return (uint)TaskInterfaceConstants.TASK_PREREQU_FAIL_SPECIAL_AWARD; // TODO: 获取玩家特殊奖励信息并校验 // English: Fetch player's special-award info and validate against required id // 受限于当前 TaskInterface 未暴露获取接口,暂时视为通过 // English: Interface lacks API; treat as pass for now return 0u; } public void ClearValidCount() { m_uValidCount = 0; } public ATaskTemplMan GetTaskTemplMan() { return EC_Game.GetTaskTemplateMan(); } public ATaskTempl GetConstSubById(uint ulId) { ATaskTempl pChild = m_pFirstChild; while (pChild != null) { if (pChild.m_FixedData.m_ID == ulId) return pChild; pChild = pChild.m_pNextSibling; } return null; } public ActiveTaskEntry DeliverTask( TaskInterface pTask, ActiveTaskList pList, ActiveTaskEntry pEntry, uint ulCaptainTask, uint ulMask, uint ulCurTime, ATaskTempl pSubTempl, ref task_sub_tags pSubTag, TaskGlobalData pGlobal, byte uParentIndex) { // 最小实现:保证接任务/放弃任务会真实改变 ActiveTaskList,从而驱动任务面板刷新 // English: Minimal implementation: make NEW/GIVEUP actually mutate ActiveTaskList so UI can refresh. if (pTask == null || pList == null) return null; // Already has task var existed = pList.GetEntry(m_FixedData.m_ID); if (existed != null && existed.m_ID != 0) return existed; if (pList.m_uTaskCount >= TaskInterfaceConstants.TASK_ACTIVE_LIST_MAX_LEN) return null; int insertIndex = pList.m_uTaskCount; ActiveTaskEntry entry = pEntry ?? new ActiveTaskEntry(); // Fill fixed data (top-level by default) entry.m_ID = (ushort)m_FixedData.m_ID; entry.m_ulTemplAddr = m_FixedData.m_ID; // non-zero marker; template is resolved by ID in GetTempl() entry.m_ulTaskTime = ulCurTime; entry.m_ParentIndex = (char)0xff; entry.m_PrevSblIndex = (char)0xff; entry.m_NextSblIndex = (char)0xff; entry.m_ChildIndex = (char)0xff; entry.m_uState = (char)0; entry.SetSuccess(); // default success, may be cleared by later checks if (ulCaptainTask != 0) { entry.m_uCapTaskId = (ushort)ulCaptainTask; entry.m_ulCapTemplAddr = ulCaptainTask; // marker only } else { entry.m_uCapTaskId = 0; entry.m_ulCapTemplAddr = 0; } // Clear union buffer if (entry.m_BufData != null) Array.Clear(entry.m_BufData, 0, entry.m_BufData.Length); // Insert and update list count pList.m_TaskEntries[insertIndex] = entry; pList.m_uTaskCount = (byte)(insertIndex + 1); // Recount for UI and budget checks pList.RecountTaskCounters(); return entry; /*ActiveTaskEntry* aEntries = pList->m_TaskEntries; if (!pEntry) pEntry = aEntries + pList->m_uTaskCount; else if (pEntry->m_ID != 0) // entry��ռ�ã���Ҫ���Ų��һ����λ; pList->RealignTask(pEntry, 1); unsigned char uIndex = static_cast(pEntry - aEntries); pEntry->m_ID = static_cast(m_ID); pEntry->m_ulTemplAddr = reinterpret_cast(this); pEntry->m_ParentIndex = uParentIndex; pEntry->m_PrevSblIndex = 0xff; pEntry->m_NextSblIndex = 0xff; pEntry->m_ChildIndex = 0xff; pEntry->m_uState = 0; pEntry->m_ulTaskTime = ulCurTime; #ifndef _TASK_CLIENT // ��ΪPQ������Ҫ��ʱ�����¼��m_ulTaskTime�� if (m_bPQTask) { pEntry->m_ulTaskTime = PublicQuestInterface::GetCurTaskStamp(m_ID); } #endif if (ulCaptainTask) { pEntry->m_ulCapTemplAddr = reinterpret_cast(GetTaskTemplMan()->GetTopTaskByID(ulCaptainTask)); if (pEntry->m_ulCapTemplAddr) pEntry->m_uCapTaskId = static_cast(ulCaptainTask); else { pEntry->m_uCapTaskId = 0; char log[1024]; sprintf(log, "DeliverTask, Cant Find CapTask = %d", ulCaptainTask); TaskInterface::WriteLog(pTask->GetPlayerId(), m_ID, 0, log); } } else { pEntry->m_uCapTaskId = 0; pEntry->m_ulCapTemplAddr = 0; } pEntry->SetSuccess(); // Later will check whether truly succeed memset(pEntry->m_BufData, 0, sizeof(pEntry->m_BufData)); #ifndef _TASK_CLIENT // ��ΪPQ�����PQ��������Ҫ��ʱ�����¼��m_ulTaskTime�� if (m_bPQTask) { ulCurTime = PublicQuestInterface::GetCurTaskStamp(m_ID); } else if (m_enumMethod == enumTMReachTreasureZone) { // ����ر�λ�úͲر�ͼ���½�λ�õ�index unsigned short uZonesSum = m_ucZonesNumX * m_ucZonesNumZ; unsigned short uTreasureLocIndex = pTask->RandNormal(1,uZonesSum); char sTreasureLocMinX = (uTreasureLocIndex % m_ucZonesNumX - 1); char sTreasureLocMinZ = (uTreasureLocIndex / m_ucZonesNumX); // ������������IJر�ͼλ�ã�ʹ�ر�λ�ò��ܴ��ڲر�ͼ�ı�Ե int sMapMinX = pTask->RandNormal(sTreasureLocMinX,sTreasureLocMinX + TASK_TREASURE_MAP_SIDE_MULTIPLE - 3) - TASK_TREASURE_MAP_SIDE_MULTIPLE + 2; int sMapMinZ = pTask->RandNormal(sTreasureLocMinZ,sTreasureLocMinZ + TASK_TREASURE_MAP_SIDE_MULTIPLE - 3) - TASK_TREASURE_MAP_SIDE_MULTIPLE + 2; // �洢���ѽ������б��� pEntry->m_iUsefulData1 = uTreasureLocIndex; pEntry->m_iUsefulData1 |= (sMapMinX << 16) & 0x00FF0000; pEntry->m_iUsefulData1 |= (sMapMinZ << 24) & 0xFF000000; } ulMask |= m_ulMask; #else if (m_enumMethod == enumTMReachTreasureZone) { task_notify_base notify; notify.reason = TASK_CLT_NOTIFY_REQUEST_TREASURE_INDEX; notify.task = static_cast(m_ID); pTask->NotifyServer(¬ify, sizeof(notify)); } #endif pList->m_uTaskCount++; if (!m_pParent) { if (m_bHidden) pList->m_uTopHideTaskCount++; else if (m_bDisplayInTitleTaskUI) pList->m_uTitleTaskCount++; else pList->m_uTopShowTaskCount++; pList->m_uUsedCount += m_uDepth; #ifndef _TASK_CLIENT if (pGlobal) { pGlobal->AddRevNum(); pGlobal->m_ulRcvUpdateTime = ulCurTime; TaskUpdateGlobalData(m_ID, pGlobal->buf); } #endif } if (uParentIndex != 0xff) { ActiveTaskEntry& ParentEntry = aEntries[uParentIndex]; if (ParentEntry.m_ChildIndex == 0xff) ParentEntry.m_ChildIndex = uIndex; else { unsigned char uChildEntry = ParentEntry.m_ChildIndex; while (aEntries[uChildEntry].m_NextSblIndex != 0xff) uChildEntry = aEntries[uChildEntry].m_NextSblIndex; aEntries[uChildEntry].m_NextSblIndex = uIndex; pEntry->m_PrevSblIndex = uChildEntry; } } #ifndef _TASK_CLIENT if (!m_pParent) DeliverGivenItems(pTask); #endif pEntry++; if (pSubTempl) { return pSubTempl->DeliverTask( pTask, pList, pEntry, 0, ulMask, ulCurTime, NULL, pSubTag, NULL, uIndex); } else if (m_bRandOne) { #ifdef _TASK_CLIENT if (pSubTag->cur_index < pSubTag->sz) { pSubTempl = GetSubByIndex(pSubTag->tags[pSubTag->cur_index]); pSubTag->cur_index++; if (pSubTempl) { return pSubTempl->DeliverTask( pTask, pList, pEntry, 0, ulMask, ulCurTime, NULL, pSubTag, NULL, uIndex); } } #else int nSel; pSubTempl = RandOneChild(pTask, nSel); if (pSubTempl) { if (pSubTag->sz < MAX_SUB_TAGS) pSubTag->tags[pSubTag->sz++] = static_cast(nSel); return pSubTempl->DeliverTask( pTask, pList, pEntry, 0, ulMask, ulCurTime, NULL, pSubTag, NULL, uIndex); } #endif } else { const ATaskTempl* pChild = m_pFirstChild; while (pChild) { pEntry = pChild->DeliverTask( pTask, pList, pEntry, 0, ulMask, ulCurTime, NULL, pSubTag, NULL, uIndex); if (m_bExeChildInOrder) return pEntry; pChild = pChild->m_pNextSibling; } } return pEntry;*/ } void RecursiveAward( TaskInterface pTask, ActiveTaskList pList, ActiveTaskEntry pEntry, uint ulCurtime, int nChoice, ref task_sub_tags pSubTag) { // 最小实现(客户端): // - 记录到 FinishedTaskList(用于前置任务/可重复接判断) // - 从 ActiveTaskList 移除该任务条目,从而驱动任务面板刷新 // English minimal client port: // - Record into FinishedTaskList (prerequisites/redo checks) // - Remove entry from ActiveTaskList to refresh UI lists if (pTask == null || pList == null || pEntry == null) return; bool success = pEntry.IsSuccess() && !pEntry.IsGiveUp(); // Only record on top-level templates (same as C++: !m_pParent && m_bNeedRecord) if (m_pParent == null && m_FixedData.m_bNeedRecord) { if (pTask is CECTaskInterface cec) { cec.RecordFinishedTask(m_FixedData.m_ID, success); } } // Remove from active list (C++ RecursiveAward clears children + removes current entry) pList.RemoveEntry(pEntry); return; // TODO : implement full logic when ActiveTaskList/ActiveTaskEntry and TaskInterface APIs are available /*{ char log[1024]; sprintf(log, "RecursiveAward: state = 0x%x", pEntry->m_uState); TaskInterface::WriteLog(pTask->GetPlayerId(), m_ID, 1, log); } ActiveTaskEntry* aEntries = pList->m_TaskEntries; // ����ʧ�ܲ���ȡ��Ʒ bool bFailedTaskDoNotTakeItem = !pEntry->IsSuccess() && m_bNotClearItemWhenFailed && m_bClearAcquired; // ��������� pList->ClearChildrenOf(pTask, pEntry, !bFailedTaskDoNotTakeItem); if (!pEntry->m_ulTemplAddr) return; // must check it if (!m_pParent && m_bNeedRecord) { static_cast(pTask->GetFinishedTaskList())->AddOneTask( m_ID, pEntry->IsSuccess()); } // �˺��޴�����������ɴ��� if (!m_pParent && m_bAccountTaskLimit) { // ��������ʱ���ж��Ƿ��������ɴ��� CheckDeliverTime(pTask, ulCurtime); // û�й�ѡ��ʧ�ܵ�ʱ�򲻼�¼��ɴ����������߹�ѡ��ʧ�ܵ�ʱ�򲻼�¼��ɴ�������������ɹ���ʱ�� if (!m_bNotIncCntWhenFailed || (m_bNotIncCntWhenFailed && pEntry->IsSuccess())) static_cast(pTask->GetFinishedCntList())->AddOrUpdate(m_ID,ulCurtime); } // ��ɫ�޴�����������ɴ��� else if (!m_pParent && m_bRoleTaskLimit) { // ��������ʱ���ж��Ƿ��������ɴ��� CheckDeliverTime(pTask, ulCurtime); // û�й�ѡ��ʧ�ܵ�ʱ�򲻼�¼��ɴ����������߹�ѡ��ʧ�ܵ�ʱ�򲻼�¼��ɴ�������������ɹ���ʱ�� if (!m_bNotIncCntWhenFailed || (m_bNotIncCntWhenFailed && pEntry->IsSuccess())) { // FinishedTaskList�ﱣ��������ɴ��� static_cast(pTask->GetFinishedTaskList())->AddForFinishCount(m_ID,pEntry->IsSuccess()); // TaskFinishTimeList�ﱣ���������ʱ�� static_cast(pTask->GetFinishedTimeList())->AddOrUpdate(m_ID, ulCurtime); } } #ifndef _TASK_CLIENT // ������Ʒ AWARD_DATA ad; CalcAwardData( pTask, &ad, pEntry, pEntry->m_ulTaskTime, ulCurtime); unsigned long ulRet = DeliverByAwardData(pTask, pList, pEntry, &ad, ulCurtime, nChoice); if (ulRet) { char log[1024]; sprintf(log, "RecursiveAward, ret = %d", ulRet); TaskInterface::WriteLog(pTask->GetPlayerId(), m_ID, 0, log); } // ȥ����õĻ�Ԥ�ȸ�����Ʒ���ڷ�����Ʒ��ִ�� if (m_bClearAcquired) { if (!bFailedTaskDoNotTakeItem) RemoveAcquiredItem(pTask, false, pEntry->IsSuccess()); } else if (!pEntry->IsSuccess()) TakeAwayGivenItems(pTask); #endif pEntry->m_ulTemplAddr = 0; pEntry->m_ID = 0; if (pList->m_uTaskCount) pList->m_uTaskCount--; else TaskInterface::WriteLog(pTask->GetPlayerId(), m_ID, 0, "Award, TaskCount == 0"); if (pEntry->m_ParentIndex != 0xff) { ActiveTaskEntry& ParentEntry = aEntries[pEntry->m_ParentIndex]; if (pEntry->m_PrevSblIndex != 0xff) aEntries[pEntry->m_PrevSblIndex].m_NextSblIndex = pEntry->m_NextSblIndex; else ParentEntry.m_ChildIndex = pEntry->m_NextSblIndex; if (pEntry->m_NextSblIndex != 0xff) aEntries[pEntry->m_NextSblIndex].m_PrevSblIndex = pEntry->m_PrevSblIndex; if (!pEntry->IsSuccess() && m_bParentAlsoFail) { pList->RealignTask(pEntry, 0); ParentEntry.ClearSuccess(); ParentEntry.SetFinished(); m_pParent->RecursiveAward(pTask, pList, &ParentEntry, ulCurtime, -1, pSubTag); } else if (pEntry->IsSuccess() && m_bParentAlsoSucc) { pList->RealignTask(pEntry, 0); ParentEntry.SetFinished(); pList->ClearChildrenOf(pTask, &ParentEntry); if (m_pParent->m_enumFinishType == enumTFTDirect) m_pParent->RecursiveAward(pTask, pList, &ParentEntry, ulCurtime, -1, pSubTag); #ifdef _TASK_CLIENT else if (!m_pParent->m_bHidden && !m_pParent->m_bDisplayInTitleTaskUI) TaskInterface::TraceTask(m_pParent->m_ID); #endif } else if (m_pParent->m_bExeChildInOrder && m_pNextSibling) { if (ParentEntry.m_ChildIndex != 0xff || pList->GetEntry(m_pNextSibling->m_ID)) // ����������������Ϊ0xff pList->RealignTask(pEntry, 0); else { m_pNextSibling->DeliverTask( pTask, pList, pEntry, 0, *pTask->GetTaskMask(), ulCurtime, NULL, pSubTag, NULL, pEntry->m_ParentIndex); #ifdef _TASK_CLIENT // �����������������ѡ��һ�������һ���������������˳��ִ�� // ǰ���������׷�ٸ�����ʱ�Ѿ����� // ���ڵڶ������Ժ��˳��ִ�е������� if (!m_pParent->m_bHidden && !m_pParent->m_bDisplayInTitleTaskUI) TaskInterface::TraceTask(m_pNextSibling->m_ID); #endif } } else if (ParentEntry.m_ChildIndex == 0xff) // ������������������ʱ����Ϊ��� { pList->RealignTask(pEntry, 0); ParentEntry.SetFinished(); if (m_pParent->m_enumFinishType == enumTFTDirect) m_pParent->RecursiveAward(pTask, pList, &ParentEntry, ulCurtime, -1, pSubTag); #ifdef _TASK_CLIENT else if (!m_pParent->m_bHidden && !m_pParent->m_bDisplayInTitleTaskUI) TaskInterface::TraceTask(m_pParent->m_ID); #endif } else pList->RealignTask(pEntry, 0); } else // Root Node { pList->RealignTask(pEntry, 0); if (pList->m_uUsedCount >= m_uDepth) pList->m_uUsedCount -= m_uDepth; else { char log[1024]; sprintf(log, "Award, Used = %d", pList->m_uUsedCount); TaskInterface::WriteLog(pTask->GetPlayerId(), m_ID, 0, log); pList->m_uUsedCount = 0; } if (m_bHidden) { if (pList->m_uTopHideTaskCount) pList->m_uTopHideTaskCount--; } else if(m_bDisplayInTitleTaskUI) { if (pList->m_uTitleTaskCount) pList->m_uTitleTaskCount--; } else { if (pList->m_uTopShowTaskCount) pList->m_uTopShowTaskCount--; } }*/ } public void TakeAwayGivenItems(TaskInterface pTask) { uint ulCount; for (int i = 0; i < m_FixedData.m_ulGivenItems; i++) { ITEM_WANTED wi = m_FixedData.m_GivenItems[i]; if (wi.m_bCommonItem) { ulCount = (uint)pTask.GetCommonItemCount(wi.m_ulItemTemplId); if (ulCount > wi.m_ulItemNum) ulCount = wi.m_ulItemNum; if (ulCount > 0) pTask.TakeAwayCommonItem(wi.m_ulItemTemplId, ulCount); } else { ulCount = (uint)pTask.GetTaskItemCount(wi.m_ulItemTemplId); if (ulCount > 0) pTask.TakeAwayTaskItem(wi.m_ulItemTemplId, ulCount); } } } public void RemoveAcquiredItem(TaskInterface pTask, bool bClearTask, bool bSuccess) { var m_enumMethod = m_FixedData.m_enumMethod; var m_ulItemsWanted = m_FixedData.m_ulItemsWanted; var m_ItemsWanted = m_FixedData.m_ItemsWanted; var m_ulGoldWanted = m_FixedData.m_ulGoldWanted; var m_iFactionContribWanted = m_FixedData.m_iFactionContribWanted; var m_iFactionExpContribWanted = m_FixedData.m_iFactionExpContribWanted; var m_ulMonsterWanted = m_FixedData.m_ulMonsterWanted; var m_MonsterWanted = m_FixedData.m_MonsterWanted; var m_ulPlayerWanted = m_FixedData.m_ulPlayerWanted; var m_PlayerWanted = m_FixedData.m_PlayerWanted; if (m_enumMethod == (uint)TaskCompletionMethod.enumTMCollectNumArticle) { for (int i = 0; i < m_ulItemsWanted; i++) { ITEM_WANTED wi = m_ItemsWanted[i]; uint ulCount; if (wi.m_bCommonItem) { if (bClearTask) continue; ulCount = (uint)pTask.GetCommonItemCount(wi.m_ulItemTemplId); if (ulCount == 0) continue; if (wi.m_ulItemNum > 0 && ulCount > wi.m_ulItemNum) ulCount = wi.m_ulItemNum; pTask.TakeAwayCommonItem(wi.m_ulItemTemplId, ulCount); } else { ulCount = (uint)pTask.GetTaskItemCount(wi.m_ulItemTemplId); if (ulCount > 0) pTask.TakeAwayTaskItem(wi.m_ulItemTemplId, ulCount); } } // �ɹ����Ǯ���� if (m_ulGoldWanted > 0 && !bClearTask && bSuccess) { uint ulGold = pTask.GetGoldNum(); if (ulGold > m_ulGoldWanted) ulGold = m_ulGoldWanted; pTask.TakeAwayGold(ulGold); } //�ɹ���Ѱ��ɹ��׶����� if (m_iFactionContribWanted > 0 && !bClearTask && bSuccess) { int iContrib = pTask.GetFactionConsumeContrib(); if (iContrib > m_iFactionContribWanted) iContrib = m_iFactionContribWanted; pTask.TakeAwayFactionConsumeContrib(iContrib); } //�ɹ���Ѱ��ɾ������� if (m_iFactionExpContribWanted > 0 && !bClearTask && bSuccess) { int iContrib = pTask.GetFactionExpContrib(); if (iContrib > m_iFactionExpContribWanted) iContrib = m_iFactionExpContribWanted; pTask.TakeAwayFactionExpContrib(iContrib); } } else if (m_enumMethod == (uint)TaskCompletionMethod.enumTMKillNumMonster) { for (int i = 0; i < m_ulMonsterWanted; i++) { // const MONSTER_WANTED& mw = m_MonsterWanted[i]; MONSTER_WANTED mw = m_MonsterWanted[i]; if (mw.m_ulDropItemId == 0) continue; uint ulCount; if (mw.m_bDropCmnItem) { ulCount = (uint)pTask.GetCommonItemCount(mw.m_ulDropItemId); if (mw.m_ulDropItemCount > 0 && ulCount > mw.m_ulDropItemCount) ulCount = mw.m_ulDropItemCount; if (ulCount > 0) pTask.TakeAwayCommonItem(mw.m_ulDropItemId, ulCount); } else { ulCount = (uint)pTask.GetTaskItemCount(mw.m_ulDropItemId); if (ulCount > 0) pTask.TakeAwayTaskItem(mw.m_ulDropItemId, ulCount); } } } else if (m_enumMethod == (uint)TaskCompletionMethod.enumTMKillPlayer) { for (uint i = 0; i < m_ulPlayerWanted; ++i) { // const PLAYER_WANTED& pw = m_PlayerWanted[i]; var pw = m_PlayerWanted[i]; if (pw.m_ulDropItemId == 0) continue; uint ulCount; if (pw.m_bDropCmnItem) { ulCount = (uint)pTask.GetCommonItemCount(pw.m_ulDropItemId); if (pw.m_ulDropItemCount > 0 && ulCount > pw.m_ulDropItemCount) ulCount = pw.m_ulDropItemCount; if (ulCount > 0) pTask.TakeAwayCommonItem(pw.m_ulDropItemId, ulCount); } else { ulCount = (uint)pTask.GetTaskItemCount(pw.m_ulDropItemId); if (ulCount>0) pTask.TakeAwayTaskItem(pw.m_ulDropItemId, ulCount); } } } } public bool IsValidState() { return m_uValidCount < TaskTemplConstants.TASK_MAX_VALID_COUNT; } public static bool _compare_pq_key_value( COMPARE_KEY_VALUE CompKeyVal) { long lleftValue = CompKeyVal.lLeftNum; if (CompKeyVal.nLeftType == 0) { lleftValue = PublicQuestInterface.QuestGetGlobalValue(CompKeyVal.lLeftNum); // pTask->GetGlobalValue(CompKeyVal.lLeftNum); } long lRightValue = CompKeyVal.lRightNum; if (CompKeyVal.nRightType == 0) { lRightValue = PublicQuestInterface.QuestGetGlobalValue(CompKeyVal.lRightNum); // pTask->GetGlobalValue(CompKeyVal.lRightNum); } switch(CompKeyVal.nCompOper) { case 0: { if (lleftValue > lRightValue) return true; } break; case 1: { if (lleftValue == lRightValue) return true; } break; case 2: { if (lleftValue < lRightValue) return true; } break; default: break; } return false; } public uint CheckGlobalPQKeyValue(bool bFinCheck) { var m_bFinNeedComp = m_FixedData.m_bFinNeedComp; var m_bPremNeedComp = m_FixedData.m_bPremNeedComp; var m_Fin1KeyValue = m_FixedData.m_Fin1KeyValue; var m_Fin2KeyValue = m_FixedData.m_Fin2KeyValue; var m_nFinExp1AndOrExp2 = m_FixedData.m_nFinExp1AndOrExp2; var m_nPremExp1AndOrExp2 = m_FixedData.m_nPremExp1AndOrExp2; var m_Prem1KeyValue = m_FixedData.m_Prem1KeyValue; var m_Prem2KeyValue = m_FixedData.m_Prem2KeyValue; if (bFinCheck && !m_bFinNeedComp ||!bFinCheck && !m_bPremNeedComp) return 0; bool bFlag1 = false; bool bFlag2 = false; if (bFinCheck) { bFlag1 = _compare_pq_key_value(m_Fin1KeyValue); bFlag2 = _compare_pq_key_value(m_Fin2KeyValue); if (m_nFinExp1AndOrExp2 == 0 && (bFlag1 || bFlag2) //�� || m_nFinExp1AndOrExp2 == 1 && (bFlag1 && bFlag2)) //�� return 0; } else { bFlag1 = _compare_pq_key_value(m_Prem1KeyValue); bFlag2 = _compare_pq_key_value(m_Prem2KeyValue); if (m_nPremExp1AndOrExp2 == 0 && (bFlag1 || bFlag2) //�� || m_nPremExp1AndOrExp2 == 1 && (bFlag1 && bFlag2)) //�� return 0; } return TaskInterfaceConstants.TASK_PREREQU_FAIL_GLOBAL_KEYVAL; } public void OnSetFinished( TaskInterface pTask, ActiveTaskList pList, ActiveTaskEntry pEntry, bool bNotifyMem = true) { var m_enumFinishType = m_FixedData.m_enumMethod; pEntry.SetFinished(); // ֪ͨ�ͻ�������� NotifyClient( pTask, pEntry, TaskTemplConstants.TASK_SVR_NOTIFY_FINISHED, 0); if(pEntry.GetTempl().m_FixedData.m_bPQTask) PublicQuestInterface.QuestRemovePlayer((int)pEntry.GetTempl().m_FixedData.m_ID, pTask.GetPlayerId()); if (m_enumFinishType == (uint)TaskFinishType.enumTFTDirect || !pEntry.IsSuccess()) DeliverAward(pTask, pList, pEntry, -1, bNotifyMem); } public void GiveUpOneTask( TaskInterface pTask, ActiveTaskList pList, ActiveTaskEntry pEntry, bool bForce) { var m_bCanGiveUp = m_FixedData.m_bCanGiveUp; if (m_pParent != null || !m_bCanGiveUp) return; pEntry.ClearSuccess(); pEntry.SetGiveUp(); OnSetFinished(pTask, pList, pEntry); // TaskInterface::WriteLog(pTask.GetPlayerId(), m_ID, 1, "GiveUpTask"); // TaskInterface::WriteKeyLog(pTask.GetPlayerId(), m_ID, 1, "GiveUpTask"); } private void NotifyClient( TaskInterface pTask, ActiveTaskEntry pEntry, byte uReason, uint ulCurTime, uint ulParam=0, int dps=0, int dph=0) { var m_ID = m_FixedData.m_ID; var m_MonsterWanted = m_FixedData.m_MonsterWanted; string log = new string(new char[1024]); byte[] buf = new byte[512]; // task_notify_base* pNotify = reinterpret_cast(buf); var pNotify = new task_notify_base(); int sz = 0; pNotify.reason = uReason; pNotify.task = (ushort)(m_ID); bool bWriteLog = true; switch (uReason) { case TaskTemplConstants.TASK_SVR_NOTIFY_PLAYER_KILLED: var svr_player_killed = new svr_player_killed(); svr_player_killed.baseObj = pNotify; svr_player_killed.player_num = pEntry.m_wMonsterNum[ulParam]; svr_player_killed.index = (ushort)ulParam; sz = Marshal.SizeOf(svr_player_killed); break; case TaskTemplConstants.TASK_SVR_NOTIFY_MONSTER_KILLED: { var svrMonsterKilled = new svr_monster_killed() { baseObj = pNotify, monster_id = m_MonsterWanted[ulParam].m_ulMonsterTemplId, monster_num = pEntry.m_wMonsterNum[ulParam], dps = (dps != 0 && dph != 0) ? dps : 0, dph = (dps != 0 && dph != 0) ? dph : 0 }; sz = Marshal.SizeOf(svrMonsterKilled); bWriteLog = false; } break; case TaskTemplConstants.TASK_SVR_NOTIFY_NEW: { // static_cast(pNotify)->set_data( // ulCurTime, // reinterpret_cast(pEntry), // *(reinterpret_cast(ulParam)) // ); var svrNewTask = new svr_new_task { baseObj = pNotify, }; svrNewTask.set_data( ulCurTime, pEntry.m_ID, Marshal.PtrToStructure((IntPtr)ulParam) ); sz = svrNewTask.get_size(); break; } case TaskTemplConstants.TASK_SVR_NOTIFY_COMPLETE: // static_cast(pNotify)->set_data( // ulCurTime, // *(reinterpret_cast(ulParam)) // ); // sz = static_cast(pNotify)->get_size(); var svrTaskComplete = new svr_task_complete { baseObj = pNotify, }; svrTaskComplete.set_data( ulCurTime, Marshal.PtrToStructure((IntPtr)ulParam) ); sz = svrTaskComplete.get_size(); break; case TaskTemplConstants.TASK_SVR_NOTIFY_GIVE_UP: case TaskTemplConstants.TASK_SVR_NOTIFY_FINISHED: case TaskTemplConstants.TASK_SVR_NOTIFY_DIS_GLOBAL_VAL: sz = Marshal.SizeOf(); break; case TaskTemplConstants.TASK_SVR_NOTIFY_ERROR_CODE: var svrTaskErrCode = new svr_task_err_code { baseObj = pNotify }; svrTaskErrCode.err_code = ulParam; sz = Marshal.SizeOf( svrTaskErrCode ); break; default: // sprintf(log, "NotifyClient, Unknown Reason = %d", uReason); // TaskInterface::WriteLog(pTask->GetPlayerId(), m_ID, 0, log); return; } if (bWriteLog) { // sprintf(log, "svr: Reason = %d, Param = 0x%x", uReason, ulParam); // TaskInterface::WriteLog(pTask->GetPlayerId(), m_ID, 1, log); } // assert(sz <= sizeof(buf)); pTask.NotifyClient(buf, sz); } public bool DeliverAward( TaskInterface pTask, ActiveTaskList pList, ActiveTaskEntry pEntry, int nChoice, bool bNotifyTeamMem = true, // = true Nullable pGlobal = null ) // = null { char[] log = new char[1024]; // sprintf(log, "DeliverAward: Choice = %d", nChoice); // TaskInterface::WriteLog(pTask->GetPlayerId(), m_ID, 1, log); // TaskInterface::WriteKeyLog(pTask->GetPlayerId(), m_ID, 1, log); // ���ʱ�� uint ulCurTime = pTask.GetCurTime(); RecursiveCheckTimeLimit(pTask, pList, pEntry, ulCurTime); // TODO : implement full logic when ActiveTaskList/ActiveTaskEntry and TaskInterface APIs are available // if (!RecursiveCheckParent(pTask, pList, pEntry)) // pEntry->ClearSuccess(); // // // �������ʧ����� // if (!pEntry->IsAwardNotifyTeam() && bNotifyTeamMem && pEntry->GetCapOrSelf()->m_bTeamwork && !pEntry->IsSuccess()) // { // AwardNotifyTeamMem(pTask, pEntry); // pEntry->SetAwardNotifyTeam(); // Nofity only once // } // // if (!pTask->IsDeliverLegal()) // �������������׵�����״̬ // return false; // // if (pEntry->IsGiveUp() && m_bClearAsGiveUp) // { // pList->ClearTask(pTask, pEntry, true); // pList->UpdateTaskMask(*pTask->GetTaskMask()); // NotifyClient(pTask, NULL, TASK_SVR_NOTIFY_GIVE_UP, 0); // // // �Ѱ������� // if (m_bCompareItemAndInventory) // { // pTask->LockInventory(false); // } // // return true; // } // // pEntry->SetFinished(); // // #if _TASK_CLIENT // // unsigned long ulRet; // if ((ulRet = RecursiveCheckAward( // pTask, // pList, // pEntry, // ulCurTime, // nChoice // )) != 0) // { // if (m_enumFinishType == enumTFTNPC || !pEntry->IsErrReported()) // { // NotifyClient( // pTask, // NULL, // TASK_SVR_NOTIFY_ERROR_CODE, // 0, // ulRet // ); // // pEntry->SetErrReported(); // } // // char log[1024]; // sprintf(log, "DeliverAward: ret = %d", ulRet); // TaskInterface::WriteLog(pTask->GetPlayerId(), m_ID, 1, log); // return false; // } // // #endif // // // ������ӳɹ���� // if (!pEntry->IsAwardNotifyTeam() && bNotifyTeamMem && pEntry->IsSuccess()) // { // if(pEntry->GetCapOrSelf()->m_bTeamwork) // { // AwardNotifyTeamMem(pTask, pEntry); // pEntry->SetAwardNotifyTeam(); // Nofity only once // } // else // { // const ATaskTempl* pParent = pEntry->GetTempl()->GetTopTask(); // ActiveTaskEntry* pParentEntry = static_cast(pTask->GetActiveTaskList())->GetEntry(pParent->GetID()); // // if(pParentEntry->GetCapOrSelf()->m_bTeamwork && pEntry->m_ChildIndex == 0xff) // { // bool bIsLastChild = true; // const ATaskTempl* pTempParent = pEntry->GetTempl(); // while(pTempParent)// && pTempParent->m_pNextSibling == NULL) // { // if(pTempParent->m_pNextSibling != NULL) // { // bIsLastChild = false; // break; // } // // pTempParent = pTempParent->m_pParent; // } // // if(bIsLastChild) // { // AwardNotifyTeamMem(pTask, pParentEntry); // pParentEntry->SetAwardNotifyTeam(); // Nofity only once // } // } // } // } // // task_sub_tags sub_tags; // memset(&sub_tags, 0, sizeof(sub_tags)); // sub_tags.state = pEntry->m_uState; // // // ������� // RecursiveAward(pTask, pList, pEntry, ulCurTime, nChoice, &sub_tags); // // // ֪ͨ�ͻ��� // NotifyClient( // pTask, // NULL, // TASK_SVR_NOTIFY_COMPLETE, // ulCurTime, // reinterpret_cast(&sub_tags)); // // // ���Mask // pList->UpdateTaskMask(*pTask->GetTaskMask()); // // // �Ѱ������� // if (m_bCompareItemAndInventory) // { // pTask->LockInventory(false); // } return true; } private void RecursiveCheckTimeLimit( TaskInterface pTask, ActiveTaskList pList, ActiveTaskEntry pEntry, uint ulCurTime) { // TODO: implement full logic when ActiveTaskList/ActiveTaskEntry and TaskInterface APIs are available // if (m_ulTimeLimit > 0 && pEntry.m_ulTaskTime + m_ulTimeLimit < ulCurTime) // ��ʱ // pEntry.ClearSuccess(); // // // if (m_ulAbsFailTime && m_ulAbsFailTime < ulCurTime) // ��������ʧЧ���� // // ��������ʧЧ���� // if (m_bAbsFail) // { // tm cur = *localtime((long*)&ulCurTime); // // if(m_tmAbsFailTime.before(&cur)) // { // pEntry->ClearSuccess(); // } // } // // if (m_pParent && pEntry->m_ParentIndex != 0xff) // { // ActiveTaskEntry& ParentEntry = pList->m_TaskEntries[pEntry->m_ParentIndex]; // m_pParent->RecursiveCheckTimeLimit(pTask, pList, &ParentEntry, ulCurTime); // } } } }