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;
+ }
+ }
}