596 lines
22 KiB
C#
596 lines
22 KiB
C#
using System;
|
||
using System.Reflection;
|
||
using System.Runtime.InteropServices;
|
||
using BrewMonster.Network;
|
||
using BrewMonster.Scripts.Task;
|
||
using BrewMonster.UI;
|
||
using CSNetwork.GPDataType;
|
||
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;
|
||
|
||
// Throttle CHECK_FINISH notifications per task to avoid spamming the server every tick.
|
||
private static readonly System.Collections.Generic.Dictionary<uint, uint> s_lastCheckFinishAt = new();
|
||
|
||
public static void OnTaskCheckStatus(TaskInterface pTask)
|
||
{
|
||
// 版本与交付合法性检查 // Version and deliver legality check
|
||
// CheckVersion not exposed on TaskInterface; skipping version check
|
||
if (pTask == null ||
|
||
!pTask.CheckVersion() ||
|
||
!pTask.IsDeliverLegal())
|
||
return;
|
||
|
||
|
||
// 读取激活任务列表 // 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.m_ulTemplAddr == 0)
|
||
{
|
||
// assert(false) // English: unexpected empty template
|
||
continue;
|
||
}
|
||
|
||
ATaskTempl pTempl = CurEntry.GetTempl();
|
||
if (pTempl == null) continue;
|
||
|
||
// IsValidState from C++ not found in managed port; skip validity-state check
|
||
if (!pTempl.IsValidState())
|
||
continue;
|
||
// PQ子任务 // PQ subtask
|
||
if (pTempl.m_FixedData.m_bPQSubTask)
|
||
{
|
||
// CheckGlobalPQKeyValue(true) not ported; if implemented and returns 0, notify server then continue
|
||
if(pTempl.CheckGlobalPQKeyValue(true) == 0)
|
||
{
|
||
pTempl.IncValidCount();
|
||
_notify_svr(pTask, ClientNotificationConstants.TASK_CLT_NOTIFY_CHECK_FINISH, CurEntry.m_ID);
|
||
continue;
|
||
}
|
||
}
|
||
|
||
// 超时判断 // 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)
|
||
{
|
||
// Mirror C++: if abs-fail time has passed, ask server to check/finish (will mark fail if needed).
|
||
long sec = ulCurTime - (long)(TaskInterface.GetTimeZoneBias() * 60);
|
||
if (sec < 0) sec = 0;
|
||
DateTime cur = DateTimeOffset.FromUnixTimeSeconds(sec).UtcDateTime;
|
||
if (pTempl.m_FixedData.m_tmAbsFailTime.before(cur))
|
||
{
|
||
pTempl.IncValidCount();
|
||
_notify_svr(pTask, (int)ClientNotificationConstants.TASK_CLT_NOTIFY_CHECK_FINISH, CurEntry.m_ID);
|
||
continue;
|
||
}
|
||
}
|
||
|
||
// 进入或离开区域导致失败 // 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 num_window == 1 but windows is null/empty, treat as no options (send notification)
|
||
bool shouldNotifyDirectly = false;
|
||
if (pTalk.num_window == 0)
|
||
{
|
||
shouldNotifyDirectly = true;
|
||
}
|
||
else if (pTalk.num_window == 1)
|
||
{
|
||
if (pTalk.windows == null || pTalk.windows.Length == 0)
|
||
{
|
||
// Invalid state: num_window == 1 but windows is null/empty - treat as no options
|
||
shouldNotifyDirectly = true;
|
||
}
|
||
else if (pTalk.windows[0].num_option == 0)
|
||
{
|
||
shouldNotifyDirectly = true;
|
||
}
|
||
}
|
||
|
||
if (shouldNotifyDirectly)
|
||
{
|
||
pTempl.IncValidCount();
|
||
_notify_svr(pTask, (byte)ClientNotificationConstants.TASK_CLT_NOTIFY_REACH_SITE, (ushort)pTempl.GetID());
|
||
}
|
||
else
|
||
{
|
||
// 弹出任务完成对话框(奖励对话有选项) // Popup finish dialog when award talk has options
|
||
var uiMan = EC_Game.GetGameRun()?.GetUIManager()?.GetInGameUIMan();
|
||
if (uiMan != null)
|
||
{
|
||
// check if this is the main thread
|
||
uiMan.PopupTaskFinishDialog(pTempl.GetID(), pTalk);
|
||
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[] leavePos = new float[3];
|
||
uint leaveWorldId = (uint)pTask.GetPos(leavePos);
|
||
bool regRet = false;
|
||
|
||
if (leaveWorldId == 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, leavePos))
|
||
{
|
||
regRet = true;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
if (!regRet)
|
||
{
|
||
var pTalk = pTempl.m_AwardTalk;
|
||
// If num_window == 1 but windows is null/empty, treat as no options (send notification)
|
||
bool shouldNotifyDirectly = false;
|
||
if (pTalk.num_window == 0)
|
||
{
|
||
shouldNotifyDirectly = true;
|
||
}
|
||
else if (pTalk.num_window == 1)
|
||
{
|
||
if (pTalk.windows == null || pTalk.windows.Length == 0)
|
||
{
|
||
// Invalid state: num_window == 1 but windows is null/empty - treat as no options
|
||
shouldNotifyDirectly = true;
|
||
}
|
||
else if (pTalk.windows[0].num_option == 0)
|
||
{
|
||
shouldNotifyDirectly = true;
|
||
}
|
||
}
|
||
|
||
if (shouldNotifyDirectly)
|
||
{
|
||
pTempl.IncValidCount();
|
||
_notify_svr(pTask, (byte)ClientNotificationConstants.TASK_CLT_NOTIFY_LEAVE_SITE, (ushort)pTempl.GetID());
|
||
}
|
||
else
|
||
{
|
||
// 弹出任务完成对话框(奖励对话有选项) // Popup finish dialog when award talk has options
|
||
var uiMan = EC_Game.GetGameRun()?.GetUIManager()?.GetInGameUIMan();
|
||
if (uiMan != null)
|
||
{
|
||
uiMan.PopupTaskFinishDialog(pTempl.GetID(), pTalk);
|
||
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
|
||
{
|
||
// Minimal behavior: for wait-time tasks, auto request server check when time is up.
|
||
UpdateTaskToConfirm(pTask, pTempl, CurEntry, bNeedServerCheck, ulCurTime);
|
||
}
|
||
}
|
||
}
|
||
|
||
// ATaskTemplMan.UpdateStatus(pTask) not found in C# port; skipping
|
||
GetTaskTemplMan().UpdateStatus(pTask);
|
||
}
|
||
|
||
// ===== 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;
|
||
}
|
||
|
||
public static void _notify_svr(TaskInterface pTask, byte uReason, ushort uTaskID)
|
||
{
|
||
ATaskTempl._notify_svr(pTask, uReason, uTaskID);
|
||
}
|
||
|
||
// 更新“待确认任务” / 最小实现:当客户端确认已满足完成条件时,发一次 CHECK_FINISH 给服务器(节流)
|
||
// English: Minimal port: when conditions are met client-side, send CHECK_FINISH once (throttled).
|
||
private static void UpdateTaskToConfirm(TaskInterface pTask, ATaskTempl pTempl, ActiveTaskEntry entry, bool needServerCheck, uint ulCurTime)
|
||
{
|
||
if (!needServerCheck || pTask == null || pTempl == null || entry == null) return;
|
||
|
||
// Auto-check for wait-time tasks and simple client tasks (emote/action tasks)
|
||
TaskCompletionMethod method = (TaskCompletionMethod)pTempl.m_FixedData.m_enumMethod;
|
||
if (method != TaskCompletionMethod.enumTMWaitTime &&
|
||
method != TaskCompletionMethod.enumTMSimpleClientTask)
|
||
return;
|
||
|
||
if (entry.IsFinished()) return;
|
||
|
||
uint id = entry.m_ID;
|
||
if (id == 0) return;
|
||
|
||
if (s_lastCheckFinishAt.TryGetValue(id, out uint last) && ulCurTime <= last + 1)
|
||
return;
|
||
s_lastCheckFinishAt[id] = ulCurTime;
|
||
|
||
pTempl.IncValidCount();
|
||
_notify_svr(pTask, (int)ClientNotificationConstants.TASK_CLT_NOTIFY_CHECK_FINISH, (ushort)id);
|
||
}
|
||
|
||
// Handle server notification for task updates
|
||
public static void OnServerNotify(TaskInterface pTask, byte[] pBuf, uint sz)
|
||
{
|
||
// Check version validity
|
||
// CheckVersion not exposed on TaskInterface; skipping version check
|
||
if (!pTask.CheckVersion())
|
||
return;
|
||
|
||
// Validate buffer size for base notification structure
|
||
if (sz < (uint)Marshal.SizeOf<task_notify_base>()) return;
|
||
|
||
// Marshal base notification structure from buffer
|
||
task_notify_base pNotify = GPDataTypeHelper.FromBytes<task_notify_base>(pBuf);
|
||
//BMLogger.Log($"[MH Task] TaskClient.OnServerNotify: reason={pNotify.reason}, task={pNotify.task}");
|
||
|
||
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<svr_task_err_code>()) 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)
|
||
{
|
||
// OnForgetLivingSkill method not found in ATaskTemplMan; implement if needed
|
||
ATaskTemplMan pMan = GetTaskTemplMan();
|
||
if (pMan != null) pMan.OnForgetLivingSkill(pTask);
|
||
return;
|
||
}
|
||
// Handle new task notification
|
||
else if (pNotify.reason == TaskTemplConstants.TASK_SVR_NOTIFY_NEW)
|
||
{
|
||
ATaskTemplMan pMan = GetTaskTemplMan();
|
||
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<svr_task_dyn_time_mark>()) return;
|
||
// TODO: OnDynTasksTimeMark method not found in ATaskTemplMan; implement if needed
|
||
ATaskTemplMan pMan = GetTaskTemplMan();
|
||
if (pMan != null)
|
||
{
|
||
svr_task_dyn_time_mark dynMark = GPDataTypeHelper.FromBytes<svr_task_dyn_time_mark>(pBuf);
|
||
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<task_notify_base>()) return;
|
||
// TODO: OnDynTasksData method not found in ATaskTemplMan; implement if needed
|
||
ATaskTemplMan pMan = GetTaskTemplMan();
|
||
if (pMan != null)
|
||
{
|
||
byte[] dynData = new byte[sz - Marshal.SizeOf<task_notify_base>()];
|
||
Array.Copy(pBuf, Marshal.SizeOf<task_notify_base>(), dynData, 0, dynData.Length);
|
||
pMan.OnDynTasksData(pTask, dynData, dynData.Length, pNotify.task != 0);
|
||
}
|
||
return;
|
||
}
|
||
// Handle storage data notification
|
||
else if (pNotify.reason == TaskTemplConstants.TASK_SVR_NOTIFY_STORAGE)
|
||
{
|
||
if (sz != Marshal.SizeOf<task_notify_base>() + Marshal.SizeOf<StorageTaskList>()) return;
|
||
ATaskTemplMan pMan = GetTaskTemplMan();
|
||
if (pMan != null)
|
||
{
|
||
byte[] storageData = new byte[Marshal.SizeOf<StorageTaskList>()];
|
||
Array.Copy(pBuf, Marshal.SizeOf<task_notify_base>(), storageData, 0, storageData.Length);
|
||
pMan.OnStorageData(pTask, storageData);
|
||
}
|
||
pTask.UpdateTaskUI(pNotify.task, pNotify.reason);
|
||
return;
|
||
}
|
||
// Handle special award notification
|
||
else if (pNotify.reason == TaskTemplConstants.TASK_SVR_NOTIFY_SPECIAL_AWARD)
|
||
{
|
||
// Tsvr_task_special_award and special_award structs not defined; need to define
|
||
if (sz != Marshal.SizeOf<svr_task_special_award>()) return;
|
||
ATaskTemplMan pMan = GetTaskTemplMan();
|
||
if (pMan != null)
|
||
{
|
||
svr_task_special_award awardNotify = GPDataTypeHelper.FromBytes<svr_task_special_award>(pBuf);
|
||
pMan.OnSpecialAward(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
|
||
// CECGameUIMan* pGameUI = g_pGame->GetGameRun()->GetUIManager()->GetInGameUIMan();
|
||
// pGameUI->PopupNewbieGiftRemind();
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
// Handle task limit increase notification
|
||
else if (pNotify.reason == TaskTemplConstants.TASK_SVR_NOTIFY_SET_TASK_LIMIT)
|
||
{
|
||
ActiveTaskList pLst = TryGetActiveList(pTask);
|
||
if (pLst != null)
|
||
{
|
||
// ExpandMaxSimultaneousCount method not implemented; implement if needed
|
||
pLst.ExpandMaxSimultaneousCount();
|
||
}
|
||
// PopChatMessage static method and FIXMSG_TASK_LIMIT_INCREASED constant not found
|
||
pTask.PopChatMessage((int)FixedMsg.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>())
|
||
{
|
||
svr_monster_killed pKilled = GPDataTypeHelper.FromBytes<svr_monster_killed>(pBuf) ;//Marshal.PtrToStructure<svr_monster_killed>(pNotify.AddrOfPinnedObject());
|
||
if (pKilled.monster_num >= 2)
|
||
{
|
||
// CECAutoTeam pAutoTeam = EC_Game.GetGameRun().GetHostPlayer().GetAutoTeam();
|
||
// pAutoTeam.DoAutoTeam((int)CECAutoTeam.AutoTeamType.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 )
|
||
{
|
||
// TODO: CECUIHelper.OnTaskCompleted not found; implement UI update if needed
|
||
// CECUIHelper.OnTaskCompleted(pNotify.task);
|
||
}
|
||
else if (pNotify.reason == TaskTemplConstants.TASK_SVR_NOTIFY_GIVE_UP)
|
||
{
|
||
ActiveTaskList pLst = TryGetActiveList(pTask);
|
||
if (pLst != null)
|
||
{
|
||
pLst.ClearTask(pTask, pEntry, false);
|
||
}
|
||
pLst.ClearTask(pTask, pEntry, false);
|
||
if (pTempl.m_FixedData.m_bDisplayInTitleTaskUI)
|
||
pTask.UpdateTaskUI(pTempl.m_FixedData.m_ID, TaskTemplConstants.TASK_SVR_NOTIFY_GIVE_UP);
|
||
//if ((pTempl.m_FixedData.m_enumMethod == (uint)TaskCompletionMethod.enumTMSimpleClientTask) && pTempl.m_FixedData.m_uiEmotion > 0)
|
||
//pTask.UpdateTaskUI(pTempl.m_FixedData.m_ID, TaskTemplConstants.TASK_SVR_NOTIFY_GIVE_UP);
|
||
|
||
pTask.OnGiveupTask((int)pTempl.m_FixedData.m_ID);
|
||
}
|
||
// 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();
|
||
// OnServerNotify method signature may need adjustment for C# (ref/out parameters)
|
||
pTempl.OnServerNotify(pTask, pEntry, pNotify, sz, pBuf);
|
||
}
|
||
|
||
// Helper method to get task template manager
|
||
private static ATaskTemplMan GetTaskTemplMan()
|
||
{
|
||
return EC_Game.GetTaskTemplateMan();
|
||
}
|
||
#endif
|
||
}
|
||
} |