diff --git a/Assets/PerfectWorld/Prefab/Task/UI/TaskWindow.prefab b/Assets/PerfectWorld/Prefab/Task/UI/TaskWindow.prefab index 6e6d6d4517..9a7f272149 100644 --- a/Assets/PerfectWorld/Prefab/Task/UI/TaskWindow.prefab +++ b/Assets/PerfectWorld/Prefab/Task/UI/TaskWindow.prefab @@ -6483,7 +6483,7 @@ MonoBehaviour: m_TargetGraphic: {fileID: 7392889747821849613} m_HandleRect: {fileID: 2343337405992641122} m_Direction: 2 - m_Value: 0 + m_Value: 1 m_Size: 1 m_NumberOfSteps: 0 m_OnValueChanged: @@ -7284,6 +7284,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 101488732bfd4d2fab4ea07f7ac6731f, type: 3} m_Name: m_EditorClassIdentifier: + _nameTaskText: {fileID: 502589151962525598} m_pTxt_QuestNO: {fileID: 9151364455187038631} m_pTv_Quest: {fileID: 6916443525973237579} m_pTxt_Content: {fileID: 392616940935835323} diff --git a/Assets/PerfectWorld/Scripts/MainFiles/EC_Game.Time.cs b/Assets/PerfectWorld/Scripts/MainFiles/EC_Game.Time.cs index 461da28c31..c1e6089fb2 100644 --- a/Assets/PerfectWorld/Scripts/MainFiles/EC_Game.Time.cs +++ b/Assets/PerfectWorld/Scripts/MainFiles/EC_Game.Time.cs @@ -1,5 +1,6 @@ using UnityEngine; using System; +using CSNetwork; namespace BrewMonster.Network { @@ -57,11 +58,7 @@ namespace BrewMonster.Network return m_AbsTimeStart + (int)sec; } } - - - public float timeGetTime() - { - return Time.time; - } + + } } \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_InventoryUI.cs b/Assets/PerfectWorld/Scripts/Managers/EC_InventoryUI.cs index 07549d66ee..ad32c71052 100644 --- a/Assets/PerfectWorld/Scripts/Managers/EC_InventoryUI.cs +++ b/Assets/PerfectWorld/Scripts/Managers/EC_InventoryUI.cs @@ -4,7 +4,7 @@ using UnityEngine; using UnityEngine.UI; using System.Reflection; using System.Text; -using System.Text.RegularExpressions; +using CSNetwork.GPDataType; using BrewMonster.Network; using BrewMonster; using BrewMonster.Common; @@ -79,95 +79,7 @@ namespace BrewMonster.Scripts.Managers /// Formatted text for TextMeshPro private static string FormatForTextMeshPro(string text) { - if (string.IsNullOrEmpty(text)) - return string.Empty; - - StringBuilder result = new StringBuilder(text); - - // Handle line breaks (\r) - result.Replace("\\r", "\n"); - - // Handle color codes (^RRGGBB format) - string processedText = ProcessColorCodes(result); - - return processedText; - } - - /// - /// Format text for legacy Text components (limited rich text support) - /// - /// Raw text with formatting codes - /// Formatted text for legacy Text - private static string FormatForLegacyText(string text) - { - if (string.IsNullOrEmpty(text)) - return string.Empty; - - StringBuilder result = new StringBuilder(text); - - // Handle line breaks (\r) - result.Replace("\\r", "\n"); - - // Handle color codes (^RRGGBB format) - convert to Unity's rich text format - string processedText = ProcessColorCodesForLegacy(result); - - return processedText; - } - - /// - /// Process color codes for TextMeshPro (supports hex colors directly) - /// - private static string ProcessColorCodes(StringBuilder text) - { - // Pattern to match color codes: ^ followed by 6 hex characters - string pattern = @"\^([0-9A-Fa-f]{6})"; - - return Regex.Replace(text.ToString(), pattern, match => - { - string hexColor = match.Groups[1].Value; - return $""; - }, RegexOptions.None); - } - - /// - /// Process color codes for legacy Text (convert to Unity rich text format) - /// - private static string ProcessColorCodesForLegacy(StringBuilder text) - { - // Pattern to match color codes: ^ followed by 6 hex characters - string pattern = @"\^([0-9A-Fa-f]{6})"; - - return Regex.Replace(text.ToString(), pattern, match => - { - string hexColor = match.Groups[1].Value; - // Convert hex to Unity color format - Color color = HexToColor(hexColor); - return $""; - }, RegexOptions.None); - } - - /// - /// Convert hex color string to Unity Color - /// - /// Hex color string (e.g., "ffcb4a") - /// Unity Color object - private static Color HexToColor(string hex) - { - if (hex.Length != 6) - return Color.white; - - try - { - int r = Convert.ToInt32(hex.Substring(0, 2), 16); - int g = Convert.ToInt32(hex.Substring(2, 2), 16); - int b = Convert.ToInt32(hex.Substring(4, 2), 16); - - return new Color(r / 255f, g / 255f, b / 255f, 1f); - } - catch - { - return Color.white; - } + return EC_Utility.FormatForTextMeshPro(text); } // Current selected item for equip/unequip operations @@ -749,7 +661,7 @@ namespace BrewMonster.Scripts.Managers { if (legacy != null) { - legacy.text = FormatForLegacyText(value ?? string.Empty); + legacy.text = EC_Utility.FormatForLegacyText(value ?? string.Empty); } if (tmp != null) { @@ -766,7 +678,7 @@ namespace BrewMonster.Scripts.Managers { string formattedText = preferTextMeshPro ? FormatForTextMeshPro(value ?? string.Empty) : - FormatForLegacyText(value ?? string.Empty); + EC_Utility.FormatForLegacyText(value ?? string.Empty); if (legacy != null) { diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommandFactory.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommandFactory.cs index e7436d4524..770c4232b9 100644 --- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommandFactory.cs +++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommandFactory.cs @@ -549,5 +549,14 @@ namespace CSNetwork.C2SCommand }; return SerializeCommand(CommandID.SEVNPC_SERVE, cmd); } + + public static Octets CreateTaskNotifyCmd() + { + var cmd = new cmd_header + { + cmd = (ushort)CommandID.TASK_NOTIFY + }; + return SerializeCommand(CommandID.TASK_NOTIFY, cmd); + } } } diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs index 3a0468c9d9..51bbe10263 100644 --- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs +++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs @@ -17,6 +17,7 @@ using System.Runtime.InteropServices; using System.Text; using System.Text; using System.Threading.Tasks; +using BrewMonster.Scripts.Task; using CommandID = CSNetwork.GPDataType.CommandID; namespace CSNetwork @@ -1148,5 +1149,12 @@ namespace CSNetwork iCount += iNumSend; } } + + public void c2s_SendCmdTaskNotify(ref task_notify_base pBuf, uint sz) + { + gamedatasend gamedatasend = new gamedatasend(); + gamedatasend.Data = C2SCommandFactory.CreateTaskNotifyCmd(); + SendProtocol(gamedatasend); + } } } \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs b/Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs index dbf5163304..4b15bd8f5f 100644 --- a/Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs +++ b/Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs @@ -12,6 +12,7 @@ using System.Collections.Generic; using System.IO; using System.Text; using System.Threading.Tasks; +using BrewMonster.Scripts.Task; using UnityEngine; using UnityEngine.SceneManagement; @@ -283,6 +284,12 @@ namespace BrewMonster.Network //Debug.Log("[Dat]- SendCmdGetAllData"); Instance._gameSession.c2s_SendCmdGetAllData(byPack, byEquip, byTask); } + + public static void c2s_CmdTaskNotify(ref task_notify_base pBuf, uint sz) + { + Instance._gameSession.c2s_SendCmdTaskNotify(ref pBuf, sz); + } + #endregion public static void GetRoleBaseInfo(int iNumRole, List aRoleIDs) diff --git a/Assets/PerfectWorld/Scripts/Task/ATaskTemplMan.cs b/Assets/PerfectWorld/Scripts/Task/ATaskTemplMan.cs index 9c336af594..8055aeaf2f 100644 --- a/Assets/PerfectWorld/Scripts/Task/ATaskTemplMan.cs +++ b/Assets/PerfectWorld/Scripts/Task/ATaskTemplMan.cs @@ -1,3 +1,4 @@ +using System; using BrewMonster; using ModelRenderer.Scripts.GameData; using System.Collections.Generic; @@ -157,7 +158,7 @@ namespace BrewMonster.Scripts.Task public ATaskTempl GetTaskTemplByID(uint ulID) { - if (m_TaskTemplMap.TryGetValue((uint)ulID, out ATaskTempl task)) + if (m_AllTemplMap.TryGetValue((uint)ulID, out ATaskTempl task)) { return task; } @@ -349,6 +350,41 @@ namespace BrewMonster.Scripts.Task } + + 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) diff --git a/Assets/PerfectWorld/Scripts/Task/CECTaskInterface.cs b/Assets/PerfectWorld/Scripts/Task/CECTaskInterface.cs index f3c3990cd2..f2c8f32263 100644 --- a/Assets/PerfectWorld/Scripts/Task/CECTaskInterface.cs +++ b/Assets/PerfectWorld/Scripts/Task/CECTaskInterface.cs @@ -474,7 +474,7 @@ namespace BrewMonster.Scripts.Task { return EC_Game.GetTaskTemplateMan(); } - private ActiveTaskList GetActiveTaskList() + public ActiveTaskList GetActiveTaskList() { return m_pActiveListBuf; } @@ -973,6 +973,46 @@ namespace BrewMonster.Scripts.Task return 0u; } + + public void ShowPunchBagMessage(bool bSucced,bool bMax,uint MonsterTemplID,int dps,int dph) + { + // TODO : implement UI message box + + // CECGameUIMan* pGameUI = g_pGame->GetGameRun()->GetUIManager()->GetInGameUIMan(); + // + // if (pGameUI == NULL) return; + // + // elementdataman *pDataMan = g_pGame->GetElementDataMan(); + // + // DATA_TYPE dt; + // MONSTER_ESSENCE *pMonster = (MONSTER_ESSENCE *)pDataMan->get_data_ptr(MonsterTemplID, ID_SPACE_ESSENCE, dt); + // + // ACString strNpcName; + // if( dt == DT_MONSTER_ESSENCE ) + // { + // if( pMonster) + // strNpcName = pMonster->name; + // } + // + // ACString str; + // if (!bSucced) + // { + // str.Format(pGameUI->GetStringFromTable(303),strNpcName); + // pGameUI->MessageBox("",str,MB_OK,A3DCOLORRGBA(255, 255, 255, 160)); + // } + // + // else if (bMax) + // { + // str.Format(pGameUI->GetStringFromTable(304),dph); + // pGameUI->MessageBox("",str,MB_OK,A3DCOLORRGBA(255, 255, 255, 160)); + // } + // else + // { + // str.Format(pGameUI->GetStringFromTable(305),dph); + // pGameUI->MessageBox("",str,MB_OK,A3DCOLORRGBA(255, 255, 255, 160)); + // } + + } #endif @@ -1162,7 +1202,110 @@ namespace BrewMonster.Scripts.Task string strName = m_pHost.GetName(); // assumes string; use ToString() if needed return ret.Replace(SYMBOL_HOSTNAME, $"&{strName}&"); } - } + + public bool IsTaskReadyToConfirm(int iTaskID) + { + if (m_TasksToConfirm == null) return false; + bool ret; + if (m_TasksToConfirm.TryGetValue(iTaskID, out ret)) return ret; + return false; + } + + public void NotifyServer(ref task_notify_base pBuf, uint sz) + { + UnityGameSession.c2s_CmdTaskNotify(ref pBuf, sz); + } + + public bool CheckVersion() + { + // return static_cast(GetActiveTaskList())->m_Version == TASK_ENTRY_DATA_CUR_VER; + return GetActiveTaskList().m_Version == TaskTemplConstants.TASK_ENTRY_DATA_CUR_VER; + } + + public StorageTaskList GetStorageTaskList() + { + StorageTaskList ret = new StorageTaskList(); + ret.ReadByte(m_pStorageTaskListBuf); + return ret; + } + + public uint GetTaskMask() + { + return 0; + } + + public byte[] GetFinishedTimeList() + { + return m_pFinishedTimeListBuf; + } + + void SetForceNavigateFinishFlag(bool bFinish) { m_bForceNavigateFinish = bFinish;} // + public void OnNewTask(int iTaskID) + { + ATaskTempl pTempl = GetTaskTemplMan().GetTaskTemplByID((uint)iTaskID); + if (pTempl != null + && pTempl.m_FixedData.m_enumMethod== (uint)TaskCompletionMethod.enumTMSimpleClientTaskForceNavi) + { + SetForceNavigateFinishFlag(false); + + // TODO: trigger navigation event + // m_pHost.OnNaviageEvent(iTaskID,CECNavigateCtrl::EM_PREPARE); + } + } + + public void OnTaskConfirmUpdate() + { + // TODO: update task confirm UI + // CECGameUIMan* pGameUI = g_pGame->GetGameRun()->GetUIManager()->GetInGameUIMan(); + // if (pGameUI) pGameUI->UpdateTaskConfirm(); + } + + public void UpdateConfirmTasksMap() + { + m_TasksToConfirm.Clear(); + TaskClient.OnTaskCheckStatus(this); + OnTaskConfirmUpdate(); + } + + public void OnCompleteTask(int iTaskID) + { + ATaskTempl pTempl = GetTaskTemplMan().GetTaskTemplByID((uint)iTaskID); + if (pTempl != null && + pTempl.m_FixedData.m_enumMethod == (uint)TaskCompletionMethod.enumTMSimpleClientTaskForceNavi) + { + //TODO: trigger navigation end event + // m_pHost.OnNaviageEvent(iTaskID,CECNavigateCtrl::EM_END); + SetForceNavigateFinishFlag(false); + } + } + + public void TakeAwayCommonItem(uint ulTemplId, uint ulNum) {} + + public void TakeAwayTaskItem(uint ulTemplId, uint ulNum) {} + public void TakeAwayGold(uint ulNum) {} + public void TakeAwayFactionConsumeContrib(int ulNum){} + public void OnGiveupTask(int iTaskID) + { + ATaskTempl pTempl = GetTaskTemplMan().GetTaskTemplByID((uint)iTaskID); + if (pTempl != null && + pTempl.m_FixedData.m_enumMethod == (uint)TaskCompletionMethod.enumTMSimpleClientTaskForceNavi) + { + // TODO: trigger navigation end event + // m_pHost.OnNaviageEvent(iTaskID,CECNavigateCtrl::EM_END); + SetForceNavigateFinishFlag(false); + } + } + + public void UpdateTaskUI(uint idTask, int reason) + { + // TODO: update task UI + // CECGameUIMan* pGameUI = g_pGame->GetGameRun()->GetUIManager()->GetInGameUIMan(); + // if (pGameUI) + // { + // pGameUI->UpdateTask(idTask, reason); + // } + } + } } diff --git a/Assets/PerfectWorld/Scripts/Task/TaskClient.cs b/Assets/PerfectWorld/Scripts/Task/TaskClient.cs new file mode 100644 index 0000000000..656a3873b5 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Task/TaskClient.cs @@ -0,0 +1,515 @@ +using System; +using System.Reflection; +using System.Runtime.InteropServices; +using BrewMonster.Network; +using BrewMonster.Scripts.Task; +using PerfectWorld.Scripts.Task; +using UnityEngine; + +namespace BrewMonster.Scripts.Task +{ + + // provide some global methods + public class TaskClient + { +#if _TASK_CLIENT + + private const uint FINISH_DLG_SHOWN_TIME = 3000; // TODO: Confirm correct value + private static uint s_finishDlgShownTime = 0; + + public static void OnTaskCheckStatus(TaskInterface pTask) + { + // 版本与交付合法性检查 // Version and deliver legality check + if (pTask == null || !pTask.IsDeliverLegal()) + return; + // TODO: CheckVersion not exposed on TaskInterface; skipping version check + + // 读取激活任务列表 // Read active task list + ActiveTaskList pLst = TryGetActiveList(pTask); + if (pLst == null) return; + ActiveTaskEntry[] aEntries = pLst.m_TaskEntries; + uint ulCurTime = GetCurTime(); + + // 遍历所有激活任务 // Iterate active tasks + for (int i = 0; i < pLst.m_uTaskCount; i++) + { + ActiveTaskEntry CurEntry = aEntries[i]; + if (CurEntry == null) continue; + + if (CurEntry.m_ulTemplAddr == 0) + { + // assert(false) // English: unexpected empty template + continue; + } + + ATaskTempl pTempl = CurEntry.GetTempl(); + if (pTempl == null) continue; + + // TODO: IsValidState from C++ not found in managed port; skip validity-state check + + // PQ子任务 // PQ subtask + if (pTempl.m_FixedData.m_bPQSubTask) + { + // TODO: CheckGlobalPQKeyValue(true) not ported; if implemented and returns 0, notify server then continue + // if (pTempl.CheckGlobalPQKeyValue(true) == 0) { ... } + } + + // 超时判断 // Timeout check + if (pTempl.m_FixedData.m_ulTimeLimit != 0 + && CurEntry.m_ulTaskTime + pTempl.m_FixedData.m_ulTimeLimit < ulCurTime) + { + pTempl.IncValidCount(); + _notify_svr(pTask, (int)ClientNotificationConstants.TASK_CLT_NOTIFY_CHECK_FINISH, CurEntry.m_ID); + continue; + } + + // 绝对失效时间判断 // Absolute fail time check + if (pTempl.m_FixedData.m_bAbsFail) + { + // TODO: Time zone bias and 'task_tm.before' not ported; skipping precise comparison + } + + // 进入或离开区域导致失败 // Entering or leaving region causes failure + { + float[] pos = new float[3]; + uint ulWorldId = (uint)pTask.GetPos(pos); + + // 进入区域失败 // Enter region fail + if (pTempl.m_FixedData.m_bEnterRegionFail && ulWorldId == pTempl.m_FixedData.m_ulEnterRegionWorld) + { + for (uint iRegion = 0; iRegion < pTempl.m_FixedData.m_ulEnterRegionCnt; iRegion++) + { + Task_Region t = pTempl.m_FixedData.m_pEnterRegion[(int)iRegion]; + if (IsInZone(t.zvMin, t.zvMax, pos)) + { + pTempl.IncValidCount(); + _notify_svr(pTask, (int)ClientNotificationConstants.TASK_CLT_NOTIFY_CHECK_FINISH, CurEntry.m_ID); + break; + } + } + } + + // 离开区域失败 // Leave region fail + if (pTempl.m_FixedData.m_bLeaveRegionFail) + { + bool bLeaveRegion = false; + if (ulWorldId != pTempl.m_FixedData.m_ulLeaveRegionWorld) bLeaveRegion = true; + else + { + uint iRegion = 0; + for (; iRegion < pTempl.m_FixedData.m_ulLeaveRegionCnt; iRegion++) + { + Task_Region t = pTempl.m_FixedData.m_pLeaveRegion[(int)iRegion]; + if (IsInZone(t.zvMin, t.zvMax, pos)) + break; + } + if (iRegion >= pTempl.m_FixedData.m_ulLeaveRegionCnt) bLeaveRegion = true; + } + if (bLeaveRegion) + { + pTempl.IncValidCount(); + _notify_svr(pTask, (int)ClientNotificationConstants.TASK_CLT_NOTIFY_CHECK_FINISH, CurEntry.m_ID); + } + } + } + + // 离开家族失败 // Leave faction fail + if (!pTask.IsAtCrossServer() && pTempl.m_FixedData.m_bLeaveFactionFail && !pTask.IsInFaction(1)) + { + pTempl.IncValidCount(); + _notify_svr(pTask, (int)ClientNotificationConstants.TASK_CLT_NOTIFY_CHECK_FINISH, CurEntry.m_ID); + continue; + } + + // 对话/NPC完成类任务跳过本轮 // Skip talk-to-NPC or NPC-finish tasks here + if ((TaskCompletionMethod)pTempl.m_FixedData.m_enumMethod == TaskCompletionMethod.enumTMTalkToNPC + || pTempl.m_FixedData.m_bMarriage + || (TaskFinishType)pTempl.m_FixedData.m_enumFinishType == TaskFinishType.enumTFTNPC) + continue; + + // 判断未完成的直接完成判定 // Check direct-finish for unfinished tasks + if (!CurEntry.IsFinished()) + { + // 到达地点直接完成 // Reach-site direct finish + if ((TaskCompletionMethod)pTempl.m_FixedData.m_enumMethod == TaskCompletionMethod.enumTMReachSite + && (TaskFinishType)pTempl.m_FixedData.m_enumFinishType == TaskFinishType.enumTFTDirect) + { + if (ulCurTime - s_finishDlgShownTime < FINISH_DLG_SHOWN_TIME) + continue; + + float[] pos = new float[3]; + uint ulWorldId = (uint)pTask.GetPos(pos); + + if (ulWorldId == pTempl.m_FixedData.m_ulReachSiteId) + { + for (uint iRegion = 0; iRegion < pTempl.m_FixedData.m_ulReachSiteCnt; iRegion++) + { + Task_Region t = pTempl.m_FixedData.m_pReachSite[(int)iRegion]; + if (IsInZone(t.zvMin, t.zvMax, pos)) + { + var pTalk = pTempl.m_AwardTalk; + if (pTalk.num_window == 0 || (pTalk.num_window == 1 && pTalk.windows != null && pTalk.windows.Length > 0 && pTalk.windows[0].num_option == 0)) + { + pTempl.IncValidCount(); + _notify_svr(pTask, ClientNotificationConstants.TASK_CLT_NOTIFY_REACH_SITE, (ushort)pTempl.GetID()); + } + else + { + // TODO: PopupTaskFinishDialog not exposed; implement UI as needed + s_finishDlgShownTime = ulCurTime; + } + break; + } + } + } + continue; + } + + // 离开地点直接完成 // Leave-site direct finish + if ((TaskCompletionMethod)pTempl.m_FixedData.m_enumMethod == TaskCompletionMethod.enumTMLeaveSite + && (TaskFinishType)pTempl.m_FixedData.m_enumFinishType == TaskFinishType.enumTFTDirect) + { + if (ulCurTime - s_finishDlgShownTime < FINISH_DLG_SHOWN_TIME) + continue; + + float[] pos = new float[3]; + uint ulWorldId = (uint)pTask.GetPos(pos); + bool regRet = false; + + if (ulWorldId == pTempl.m_FixedData.m_ulLeaveSiteId) + { + for (uint iRegion = 0; iRegion < pTempl.m_FixedData.m_ulLeaveSiteCnt; iRegion++) + { + Task_Region t = pTempl.m_FixedData.m_pLeaveSite[(int)iRegion]; + if (IsInZone(t.zvMin, t.zvMax, pos)) + { + regRet = true; + break; + } + } + } + if (!regRet) + { + var pTalk = pTempl.m_AwardTalk; + if (pTalk.num_window == 0 || (pTalk.num_window == 1 && pTalk.windows != null && pTalk.windows.Length > 0 && pTalk.windows[0].num_option == 0)) + { + pTempl.IncValidCount(); + _notify_svr(pTask, ClientNotificationConstants.TASK_CLT_NOTIFY_LEAVE_SITE, (ushort)pTempl.GetID()); + } + else + { + // TODO: PopupTaskFinishDialog not exposed; implement UI as needed + s_finishDlgShownTime = ulCurTime; + } + } + continue; + } + } + + // 非子任务:检查奖励条件并按需标记/通知 // If no children, check award conditions and update + if (pTempl != null && pTempl.m_pFirstChild == null) + { + bool bNeedServerCheck = + pTempl.RecursiveCheckAward(pTask, pLst, CurEntry, ulCurTime, -1) == 0 + && pTempl.CanFinishTask(pTask, CurEntry, ulCurTime); + + if (pTempl.m_FixedData.m_bDisplayInExclusiveUI && pTempl.m_FixedData.m_bAutoDeliver + && (TaskFinishType)pTempl.m_FixedData.m_enumFinishType == TaskFinishType.enumTFTDirect) + { + // TODO: Hook game UI and update auto-deliver countdown; no UI manager available here + uint ulRemainTime = 0; + if ((TaskCompletionMethod)pTempl.m_FixedData.m_enumMethod == TaskCompletionMethod.enumTMWaitTime) + { + uint ultime = CurEntry.m_ulTaskTime + pTempl.m_FixedData.m_ulWaitTime; + if (ultime > ulCurTime) ulRemainTime = ultime - ulCurTime; + } + + // TODO: pTempl.m_bReadyToNotifyServer/ResetAutoDelTask workflow may need UI support + if (pTempl.m_FixedData.m_bReadyToNotifyServer && bNeedServerCheck) + { + pTempl.IncValidCount(); + // TODO: pTempl.ResetAutoDelTask() not exposed; skip + _notify_svr(pTask, (int)ClientNotificationConstants.TASK_CLT_NOTIFY_CHECK_FINISH, (ushort)CurEntry.m_ID); + } + } + else + { + // TODO: UpdateTaskToConfirm not ported; implement confirmation UI/state if needed + UpdateTaskToConfirm(pTask, pTempl, bNeedServerCheck); + } + } + } + + // TODO: ATaskTemplMan.UpdateStatus(pTask) not found in C# port; skipping + } + + // ===== Helpers ===== + + // 取当前时间(服务器绝对时间) // Get current time (server absolute) + private static uint GetCurTime() + { + return (uint)EC_Game.GetServerAbsTime(); + } + + // 反射读取激活任务列表 // Read active task list via reflection + private static ActiveTaskList TryGetActiveList(TaskInterface pTask) + { + // Try to get private method GetActiveTaskList on CECTaskInterface + MethodInfo mi = pTask.GetType().GetMethod("GetActiveTaskList", BindingFlags.Instance | BindingFlags.NonPublic); + if (mi != null) + { + try { return mi.Invoke(pTask, null) as ActiveTaskList; } catch { } + } + // Fallback to private field m_pActiveListBuf + FieldInfo fi = pTask.GetType().GetField("m_pActiveListBuf", BindingFlags.Instance | BindingFlags.NonPublic); + if (fi != null) + { + try { return fi.GetValue(pTask) as ActiveTaskList; } catch { } + } + return null; + } + + // 区域内检测(AABB) // In-zone check (AABB) + private static bool IsInZone(ZONE_VERT min, ZONE_VERT max, float[] pos) + { + if (pos == null || pos.Length < 3) return false; + return pos[0] >= min.x && pos[0] <= max.x + && pos[1] >= min.y && pos[1] <= max.y + && pos[2] >= min.z && pos[2] <= max.z; + } + + private 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( ref notify, (uint)Marshal.SizeOf()); + } + + // 更新“待确认任务” // Update task to confirm + private static void UpdateTaskToConfirm(TaskInterface pTask, ATaskTempl pTempl, bool needServerCheck) + { + // TODO: Implement confirmation queue/UI if required by design + } + + // Handle server notification for task updates + public static void OnServerNotify(TaskInterface pTask, byte[] pBuf, uint sz) + { + // Check version validity + // TODO: CheckVersion not exposed on TaskInterface; skipping version check + if (!pTask.CheckVersion()) + return; + + // Validate buffer size for base notification structure + if (sz < (uint)Marshal.SizeOf()) return; + + // Marshal base notification structure from buffer + GCHandle handle = GCHandle.Alloc(pBuf, GCHandleType.Pinned); + task_notify_base pNotify; + try + { + pNotify = Marshal.PtrToStructure(handle.AddrOfPinnedObject()); + } + finally + { + handle.Free(); + } + + ATaskTempl pTempl = null; + ActiveTaskEntry pEntry = null; + + // Handle error code notification + if (pNotify.reason == TaskTemplConstants.TASK_SVR_NOTIFY_ERROR_CODE) + { + // TODO: svr_task_err_code struct not defined; need to define or use alternative approach + // if (sz != Marshal.SizeOf()) return; + +#if _ELEMENTCLIENT + // TODO: GetEntry method not implemented in ActiveTaskList; need to implement or use alternative + // ActiveTaskList pLst = TryGetActiveList(pTask); + // if (pLst != null) + // { + // pEntry = GetEntry(pLst, pNotify.task); + // if (pEntry != null) pEntry.SetErrReported(); + // } + // TODO: TaskShowErrMessage not found; implement error message display + // TaskShowErrMessage(...); +#endif + return; + } + // Handle forget skill notification + else if (pNotify.reason == TaskTemplConstants.TASK_SVR_NOTIFY_FORGET_SKILL) + { + // TODO: OnForgetLivingSkill method not found in ATaskTemplMan; implement if needed + // ATaskTemplMan pMan = GetTaskTemplMan(pTask); + // if (pMan != null) pMan.OnForgetLivingSkill(pTask); + return; + } + // Handle new task notification + else if (pNotify.reason == TaskTemplConstants.TASK_SVR_NOTIFY_NEW) + { + ATaskTemplMan pMan = GetTaskTemplMan(pTask); + if (pMan != null) pTempl = pMan.GetTopTaskByID(pNotify.task); + } + // Handle dynamic task time mark notification + else if (pNotify.reason == TaskTemplConstants.TASK_SVR_NOTIFY_DYN_TIME_MARK) + { + // TODO: svr_task_dyn_time_mark struct not defined; need to define or use alternative + // if (sz != Marshal.SizeOf()) return; + // TODO: OnDynTasksTimeMark method not found in ATaskTemplMan; implement if needed + // ATaskTemplMan pMan = GetTaskTemplMan(pTask); + // if (pMan != null) + // { + // svr_task_dyn_time_mark dynMark = Marshal.PtrToStructure(handle.AddrOfPinnedObject()); + // pMan.OnDynTasksTimeMark(pTask, dynMark.time_mark, dynMark.version); + // } + return; + } + // Handle dynamic task data notification + else if (pNotify.reason == TaskTemplConstants.TASK_SVR_NOTIFY_DYN_DATA) + { + if (sz <= (uint)Marshal.SizeOf()) return; + // TODO: OnDynTasksData method not found in ATaskTemplMan; implement if needed + // ATaskTemplMan pMan = GetTaskTemplMan(pTask); + // if (pMan != null) + // { + // byte[] dynData = new byte[sz - Marshal.SizeOf()]; + // Array.Copy(pBuf, Marshal.SizeOf(), dynData, 0, dynData.Length); + // pMan.OnDynTasksData(pTask, dynData, (uint)dynData.Length, pNotify.task != 0); + // } + return; + } + // Handle storage data notification + else if (pNotify.reason == TaskTemplConstants.TASK_SVR_NOTIFY_STORAGE) + { + // TODO: StorageTaskList struct not defined; need to define or use alternative + // if (sz != Marshal.SizeOf() + Marshal.SizeOf()) return; + // TODO: OnStorageData method not found in ATaskTemplMan; implement if needed + // ATaskTemplMan pMan = GetTaskTemplMan(pTask); + // if (pMan != null) + // { + // byte[] storageData = new byte[Marshal.SizeOf()]; + // Array.Copy(pBuf, Marshal.SizeOf(), storageData, 0, storageData.Length); + // pMan.OnStorageData(pTask, storageData); + // } + // TODO: UpdateTaskUI static method not found; implement UI update if needed + // TaskInterface.UpdateTaskUI(pNotify.task, pNotify.reason); + return; + } + // Handle special award notification + else if (pNotify.reason == TaskTemplConstants.TASK_SVR_NOTIFY_SPECIAL_AWARD) + { + // TODO: svr_task_special_award and special_award structs not defined; need to define + // if (sz != Marshal.SizeOf()) return; + // TODO: OnSpecialAward method not found in ATaskTemplMan; implement if needed + // ATaskTemplMan pMan = GetTaskTemplMan(pTask); + // if (pMan != null) + // { + // svr_task_special_award awardNotify = Marshal.PtrToStructure(handle.AddrOfPinnedObject()); + // pMan.OnSpecialAward(ref awardNotify.sa, pTask); + // if (awardNotify.sa.id1 == 0) + // { + // // ID is 0 means no storage space, show newbie gift reminder + // // TODO: CECGameUIMan and PopupNewbieGiftRemind not found; implement UI if needed + // } + // } + return; + } + // Handle task limit increase notification + else if (pNotify.reason == TaskTemplConstants.TASK_SVR_NOTIFY_SET_TASK_LIMIT) + { + ActiveTaskList pLst = TryGetActiveList(pTask); + if (pLst != null) + { + // TODO: ExpandMaxSimultaneousCount method not implemented; implement if needed + // pLst.ExpandMaxSimultaneousCount(); + } + // TODO: PopChatMessage static method and FIXMSG_TASK_LIMIT_INCREASED constant not found + // TaskInterface.PopChatMessage(FIXMSG_TASK_LIMIT_INCREASED); + return; + } + // Search for task entry in active task list + else + { + ActiveTaskList pLst = TryGetActiveList(pTask); + if (pLst != null) + { + for (byte i = 0; i < pLst.m_uTaskCount; i++) + { + ActiveTaskEntry CurEntry = pLst.m_TaskEntries[i]; + if (CurEntry == null) continue; + + if (CurEntry.m_ID != pNotify.task || CurEntry.m_ulTemplAddr == 0) + continue; + + pTempl = CurEntry.GetTempl(); + pEntry = CurEntry; + break; + } + } + } + + // Handle player killed notification + if (pNotify.reason == TaskTemplConstants.TASK_SVR_NOTIFY_PLAYER_KILLED) + { + // TODO: CECUIHelper.OnTaskProcessUpdated not found; implement UI update if needed + // CECUIHelper.OnTaskProcessUpdated(pNotify.task); + } + // Handle monster killed notification + if (pNotify.reason == TaskTemplConstants.TASK_SVR_NOTIFY_MONSTER_KILLED) + { + // Monster kill count >= 2 triggers auto team + // TODO: svr_monster_killed struct not defined; need to define or use alternative + // if (sz == Marshal.SizeOf()) + // { + // svr_monster_killed pKilled = Marshal.PtrToStructure(handle.AddrOfPinnedObject()); + // if (pKilled.monster_num >= 2) + // { + // // TODO: CECAutoTeam and DoAutoTeam not found; implement auto team if needed + // // CECAutoTeam pAutoTeam = g_pGame.GetGameRun().GetHostPlayer().GetAutoTeam(); + // // pAutoTeam.DoAutoTeam(CECAutoTeam.TYPE_TASK, pNotify.task); + // } + // } + // TODO: CECUIHelper.OnTaskProcessUpdated not found; implement UI update if needed + // CECUIHelper.OnTaskProcessUpdated(pNotify.task); + } + // Handle task completion or give up notification + else if (pNotify.reason == TaskTemplConstants.TASK_SVR_NOTIFY_COMPLETE || pNotify.reason == TaskTemplConstants.TASK_SVR_NOTIFY_GIVE_UP) + { + // TODO: CECUIHelper.OnTaskCompleted not found; implement UI update if needed + // CECUIHelper.OnTaskCompleted(pNotify.task); + } + + // Validate template was found + if (pTempl == null) + { + // TODO: Replace assert with appropriate error handling + Debug.Assert(false, "Task template not found"); + return; + } + + // Clear valid count and process server notification + pTempl.ClearValidCount(); + // TODO: OnServerNotify method signature may need adjustment for C# (ref/out parameters) + pTempl.OnServerNotify(pTask, pEntry, ref pNotify, sz); + } + + // Helper method to get task template manager + private static ATaskTemplMan GetTaskTemplMan(TaskInterface pTask) + { + if (pTask is CECTaskInterface cecTask) + { + // Use reflection to access private GetTaskTemplMan method + MethodInfo mi = cecTask.GetType().GetMethod("GetTaskTemplMan", BindingFlags.Instance | BindingFlags.NonPublic); + if (mi != null) + { + try { return mi.Invoke(cecTask, null) as ATaskTemplMan; } catch { } + } + } + // Fallback to global access if available + // TODO: Check if EC_Game.GetTaskTemplateMan() is accessible + return EC_Game.GetTaskTemplateMan(); + } +#endif + } +} \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Task/TaskClient.cs.meta b/Assets/PerfectWorld/Scripts/Task/TaskClient.cs.meta new file mode 100644 index 0000000000..c3d40249b5 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Task/TaskClient.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: eb05c492df834a7da959d4d32b56057e +timeCreated: 1763461796 \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Task/TaskInterface.cs b/Assets/PerfectWorld/Scripts/Task/TaskInterface.cs index 2a31733edf..7ef606ed71 100644 --- a/Assets/PerfectWorld/Scripts/Task/TaskInterface.cs +++ b/Assets/PerfectWorld/Scripts/Task/TaskInterface.cs @@ -1,3 +1,5 @@ +using PerfectWorld.Scripts.Task; + namespace BrewMonster.Scripts.Task { public interface TaskInterface @@ -47,6 +49,7 @@ namespace BrewMonster.Scripts.Task uint GetTaskId(uint ulIndex); uint CanDeliverTask(uint ulTaskId); + void UpdateTaskUI(uint idTask, int reason); #endif // bool HasTask(uint taskId); // bool CheckTaskForbid(uint taskId); @@ -79,6 +82,22 @@ namespace BrewMonster.Scripts.Task bool IsKing(); bool IsInTeam(); uint GetAccountTotalCash(); + void NotifyServer(ref task_notify_base pBuf, uint sz); + bool CheckVersion(); + StorageTaskList GetStorageTaskList(); + void ShowPunchBagMessage(bool bSucced,bool bMax,uint MonsterTemplID,int dps,int dph); + ActiveTaskList GetActiveTaskList(); + uint GetTaskMask(); + byte[] GetFinishedTimeList(); + void OnNewTask(int iTaskID); + void UpdateConfirmTasksMap(); + void OnCompleteTask(int iTaskID); + void TakeAwayCommonItem(uint ulTemplId, uint ulNum); + void TakeAwayTaskItem(uint ulTemplId, uint ulNum); + void TakeAwayGold(uint ulNum) ; + void TakeAwayFactionConsumeContrib(int ulNum); + void TakeAwayFactionExpContrib(int ulNum){} + void OnGiveupTask(int iTaskID); } } \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Task/TaskProcess.cs b/Assets/PerfectWorld/Scripts/Task/TaskProcess.cs index 3f36271765..4c92ec6682 100644 --- a/Assets/PerfectWorld/Scripts/Task/TaskProcess.cs +++ b/Assets/PerfectWorld/Scripts/Task/TaskProcess.cs @@ -105,7 +105,7 @@ namespace PerfectWorld.Scripts.Task public bool IsAwardNotifyTeam() => (m_uState & (byte)TaskState.TASK_STATE_AWARD_NOTIFY_TEAM) != 0; public bool IsContributionFinish() => (m_uState & (byte)TaskState.TASK_STATE_CONTRIBUTION_FINISH) != 0; - // void SetFinished() { m_uState |= TASK_STATE_FINISHED; } + public void SetFinished() { m_uState |= (char)TaskState.TASK_STATE_FINISHED; } // void ClearFinished() { m_uState &= ~TASK_STATE_FINISHED; } // void SetSuccess() { m_uState |= TASK_STATE_SUCCESS; } // void ClearSuccess() { m_uState &= ~TASK_STATE_SUCCESS; } @@ -184,6 +184,7 @@ namespace PerfectWorld.Scripts.Task public class ActiveTaskList { // --- Header Fields --- + // NOTE: union public byte[] header = new byte[CECTaskInterface.TASK_ACTIVE_LIST_HEADER_LEN]; public byte m_uTaskCount; // number of tasks @@ -243,9 +244,184 @@ namespace PerfectWorld.Scripts.Task // void UpdateTaskMask(unsigned long& ulMask) const; // void UpdateUsedCount(); - // void RealignTask(ActiveTaskEntry* pEntry, unsigned char uReserve); - // void ClearTask(TaskInterface* pTask, ActiveTaskEntry* pEntry, bool bRemoveItem); - // void RecursiveClearTask(TaskInterface* pTask, ActiveTaskEntry* pEntry, bool bRemoveItem, bool bRemoveAcquired, bool bClearTask); + void RealignTask(ActiveTaskEntry pEntry, byte uReserve) + { + // TODO: implement RealignTask logic + // // unsigned char uCurIndex = static_cast(pEntry - m_TaskEntries); + // byte uCurIndex = (byte)Array.IndexOf(m_TaskEntries, pEntry); + // + // uint ulCount = (uint)m_uTaskCount - uCurIndex; // ʣ��������� + // + // if (ulCount == 0) return; // ���һ������ + // + // byte uEmptyCount = 0; + // for (int uEmpty = uCurIndex; uEmpty < TaskInterfaceConstants.TASK_ACTIVE_LIST_MAX_LEN; uEmpty++) + // { + // if (m_TaskEntries[uEmpty].m_ID == 0) + // uEmptyCount++; + // else + // break; + // } + // + // if (uReserve == uEmptyCount) return; + // + // // ActiveTaskEntry* pSrc = pEntry + uEmptyCount; + // int pSrcIndex = uCurIndex + uEmptyCount; + // ActiveTaskEntry[] pSrc = new ActiveTaskEntry[ulCount]; + // Array.Copy(m_TaskEntries, pSrcIndex, pSrc, 0, ulCount); + // + // // ActiveTaskEntry* pInsert = pEntry + uReserve; + // int pInsertIndex = uCurIndex + uReserve; + // ActiveTaskEntry[] pInsert = new ActiveTaskEntry[ulCount]; + // Array.Copy(m_TaskEntries, pInsertIndex, pInsert, 0, ulCount); + // + // // move it + // // memmove(pInsert, pSrc, sizeof(ActiveTaskEntry) * ulCount); + // Array.Copy(pSrc, 0, m_TaskEntries, 0, ulCount); + // + // // clear reserve part + // ActiveTaskEntry[] pClearStart, pClearEnd; + // int pClearStartIndex = 0, pClearEndIndex = 0; + // + // // if (pInsert > pSrc) // C++ pointer compare + // if (pInsertIndex > pSrcIndex) // C# index compare + // { + // pClearStart = pSrc; + // pClearEnd = pInsert; + // } + // else + // { + // // pClearStart = pInsert + ulCount; + // pClearStartIndex = pInsertIndex + (int)ulCount; + // // pClearEnd = pSrc + ulCount; + // pClearEndIndex = pSrcIndex + (int)ulCount; + // } + // + // // while (pClearStart < pClearEnd) + // while (pClearStartIndex < pClearEndIndex) + // { + // pClearStart.m_ulTemplAddr = 0; + // pClearStart.m_ID = 0; + // pClearStart++; + // } + // + // // calc gap + // unsigned char uGap = static_cast(pInsert - pSrc); + // unsigned long i = 0; + // + // for (; i < static_cast(uCurIndex); i++) + // { + // // Parent, PrevС��uCurIndex + // ActiveTaskEntry& CurEntry = m_TaskEntries[i]; + // + // if(!CurEntry.m_ID) + // continue; + // + // if (CurEntry.m_ChildIndex != 0xff && CurEntry.m_ChildIndex >= uCurIndex) + // CurEntry.m_ChildIndex += uGap; + // if (CurEntry.m_NextSblIndex != 0xff && CurEntry.m_NextSblIndex >= uCurIndex) + // CurEntry.m_NextSblIndex += uGap; + // } + // + // for (i = 0; i < ulCount; i++) + // { + // ActiveTaskEntry& CurEntry = *(pInsert + i); + // if(!CurEntry.m_ID) + // continue; + // + // if (CurEntry.m_ParentIndex != 0xff && CurEntry.m_ParentIndex >= uCurIndex) + // CurEntry.m_ParentIndex += uGap; + // if (CurEntry.m_PrevSblIndex != 0xff && CurEntry.m_PrevSblIndex >= uCurIndex) + // CurEntry.m_PrevSblIndex += uGap; + // if (CurEntry.m_ChildIndex != 0xff) + // CurEntry.m_ChildIndex += uGap; + // if (CurEntry.m_NextSblIndex != 0xff) + // CurEntry.m_NextSblIndex += uGap; + // } + } + public void ClearTask(TaskInterface pTask, ActiveTaskEntry pEntry, bool bRemoveItem) + { + RecursiveClearTask(pTask, pEntry, bRemoveItem, true, true); + RealignTask(pEntry, 0); + } + + void RecursiveClearTask( + TaskInterface pTask, + ActiveTaskEntry pEntry, + bool bRemoveItem, + bool bRemoveAcquired, + bool bClearTask) + { + while (pEntry.m_ChildIndex != 0xff) + { + RecursiveClearTask( + pTask, + m_TaskEntries[pEntry.m_ChildIndex], + bRemoveItem, + bRemoveAcquired, + bClearTask); + } + + ATaskTempl pTempl = pEntry.GetTempl(); + + // ȥ����õ���Ʒ +#if _TASK_CLIENT + if (bRemoveItem && pTempl != null) + { + if (bRemoveAcquired || pTempl.m_FixedData.m_bClearAcquired) pTempl.RemoveAcquiredItem(pTask, bClearTask, false); + pTempl.TakeAwayGivenItems(pTask); + } +#endif + + ushort uTaskId = pEntry.m_ID; + + pEntry.m_ulTemplAddr = 0; + pEntry.m_ID = 0; + + if (m_uTaskCount > 0) + m_uTaskCount--; + else + // TaskInterface::WriteLog(pTask->GetPlayerId(), uTaskId, 0, "ClearTask, TaskCount == 0"); + + if (pEntry.m_ParentIndex != 0xff) + { + if (pEntry.m_PrevSblIndex != 0xff) + m_TaskEntries[pEntry.m_PrevSblIndex].m_NextSblIndex = pEntry.m_NextSblIndex; + else + m_TaskEntries[pEntry.m_ParentIndex].m_ChildIndex = pEntry.m_NextSblIndex; + if (pEntry.m_NextSblIndex != 0xff) m_TaskEntries[pEntry.m_NextSblIndex].m_PrevSblIndex = pEntry.m_PrevSblIndex; + } + else + { + if (pTempl != null) + { + if (pTempl.m_FixedData.m_bHidden) + { + if (m_uTopHideTaskCount > 0) + m_uTopHideTaskCount--; + } + else if(pTempl.m_FixedData.m_bDisplayInTitleTaskUI) + { + if (m_uTitleTaskCount > 0) + m_uTitleTaskCount--; + } + else + { + if (m_uTopShowTaskCount > 0) + m_uTopShowTaskCount--; + } + + if (m_uUsedCount >= pTempl.m_uDepth) + m_uUsedCount -= pTempl.m_uDepth; + else + { + // TaskInterface::WriteLog(pTask->GetPlayerId(), uTaskId, 0, "ClearTask, No Enough Used Count"); + m_uUsedCount = 0; + } + } + } + } + // void ClearChildrenOf(TaskInterface* pTask, ActiveTaskEntry* pParent, bool bRemoveItem = true); // ActiveTaskEntry* GetEntry(unsigned long ulId) // { diff --git a/Assets/PerfectWorld/Scripts/Task/TaskTempl.Method.cs b/Assets/PerfectWorld/Scripts/Task/TaskTempl.Method.cs index ede8415632..4814b070f3 100644 --- a/Assets/PerfectWorld/Scripts/Task/TaskTempl.Method.cs +++ b/Assets/PerfectWorld/Scripts/Task/TaskTempl.Method.cs @@ -1,3 +1,6 @@ +using System; +using System.Runtime.InteropServices; +using BrewMonster.Network; using BrewMonster.Scripts.Task; using PerfectWorld.Scripts.Task; @@ -381,6 +384,203 @@ namespace BrewMonster.Scripts.Task } } // bool CanShowInExclusiveUI (TaskInterface* pTask, unsigned long ulCurTime) const; + public void OnServerNotify( + TaskInterface pTask, + ActiveTaskEntry pEntry, + ref task_notify_base pNotify, + uint sz) + { + 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 = null; + svr_player_killed pKilledPlayer = null; + StorageTaskList pStorage = pTask.GetStorageTaskList(); + svr_treasure_map pTreasure = null; + + 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 = pNotify as svr_player_killed; + int iIndex = pKilledPlayer.index; + if (iIndex < TaskInterfaceConstants.MAX_MONSTER_WANTED) + { + pEntry.m_wMonsterNum[iIndex] = pKilledPlayer.player_num; + } + } + break; + case TaskTemplConstants.TASK_SVR_NOTIFY_TREASURE_MAP: + if (m_enumMethod == (uint)TaskCompletionMethod.enumTMReachTreasureZone) + { + pTreasure = pNotify as svr_treasure_map; + 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 = pNotify as svr_monster_killed; + + 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.m_wMonsterNum[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: + var svr_new_task = pNotify as svr_new_task; + if (svr_new_task.valid_size((int)sz) ) break; + pLst = pTask.GetActiveTaskList(); + svr_new_task.get_data( + ref ulTime, + ref ulCaptainTask, + ref sub_tags + ); + + 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(), + 0); + + 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: + var svr_task_complete = pNotify as svr_task_complete; + if (svr_task_complete.valid_size((int)sz)) break; + 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(); + pLst.ClearTask(pTask, pEntry, false); + + // 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); + } #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; @@ -1047,5 +1247,576 @@ namespace BrewMonster.Scripts.Task // 受限于当前 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) + { + // TODO: implement full logic when ActiveTaskList/ActiveTaskEntry and TaskInterface APIs are available + return null; + + /*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) + { + // 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); + } + } + } + } + } } \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Task/TaskTempl.Struct.cs b/Assets/PerfectWorld/Scripts/Task/TaskTempl.Struct.cs new file mode 100644 index 0000000000..2e1e3f75cd --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Task/TaskTempl.Struct.cs @@ -0,0 +1,274 @@ +using System; +using System.Runtime.InteropServices; + +namespace BrewMonster.Scripts.Task +{ + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct task_sub_tags + { + // private const int MAX_SUB_TAGS = 32; + + // union + // { + // unsigned short sub_task; + // unsigned char state; + // }; + // IMPORTANT: union + public ushort sub_task; + public byte state; + + public byte sz; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = TaskTemplConstants.MAX_SUB_TAGS)] + public byte[] tags; + public byte cur_index; // for temporary use, dont take into account + + public int get_size() { return sz + 3; } + public bool valid_size(int _sz) + { + if (_sz < 3) return false; + return get_size() == _sz; + } + }; + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class task_notify_base + { + public byte reason; + public ushort task; + }; + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct cmd_task_notify + { + public uint size; + public byte placeholder; // Task data ... + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class svr_monster_killed : task_notify_base + { + public uint monster_id; + public ushort monster_num; + public int dps; + public int dph; + }; + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class svr_player_killed : task_notify_base + { + public ushort index; + public ushort player_num; + }; + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct StorageTaskList + { + // unsigned short m_Storages[TASK_STORAGE_COUNT][TASK_STORAGE_LEN]; + // NOTE: 2D array flattened + [MarshalAs(UnmanagedType.ByValArray, SizeConst = TaskTemplConstants.TASK_STORAGE_COUNT * TaskTemplConstants.TASK_STORAGE_LEN)] + public ushort[] m_Storages; + + // union { + // unsigned short m_StoragesTaskSetCount[TASK_STORAGE_COUNT]; + // unsigned short m_StoragesRefreshCount[TASK_STORAGE_COUNT]; + // }; + // NOTE: union + [MarshalAs(UnmanagedType.ByValArray, SizeConst = TaskTemplConstants.TASK_STORAGE_COUNT)] + public ushort[] m_StoragesTaskSetCount; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = TaskTemplConstants.TASK_STORAGE_COUNT)] + public ushort[] m_StoragesRefreshCount; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = TaskTemplConstants.TASK_STORAGE_COUNT)] + public uint[] m_StoragesRefreshTime; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = TaskTemplConstants.TASK_STORAGE_COUNT)] + public byte[] m_StoragesReceivePerDay; + + public void RemoveAll() + { + for (int i = 0; i < TaskTemplConstants.TASK_STORAGE_COUNT; i++) + { + for (int j = 0; j < TaskTemplConstants.TASK_STORAGE_LEN; j++) + { + m_Storages[i * TaskTemplConstants.TASK_STORAGE_LEN + j] = 0; + } + m_StoragesTaskSetCount[i] = 0; + m_StoragesRefreshCount[i] = 0; + m_StoragesRefreshTime[i] = 0; + m_StoragesReceivePerDay[i] = 0; + } + } + + public void ReadByte(byte[] data) + { + int offset = 0; + for (int i=0; i < TaskTemplConstants.TASK_STORAGE_COUNT; i++) + { + for (int j=0; j < TaskTemplConstants.TASK_STORAGE_LEN; j++) + { + m_Storages[i * TaskTemplConstants.TASK_STORAGE_LEN + j] = BitConverter.ToUInt16(data, (i * TaskTemplConstants.TASK_STORAGE_LEN + j) * 2); + } + } + + offset += TaskTemplConstants.TASK_STORAGE_COUNT * TaskTemplConstants.TASK_STORAGE_LEN * 2; + + // union + for (int i=0; i < TaskTemplConstants.TASK_STORAGE_COUNT; i++) + { + m_StoragesTaskSetCount[i] = BitConverter.ToUInt16(data, offset + i * 2); + m_StoragesRefreshCount[i] = BitConverter.ToUInt16(data, offset + i * 2); + } + offset += TaskTemplConstants.TASK_STORAGE_COUNT * 2; + + for (int i=0; i < TaskTemplConstants.TASK_STORAGE_COUNT; i++) + { + m_StoragesRefreshTime[i] = BitConverter.ToUInt32(data, offset + i * 4); + } + offset += TaskTemplConstants.TASK_STORAGE_COUNT * 4; + + for (int i=0; i < TaskTemplConstants.TASK_STORAGE_COUNT; i++) + { + m_StoragesReceivePerDay[i] = data[offset + i]; + } + } + } + + public class svr_new_task : task_notify_base + { + public uint cur_time; + public uint cap_task; + public task_sub_tags sub_tags; + + public void set_data( + uint _cur_time, + uint _cap_task, + task_sub_tags _sub_tags) + { + cur_time = _cur_time; + cap_task = _cap_task; + // memcpy(&sub_tags, &_sub_tags, _sub_tags.get_size()); + sub_tags = _sub_tags; + } + + public void get_data( + ref uint _cur_time, + ref uint _cap_task, + ref task_sub_tags _sub_tags) + { + _cur_time = cur_time; + _cap_task = cap_task; + // memcpy(&_sub_tags, &sub_tags, sub_tags.get_size()); + _sub_tags = sub_tags; + } + + // inline size_t get_size() const { return sizeof(task_notify_base) + 8 + sub_tags.get_size(); } + public int get_size() { return Marshal.SizeOf() + 8 + sub_tags.get_size(); } + + public bool valid_size(int sz) + { + int base_sz = Marshal.SizeOf() + 8; + if (sz <= base_sz) return false; + return sub_tags.valid_size(sz - base_sz); + } + } + + public class svr_treasure_map : task_notify_base + { + public int treasure_index; + } + + struct tm { + int tm_sec; /* seconds after the minute [0-60] */ + int tm_min; /* minutes after the hour [0-59] */ + int tm_hour; /* hours since midnight [0-23] */ + int tm_mday; /* day of the month [1-31] */ + int tm_mon; /* months since January [0-11] */ + int tm_year; /* years since 1900 */ + int tm_wday; /* days since Sunday [0-6] */ + int tm_yday; /* days since January 1 [0-365] */ + int tm_isdst; /* Daylight Savings Time flag */ + long tm_gmtoff; /* offset from UTC in seconds */ + byte tm_zone; /* timezone abbreviation */ + }; + + [StructLayout(LayoutKind.Explicit)] + public struct TaskGlobalData + { + [FieldOffset(0)] + [MarshalAs(UnmanagedType.ByValArray, SizeConst = TaskInterfaceConstants.TASK_GLOBAL_DATA_SIZE)] + public byte[] buf; + + [FieldOffset(0)] + public ulong m_ulReceiverNum; + + [FieldOffset(8)] + public ulong m_ulRcvUpdateTime; + + void AddRevNum() { m_ulReceiverNum++; } + + void CheckRcvUpdateTime(uint ulCurTime, int nFrequency) + { + // TODO: implement time-based receiver number reset logic + // if (nFrequency == TaskCompletionMethod.enumTAFNormal || m_ulRcvUpdateTime == 0) + // return; + // + // tm tmCur = *localtime((time_t*)&ulCurTime); + // tm tmRcv = *localtime((time_t*)&m_ulRcvUpdateTime); + // + // if (nFrequency == enumTAFEachDay) + // { + // if (tmCur.tm_year != tmRcv.tm_year || tmCur.tm_yday != tmRcv.tm_yday) + // m_ulReceiverNum = 0; + // } + // else if (nFrequency == enumTAFEachWeek) + // { + // if (!_is_same_week(&tmCur, &tmRcv, ulCurTime, m_ulRcvUpdateTime)) + // m_ulReceiverNum = 0; + // } + // else if (nFrequency == enumTAFEachMonth) + // { + // if (tmCur.tm_year != tmRcv.tm_year || tmCur.tm_mon != tmRcv.tm_mon) + // m_ulReceiverNum = 0; + // } + // else if (nFrequency == enumTAFEachYear) + // { + // if (tmCur.tm_year != tmRcv.tm_year) + // m_ulReceiverNum = 0; + // } + } + } + public class svr_task_complete : task_notify_base + { + public uint cur_time; + public task_sub_tags sub_tags; + + public void set_data( + uint _cur_time, + task_sub_tags _sub_tags + ) + { + cur_time = _cur_time; + // memcpy(&sub_tags, &_sub_tags, _sub_tags.get_size()); + sub_tags = _sub_tags; + } + + public void get_data( + ref uint _cur_time, + ref task_sub_tags _sub_tags) + { + _cur_time = cur_time; + // memcpy(&_sub_tags, &sub_tags, sub_tags.get_size()); + _sub_tags = sub_tags; + } + + public int get_size() { return Marshal.SizeOf() + 4 + sub_tags.get_size(); } + + public bool valid_size(int sz) + { + int base_sz = Marshal.SizeOf() + 4; + if (sz <= base_sz) return false; + return sub_tags.valid_size(sz - base_sz); + } + } + +} \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Task/TaskTempl.Struct.cs.meta b/Assets/PerfectWorld/Scripts/Task/TaskTempl.Struct.cs.meta new file mode 100644 index 0000000000..a81a30f15e --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Task/TaskTempl.Struct.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a70b3653041c46daa32cee68efb8bfec +timeCreated: 1763639774 \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Task/TaskTempl.cs b/Assets/PerfectWorld/Scripts/Task/TaskTempl.cs index 4f509ec9f1..52a704bad0 100644 --- a/Assets/PerfectWorld/Scripts/Task/TaskTempl.cs +++ b/Assets/PerfectWorld/Scripts/Task/TaskTempl.cs @@ -3,6 +3,7 @@ using System.IO; using System.Runtime.InteropServices; using System; using BrewMonster; +using CSNetwork.GPDataType; using ModelRenderer.Scripts.Common; using PerfectWorld.Scripts.Task; using UnityEngine.UIElements; @@ -52,6 +53,63 @@ namespace BrewMonster.Scripts.Task // Race-occupation mapping array public static readonly uint[] _race_occ_map = new uint[MAX_OCCUPATIONS]; + + /* + * Server Notifications + */ + + // 新任务发放 // New task issued + public const int TASK_SVR_NOTIFY_NEW = 1; + + // 任务完毕 // Task completed + public const int TASK_SVR_NOTIFY_COMPLETE = 2; + + // 任务放弃 // Task abandoned + public const int TASK_SVR_NOTIFY_GIVE_UP = 3; + + // 杀怪数量 // Monster kill count + public const int TASK_SVR_NOTIFY_MONSTER_KILLED = 4; + + // 处于得到奖励状态 // In reward receiving state + public const int TASK_SVR_NOTIFY_FINISHED = 5; + + // 错误码 // Error code + public const int TASK_SVR_NOTIFY_ERROR_CODE = 6; + + // 遗忘生活技能 // Forget life skill + public const int TASK_SVR_NOTIFY_FORGET_SKILL = 7; + + // 动态任务时间标记 // Dynamic task time mark + public const int TASK_SVR_NOTIFY_DYN_TIME_MARK = 8; + + // 动态任务数据 // Dynamic task data + public const int TASK_SVR_NOTIFY_DYN_DATA = 9; + + // 特殊奖励信息 // Special reward info + public const int TASK_SVR_NOTIFY_SPECIAL_AWARD = 10; + + // 仓库数据 // Storage data + public const int TASK_SVR_NOTIFY_STORAGE = 11; + + // 显示全局变量 // Display global variables + public const int TASK_SVR_NOTIFY_DIS_GLOBAL_VAL = 12; + + // 藏宝位置 // Treasure location + public const int TASK_SVR_NOTIFY_TREASURE_MAP = 13; + + // 设置任务列表上限 // Set task list limit + public const int TASK_SVR_NOTIFY_SET_TASK_LIMIT = 14; + + // 杀人数量 // Player kill count + public const int TASK_SVR_NOTIFY_PLAYER_KILLED = 15; + + public const int TASK_ENTRY_DATA_CUR_VER = 1; + + public const int MAX_SUB_TAGS = 32; + + public const int TASK_STORAGE_COUNT = 32; + public const int TASK_STORAGE_LEN = 10; + } public enum DynTaskType @@ -1228,6 +1286,77 @@ namespace BrewMonster.Scripts.Task return $"Min({zvMin.x}, {zvMin.y}, {zvMin.z}) - Max({zvMax.x}, {zvMax.y}, {zvMax.z})"; } } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct TaskFinishTimeEntry + { + public ushort m_uTaskId; + public uint m_ulTimeMark; + }; + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct TaskFinishTimeList + { + public ushort m_uCount; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = TaskInterfaceConstants.TASK_FINISH_TIME_MAX_LEN)] + public TaskFinishTimeEntry[] m_aList; + + public void ReadFromBuffer(byte[] data) + { + int offset = 0; + m_uCount = BitConverter.ToUInt16(data, offset); + offset += sizeof(ushort); + + m_aList = new TaskFinishTimeEntry[TaskInterfaceConstants.TASK_FINISH_TIME_MAX_LEN]; + for (int i = 0; i < TaskInterfaceConstants.TASK_FINISH_TIME_MAX_LEN; i++) + { + m_aList[i].m_uTaskId = BitConverter.ToUInt16(data, offset); + offset += sizeof(ushort); + m_aList[i].m_ulTimeMark = BitConverter.ToUInt32(data, offset); + offset += sizeof(uint); + } + } + + public uint Search(uint ulID) + { + for (int i = 0; i < m_uCount; i++) + if (m_aList[i].m_uTaskId == (ushort)ulID) + return m_aList[i].m_ulTimeMark; + + return 0; + } + + public void AddOrUpdate(uint ulID, uint ulTime){ + for (int i = 0; i < m_uCount; i++) + { + if (m_aList[i].m_uTaskId == (ushort)ulID) + { + m_aList[i].m_ulTimeMark = ulTime; + return; + } + } + + if (m_uCount >= TaskInterfaceConstants.TASK_FINISH_TIME_MAX_LEN) + return; + + m_aList[m_uCount].m_uTaskId = (ushort)ulID; + m_aList[m_uCount].m_ulTimeMark = ulTime; + m_uCount++; + } + + void RemoveAll() + { + m_uCount = 0; + for (int i = 0; i < TaskInterfaceConstants.TASK_FINISH_TIME_MAX_LEN; i++) + { + m_aList[i].m_uTaskId = 0; + m_aList[i].m_ulTimeMark = 0; + } + } + bool IsValid() { return m_uCount <= TaskInterfaceConstants.TASK_FINISH_TIME_MAX_LEN; } + }; + /// @@ -1243,7 +1372,7 @@ namespace BrewMonster.Scripts.Task public ATaskTempl m_pPrevSibling; public ATaskTempl m_pNextSibling; public ATaskTempl m_pFirstChild; - private byte m_uDepth; // 深度 // Depth + public byte m_uDepth; // 深度 // Depth const int MAX_TASK_NAME_LEN = 30; const int TASK_AWARD_MAX_CHANGE_VALUE = 255; @@ -1275,7 +1404,7 @@ namespace BrewMonster.Scripts.Task public string GetDescription() { - return ByteToStringUtils.UshortArrayToUnicodeString(m_pwstrDescript); + return ByteToStringUtils.UshortArrayToUnicodeString(m_pwstrDescript); } public bool CanFinishTask(TaskInterface pTask, ActiveTaskEntry pEntry, uint ulCurTime) diff --git a/Assets/PerfectWorld/Scripts/Task/UI/TaskWindow.cs b/Assets/PerfectWorld/Scripts/Task/UI/DlgTask.cs similarity index 92% rename from Assets/PerfectWorld/Scripts/Task/UI/TaskWindow.cs rename to Assets/PerfectWorld/Scripts/Task/UI/DlgTask.cs index 9264486616..d6165c25c5 100644 --- a/Assets/PerfectWorld/Scripts/Task/UI/TaskWindow.cs +++ b/Assets/PerfectWorld/Scripts/Task/UI/DlgTask.cs @@ -5,6 +5,7 @@ using BrewMonster.Managers; using BrewMonster.Scripts.Managers; using BrewMonster.Network; using BrewMonster.Scripts.Task; +using BrewMonster.UI; using CSNetwork.GPDataType; using ModelRenderer.Scripts.GameData; using NUnit.Framework; @@ -18,7 +19,7 @@ namespace BrewMonster.PerfectWorld.Scripts.Task.UI /// /// This is DlgTask.cpp /// - public class TaskWindow : MonoBehaviour + public class DlgTask : AUIDialog { #if UNITY_EDITOR [ContextMenu("Generate Tasks")] @@ -84,6 +85,7 @@ namespace BrewMonster.PerfectWorld.Scripts.Task.UI protected int m_iType; + [SerializeField] protected TMP_Text _nameTaskText; [SerializeField] protected TMP_Text m_pTxt_QuestNO; // PAUILABEL -> TMP_Text [SerializeField] protected TaskTreeView m_pTv_Quest; // PAUITREEVIEW -> GameObject container [SerializeField] protected TMP_Text m_pTxt_Content; // PAUITEXTAREA -> TMP_Text @@ -244,20 +246,22 @@ namespace BrewMonster.PerfectWorld.Scripts.Task.UI Color GetTaskColor(int idType) { // TODO: Map task type to color. Default white. - // if (idType < enumTTDaily || idType >= enumTTEnd) { - // ASSERT(false && "wrong task type"); - // return A3DCOLORRGB(255,255,255); - // } - // A3DCOLOR result; - // STRING_TO_A3DCOLOR(CECUIHelper::GetGameUIMan()->GetStringFromTable(idType - enumTTDaily + 3121), result); - // return result; - - - return Color.white; + if (idType < (int)ENUM_TASK_TYPE.enumTTDaily || idType >= (int)ENUM_TASK_TYPE.enumTTEnd) { + // ASSERT(false && "wrong task type"); + return Color.white; + } + Color result; + if (!EC_Utility.STRING_TO_A3DCOLOR(GetStringFromTable(idType - (int)ENUM_TASK_TYPE.enumTTDaily + 3121), out result)) + { + // 解析颜色失败,返回白色 + // [English] Failed to parse color, return white + result = Color.white; + } + return result; } // static A3DCOLOR GetTaskColor(const ATaskTempl *pTempl); // static ACString FormatTime(int nSec, const ACString& desc, int timeLimit); - private static string GetTaskNameWithColor(ATaskTempl pTempl) + private string GetTaskNameWithColor(ATaskTempl pTempl) { if (pTempl == null) return string.Empty; var type = (ENUM_TASK_TYPE)pTempl.m_FixedData.m_ulType; @@ -285,10 +289,21 @@ namespace BrewMonster.PerfectWorld.Scripts.Task.UI return name; } - private static UnityEngine.Color GetTaskColor(ATaskTempl pTempl) + private Color GetTaskColor(ATaskTempl pTempl) { // TODO: Map task type/flags to color. Default white. - return UnityEngine.Color.white; + // [English] Map task type/flags to color. Default white. + if (pTempl == null) return UnityEngine.Color.white; + int idType = (int)(ENUM_TASK_TYPE)pTempl.m_FixedData.m_ulType; + if (idType < (int)ENUM_TASK_TYPE.enumTTDaily || idType >= (int)ENUM_TASK_TYPE.enumTTEnd) { + // ASSERT(false && "wrong task type") + // [English] wrong task type + return UnityEngine.Color.white; + } + Color result; + EC_Utility.STRING_TO_A3DCOLOR(GetStringFromTable(idType - (int)ENUM_TASK_TYPE.enumTTDaily + 3121), out result); + return result; + // return UnityEngine.Color.white; } private static string A3DColorToString(UnityEngine.Color c) @@ -327,6 +342,7 @@ namespace BrewMonster.PerfectWorld.Scripts.Task.UI m_idLastTask = -2; m_pTxt_Content.SetText(""); m_pTxt_QuestItem.SetText(""); + _nameTaskText.SetText(""); m_pBtn_Abandon.interactable = false; UpdateTaskConfirm(0, false); Btn_TreasureMap.interactable = false; @@ -383,8 +399,10 @@ namespace BrewMonster.PerfectWorld.Scripts.Task.UI { if( idTask != m_idLastTask ) { + _nameTaskText.SetText(EC_Utility.FormatForTextMeshPro(GetTaskNameWithColor(pTemp))); + //pTextDesc->SetText(FormatTaskText(pTemp->GetDescription(), pTextDesc->GetColor())); - pTextDesc.SetText(pTemp.GetDescription()); + pTextDesc.SetText(EC_Utility.FormatForTextMeshPro(pTemp.GetDescription())); m_idLastTask = idTask; bLastTaskChanged = true; } @@ -493,7 +511,7 @@ namespace BrewMonster.PerfectWorld.Scripts.Task.UI // Update description when the selected task changes if (idTask != m_idLastTask) { - if (pTextDesc != null) pTextDesc.SetText(pTemp.GetDescription()); + if (pTextDesc != null) pTextDesc.SetText(EC_Utility.FormatForTextMeshPro(pTemp.GetDescription())); m_idLastTask = idTask; bLastTaskChanged = true; } @@ -714,9 +732,10 @@ namespace BrewMonster.PerfectWorld.Scripts.Task.UI // UnityEngine.UI.ScrollRect scrollRect = pTextItem.GetComponentInParent(); // float oldNormPos = scrollRect != null ? scrollRect.verticalNormalizedPosition : 0f; - if (!string.Equals(strNewTextItem, pTextItem.text)) + string formatted = EC_Utility.FormatForTextMeshPro(strNewTextItem ?? string.Empty); + if (!string.Equals(formatted, pTextItem.text)) { - pTextItem.text = strNewTextItem ?? string.Empty; + pTextItem.text = formatted; // TODO: apply hint to a tooltip UI if available (strNewHintItem) } @@ -742,43 +761,41 @@ namespace BrewMonster.PerfectWorld.Scripts.Task.UI var sb = new System.Text.StringBuilder(); int colCount = 0; const int col = 3; - int cellSpace = 30; + int cellSpace = 50; // adjust as needed for spacing + + void AppendWithSpacing(int stringId, string value) + { + var title = EC_Utility.FormatForTextMeshPro(GetStringFromTable(stringId)); + var text = $"{title} {value}"; + sb.Append( EC_Utility.FormatForTextMeshPro(text)); + sb.Append(' ', Mathf.Abs(cellSpace - text.Length)); + if ((++colCount) % col == 0) sb.Append("\n"); + } + if (award.m_ulGold > 0) { - var text = $"{GetStringFromTable(3201)} {award.m_ulGold}"; - sb.Append(text); - sb.Append(' ', cellSpace - text.Length); - if ((++colCount) % col == 0) sb.Append("\n"); + AppendWithSpacing(3201 , award.m_ulGold.ToString()); } if (award.m_ulExp > 0) { - var text = $"{GetStringFromTable(3201)} {award.m_ulExp}"; - sb.Append(text); - sb.Append(' ', cellSpace - text.Length); - if ((++colCount) % col == 0) sb.Append("\n"); + AppendWithSpacing(3202 , award.m_ulExp.ToString()); } if (award.m_ulSP > 0) { - var text = $"{GetStringFromTable(3201)} {award.m_ulSP}"; - sb.Append(text); - sb.Append(' ', cellSpace - text.Length); - if ((++colCount) % col == 0) sb.Append("\n"); + AppendWithSpacing(3203 , award.m_ulSP.ToString()); } if (award.m_ulRealmExp > 0) { - sb.Append($"{GetStringFromTable(3207)} {award.m_ulRealmExp}"); - if ((++colCount) % col == 0) sb.Append("\n"); + AppendWithSpacing( 3207, award.m_ulRealmExp.ToString()); } if (award.m_iForceContrib > 0) { - sb.Append($"{GetStringFromTable(3205)} {award.m_iForceContrib}"); - if ((++colCount) % col == 0) sb.Append("\n"); + AppendWithSpacing(3205, award.m_iForceContrib.ToString()); } if (award.m_iForceRepu > 0) { - sb.Append($"{GetStringFromTable(3206)} {award.m_iForceRepu}"); - if ((++colCount) % col == 0) sb.Append("\n"); + AppendWithSpacing(3206, award.m_iForceRepu.ToString()); } if (sb.Length > 0 && m_pTxt_BaseAward != null) @@ -798,11 +815,11 @@ namespace BrewMonster.PerfectWorld.Scripts.Task.UI if (m_pTxt_BaseAward != null && m_pTxt_BaseAward.gameObject.activeSelf) { string strAward = (GetStringFromTable(3204) ?? string.Empty) + "\n" + (m_pTxt_BaseAward.text ?? string.Empty); - m_pTxt_BaseAward.text = strAward; + m_pTxt_BaseAward.text = EC_Utility.FormatForTextMeshPro(strAward); } else if (m_pTxt_BaseAward != null) { - m_pTxt_BaseAward.text = GetStringFromTable(3204) ?? string.Empty; + m_pTxt_BaseAward.text = EC_Utility.FormatForTextMeshPro(GetStringFromTable(3204) ?? string.Empty); m_pTxt_BaseAward.gameObject.SetActive(true); } } @@ -910,53 +927,53 @@ namespace BrewMonster.PerfectWorld.Scripts.Task.UI return $"{label}{ts:hh\\:mm\\:ss}\n"; } - private static string GetStringFromTable(int id) - { - // TODO: return AUIManager.GetStringFromTable(id); - - // HARD CODED STRINGS - switch (id) - { - case 3101: - return "Hàng ngày"; - case 3102: - return "Tu chân"; - case 3103: - return "Chủ tuyến"; - case 3104: - return "Phụ tuyến"; - case 3105: - return "Event"; - case 3106: - return "7 Killer List"; - case 3107: - return "Bang hội"; - case 3108: - return "Management"; - case 3109: - return "Huyền thoại"; - case 3110: - return "Câu hỏi"; - case 3201: - return "Gold:"; - case 3202: - return "EXP:"; - case 3203: - return "SP:"; - case 3207: - return "Realm EXP:"; - case 3205: - return "Contribution:"; - case 3206: - return "Reputation:"; - case 7621: - return "NPC giao nhiệm vụ: "; - default: - return $"UnKnown_{id} "; - } - } + // private static string GetStringFromTable(int id) + // { + // // TODO: return AUIManager.GetStringFromTable(id); + // + // // HARD CODED STRINGS + // switch (id) + // { + // case 3101: + // return "Hàng ngày"; + // case 3102: + // return "Tu chân"; + // case 3103: + // return "Chủ tuyến"; + // case 3104: + // return "Phụ tuyến"; + // case 3105: + // return "Event"; + // case 3106: + // return "7 Killer List"; + // case 3107: + // return "Bang hội"; + // case 3108: + // return "Management"; + // case 3109: + // return "Huyền thoại"; + // case 3110: + // return "Câu hỏi"; + // case 3201: + // return "Gold:"; + // case 3202: + // return "EXP:"; + // case 3203: + // return "SP:"; + // case 3207: + // return "Realm EXP:"; + // case 3205: + // return "Contribution:"; + // case 3206: + // return "Reputation:"; + // case 7621: + // return "NPC giao nhiệm vụ: "; + // default: + // return $"UnKnown_{id} "; + // } + // } - private static string GetStringFromTable(uint id) + private string GetStringFromTable(uint id) { // return AUIManager.GetStringFromTable(id); return GetStringFromTable((int)id); @@ -1242,7 +1259,8 @@ namespace BrewMonster.PerfectWorld.Scripts.Task.UI m_pBtn_FinishTask.gameObject.SetActive(true); // TODO: Enable/disable based on task readiness // m_pBtn_FinishTask->Enable(pTask->IsTaskReadyToConfirm(idTask)); - + m_pBtn_FinishTask.interactable = pTask.IsTaskReadyToConfirm(idTask); + } else m_pBtn_FinishTask.gameObject.SetActive(false); } diff --git a/Assets/PerfectWorld/Scripts/Task/UI/TaskWindow.cs.meta b/Assets/PerfectWorld/Scripts/Task/UI/DlgTask.cs.meta similarity index 100% rename from Assets/PerfectWorld/Scripts/Task/UI/TaskWindow.cs.meta rename to Assets/PerfectWorld/Scripts/Task/UI/DlgTask.cs.meta diff --git a/Assets/PerfectWorld/Scripts/UI/AUIManager.cs b/Assets/PerfectWorld/Scripts/UI/AUIManager.cs index f50a08f130..310ba92f32 100644 --- a/Assets/PerfectWorld/Scripts/UI/AUIManager.cs +++ b/Assets/PerfectWorld/Scripts/UI/AUIManager.cs @@ -171,18 +171,18 @@ namespace BrewMonster.UI //} //s.Close(); - //if (a_stricmp(GetStringFromTable(1), _AL("")) == 0) //1 Ĭ - // m_StringTable[1] = _AL("ϸһ"); + //if (a_stricmp(GetStringFromTable(1), _AL("")) == 0) //1 Ĭ������ + // m_StringTable[1] = _AL("����ϸ��һ����"); //m_strDefaultFontName = GetStringFromTable(1); - //if (a_stricmp(GetStringFromTable(2), _AL("")) == 0) //2 ĬС + //if (a_stricmp(GetStringFromTable(2), _AL("")) == 0) //2 Ĭ�������С // m_StringTable[2] = _AL("10"); //m_nDefaultFontSize = a_atoi(GetStringFromTable(2)); - //if (a_stricmp(GetStringFromTable(3), _AL("")) == 0) //3 '\t' ൱ڶٸ 'W'Ŀ + //if (a_stricmp(GetStringFromTable(3), _AL("")) == 0) //3 ���� '\t' �൱�ڶ��ٸ� 'W'�Ŀ��� // m_StringTable[3] = _AL("30"); //_tab_char = a_atoi(GetStringFromTable(3)); - //if (a_stricmp(GetStringFromTable(4), _AL("")) == 0) //4 m_FontImagePicture С + //if (a_stricmp(GetStringFromTable(4), _AL("")) == 0) //4 m_FontImagePicture �����С // m_StringTable[4] = m_StringTable[2]; - //if (a_stricmp(GetStringFromTable(5), _AL("")) == 0) //5 MessageBox С + //if (a_stricmp(GetStringFromTable(5), _AL("")) == 0) //5 MessageBox �����С // m_StringTable[5] = m_StringTable[2]; //if (a_stricmp(GetStringFromTable(6), _AL("")) == 0) //6 MessageBox shadow // m_StringTable[6] = _AL("0"); diff --git a/Assets/Scripts/CECHostPlayer.Task.cs b/Assets/Scripts/CECHostPlayer.Task.cs index a4c9e519f9..6e60c39c98 100644 --- a/Assets/Scripts/CECHostPlayer.Task.cs +++ b/Assets/Scripts/CECHostPlayer.Task.cs @@ -66,9 +66,9 @@ public partial class CECHostPlayer if (header == CommandID.TASK_DATA) { -#if !LOAD_TASK_TEMPL - return; // Task templates loading not implemented in C# -#endif + +#if LOAD_TASK_TEMPL // only load on developer project + // Parse aggregated task buffers cmd_task_data pCmd = cmd_task_data.FromBuffer(pDataBuf); // cmd_task_data pCmd = GPDataTypeHelper.FromBytes(pDataBuf); @@ -89,6 +89,8 @@ public partial class CECHostPlayer } m_pTaskInterface.CheckPQEnterWorldInit(); + +#endif // check if player has equipped goblin (not yet implemented in C#) // TODO: implement goblin initialization when equipment system is ready @@ -114,7 +116,8 @@ public partial class CECHostPlayer private void OnServerNotify(CECTaskInterface pInterface, byte[] data, int size) { - // TODO: Implement server notify handling for task var data + TaskClient.OnServerNotify(pInterface, data, (uint)size); + } } diff --git a/Assets/Scripts/EC_Utility.cs b/Assets/Scripts/EC_Utility.cs index 0a2041baa4..075e0f7b07 100644 --- a/Assets/Scripts/EC_Utility.cs +++ b/Assets/Scripts/EC_Utility.cs @@ -4,6 +4,7 @@ using System; using System.Collections; using System.Runtime.InteropServices; using System.Text; +using System.Text.RegularExpressions; using UnityEngine; using static CECPlayer; @@ -157,4 +158,117 @@ public static class EC_Utility { return (y.CompareTo(x) < 0) ? y : x; } + + public static bool STRING_TO_A3DCOLOR(string str, out Color clr) + { + // ��� A3DCOLOR_TO_STRING ������ת�� + // [English] Parse according to A3DCOLOR_TO_STRING conversion format + + bool ret = false; + clr = Color.white; + + byte[] rgb = new byte[6]; + + int nLen = str != null ? str.Length : 0; + while (nLen == 7) + { + // ����ĸ��ת����Сд + // [English] Convert uppercase letters to lowercase + str = str.ToLowerInvariant(); + + if (str[0] != '^') + break; + + char c; + int i; + for (i = 1; i < nLen; ++i) + { + c = str[i]; + if (c >= '0' && c <= '9') + { + rgb[i - 1] = (byte)(c - '0'); + } + else if (c >= 'a' && c <= 'f') + { + rgb[i - 1] = (byte)(10 + (c - 'a')); + } + else + { + break; + } + } + if (i < nLen) + break; + + byte r = (byte)((rgb[0] << 4) + rgb[1]); + byte g = (byte)((rgb[2] << 4) + rgb[3]); + byte b = (byte)((rgb[4] << 4) + rgb[5]); + clr = new Color(r / 255f, g / 255f, b / 255f, 1f); + + ret = true; + break; + } + + return ret; + } + + + // ===== Text/Color helpers moved from EC_InventoryUI ===== + public static string FormatForLegacyText(string text) + { + if (string.IsNullOrEmpty(text)) + return string.Empty; + + StringBuilder result = new StringBuilder(text); + result.Replace("\\r", "\n"); + return ProcessColorCodesForLegacy(result); + } + + public static string FormatForTextMeshPro(string text) + { + if (string.IsNullOrEmpty(text)) + return string.Empty; + + StringBuilder result = new StringBuilder(text); + result.Replace("\\r", "\n"); + return ProcessColorCodes(result); + } + + public static string ProcessColorCodes(StringBuilder text) + { + string pattern = @"\^([0-9A-Fa-f]{6})"; + return Regex.Replace(text.ToString(), pattern, match => + { + string hexColor = match.Groups[1].Value; + return $""; + }, RegexOptions.None); + } + + public static string ProcessColorCodesForLegacy(StringBuilder text) + { + string pattern = @"\^([0-9A-Fa-f]{6})"; + return Regex.Replace(text.ToString(), pattern, match => + { + string hexColor = match.Groups[1].Value; + Color color = HexToColor(hexColor); + return $""; + }, RegexOptions.None); + } + + private static Color HexToColor(string hex) + { + if (hex == null || hex.Length != 6) + return Color.white; + try + { + int r = Convert.ToInt32(hex.Substring(0, 2), 16); + int g = Convert.ToInt32(hex.Substring(2, 2), 16); + int b = Convert.ToInt32(hex.Substring(4, 2), 16); + return new Color(r / 255f, g / 255f, b / 255f, 1f); + } + catch + { + return Color.white; + } + } }