using BrewMonster.Network; using BrewMonster.Scripts; using BrewMonster.UI; using CSNetwork; using CSNetwork.GPDataType; using Cysharp.Threading.Tasks; using System; using System.Runtime.InteropServices; using UnityEngine; using static BrewMonster.Scripts.CECHPWork; namespace BrewMonster { public partial class CECHostPlayer { public void OnMsgHstCorrectPos(in ECMSG Msg) { //Debug.LogError("HoangDev : OnMsgHstCorrectPos"); byte[] buf = (byte[])Msg.dwParam1; // chỗ bạn lưu pDataBuf GCHandle handle = GCHandle.Alloc(buf, GCHandleType.Pinned); cmd_host_correct_pos pCmd = (cmd_host_correct_pos)Marshal.PtrToStructure( handle.AddrOfPinnedObject(), typeof(cmd_host_correct_pos)); handle.Free(); //cmd_host_correct_pos pCmd = GPDataTypeHelper.FromBytes((byte[])Msg.dwParam1); // Debug.LogError("HoangDev :pCmd.pos " + pCmd.pos); SetPos(pCmd.pos); m_vVelocity.Clear(); m_CDRInfo.vAbsVelocity.Clear(); m_MoveCtrl.SetMoveStamp(pCmd.stamp); } public void OnMsgHstSetMoveStamp(ECMSG Msg) { cmd_set_move_stamp pCmd = GPDataTypeHelper.FromBytes((byte[])Msg.dwParam1); m_MoveCtrl.SetMoveStamp(pCmd.move_stamp); } public void OnMsgHstGoto(in ECMSG Msg) { PopupManager.NotifyPlayerRevived(); // p1 is a byte[] buffer; parse into cmd_notify_hostpos then set position // p1 是一个 byte[] 缓冲区;解析为 cmd_notify_hostpos 然后设置位置 byte[] buf = (byte[])Msg.dwParam1; cmd_notify_hostpos pCmd = GPDataTypeHelper.FromBytes(buf); int idInst = pCmd.tag; Vector3 vPos = new Vector3(pCmd.vPos.x, pCmd.vPos.y, pCmd.vPos.z); int iLine = pCmd.line; // Call Goto method to properly handle teleportation // 调用 Goto 方法来正确处理传送 if (!Goto(idInst, vPos, iLine)) { BMLogger.LogError($"OnMsgHstGoto: Failed to teleport to instance {idInst}"); return; } } void OnMsgHstWayPoint(ECMSG Msg) { CECGameUIMan pGameUI = CECUIManager.Instance.GetInGameUIMan(); if (Convert.ToInt32(Msg.dwParam2) == CommandID.ACTIVATE_WAYPOINT) { cmd_activate_waypoint pCmd = GPDataTypeHelper.FromBytes((byte[])Msg.dwParam1); m_aWayPoints.Add(pCmd.waypoint); // add to waypoints array // pGameUI.GetMapDlgsMgr().UpdateWayPoints(&pCmd.waypoint, 1, false); } else if (Convert.ToInt32(Msg.dwParam2) == CommandID.WAYPOINT_LIST) { cmd_waypoint_list pCmd = new cmd_waypoint_list(); pCmd.FromBytes((byte[])Msg.dwParam1); for (int i=0; i < pCmd.count; i++) m_aWayPoints.Add(pCmd.list[i]); // update the whole list pGameUI.GetMapDlgsMgr().UpdateWayPoints(pCmd.list, pCmd.count, true); } } private void OnMsgHstPressCancel(ECMSG Msg) { CECHPWork pCurWork = null; pCurWork = m_pWorkMan.GetRunningWork(Host_work_ID.WORK_TRACEOBJECT); if (pCurWork is CECHPWorkTrace workTrace) { workTrace.PressCancel(); return; } pCurWork = m_pWorkMan.GetRunningWork(Host_work_ID.WORK_HACKOBJECT); if (pCurWork != null) { UnityGameSession.c2s_CmdCancelAction(); return; } pCurWork = m_pWorkMan.GetRunningWork(Host_work_ID.WORK_USEITEM); if (pCurWork != null) { UnityGameSession.c2s_CmdCancelAction(); return; } pCurWork = m_pWorkMan.GetRunningWork(Host_work_ID.WORK_SPELLOBJECT); if (pCurWork != null) { int iState = ((CECHPWorkSpell)pCurWork).GetState(); if (iState == CECHPWorkSpell.Spell_magic_state.ST_INCANT) { UnityGameSession.c2s_CmdCancelAction(); return; } } pCurWork = m_pWorkMan.GetRunningWork(Host_work_ID.WORK_PICKUP); if (pCurWork != null) { if (((EC_HPWorkPick)pCurWork).IsGather()) { UnityGameSession.c2s_CmdCancelAction(); return; } } //todo: handle this part // pCurWork = m_pWorkMan.GetRunningWork(Host_work_ID.WORK_CONCENTRATE); // if (pCurWork !=null){ // if (IsOperatingPet()){ // UnityGameSession.c2s_CmdCancelAction(); // return; // } // } // pCurWork = m_pWorkMan.GetRunningWork(Host_work_ID.WORK_CONGREGATE); // if (pCurWork !=null){ // if (IsCongregating()){ // UnityGameSession.c2s_CmdCancelAction(); // return; // } // } if (m_bUsingTrashBox || DoingSessionPose()) { UnityGameSession.c2s_CmdCancelAction(); return; } // Cancel current selection if (m_idSelTarget > 0) { SelectTarget(0); return; } // Some work have lower priority pCurWork = m_pWorkMan.GetRunningWork(Host_work_ID.WORK_MOVETOPOS); if (pCurWork != null) { ((CECHPWorkMove)pCurWork).PressCancel(); return; } } public void StopMovement() { m_MoveCtrl.SendStopMoveCmd(playerTransform.position, 5f, (int)GPMoveMode.GP_MOVE_WALK); } // Return to a target town through skill // 通过技能返回目标城镇 // This method implements the Goto logic from the original C++ code // 此方法实现了原始 C++ 代码中的 Goto 逻辑 private bool Goto(int idInst, Vector3 vPos, int iParallelWorldID) { Action actDone = () => { // Stop all current work and goto specified position // 停止所有当前工作并转到指定位置 if (m_pWorkMan != null) { // Stop auto-moving if active // 如果正在自动移动则停止 // Note: IsAutoMoving check would go here if available // 注意:如果可用,IsAutoMoving 检查将放在这里 if (IsAutoMoving()) { CECHPWorkMove pWorkMove = (m_pWorkMan.GetRunningWork(CECHPWork.Host_work_ID.WORK_MOVETOPOS) as CECHPWorkMove); pWorkMove.SetUseAutoMoveDialog(false); pWorkMove.PressCancel(); pWorkMove.Finish(); } // Finish all work // 完成所有工作 m_pWorkMan.FinishAllWork(true); } if (!IsFlying()) ShowWing(false); // Add a little height to ensure player's AABB won't embed with building // 增加一点高度以确保玩家的 AABB 不会嵌入建筑物 vPos += EC_Utility.ToVector3(GPDataTypeHelper.g_vAxisY) * 0.1f; // Ensure we are not under ground (terrain height check would go here) // 确保我们不会在地下(地形高度检查将放在这里) // Note: Terrain height check is skipped for now as it requires world access // 注意:暂时跳过地形高度检查,因为它需要世界访问 A3DVECTOR3 vNormal = new A3DVECTOR3(); float vTerrainHeight = vPos.y; if (Physics.RaycastNonAlloc(vPos, (Vector3.down), hits, 1000f, 1 << 6) > 0) { vTerrainHeight = hits[0].point.y; vNormal = EC_Utility.ToA3DVECTOR3(hits[0].normal); } if (vPos.y < vTerrainHeight) { vPos.y = vTerrainHeight; } // Set position // 设置位置 SetPos(vPos); m_CDRInfo.vTPNormal = vPos.y <= vTerrainHeight + 0.1f ? vNormal : g_vOrigin; m_CDRInfo.fYVel = 0.0f; m_CDRInfo.vAbsVelocity.Clear(); // Reset jump state if available // 如果可用则重置跳跃状态 ResetJump(); // Uncomment if ResetJump method exists m_MoveCtrl.SetHostLastPos(EC_Utility.ToA3DVECTOR3(vPos)); m_MoveCtrl.SetLastSevPos(EC_Utility.ToA3DVECTOR3(vPos)); // Update camera if available // 如果可用则更新相机 // UpdateFollowCamera(false, 10); // Uncomment if UpdateFollowCamera method exists // Streaming anchor follows navigate clone when force-navigate is active (LitModelHolder.UpdateStreamingAnchorPosition). // 强制导航时流式锚点跟随导航克隆(LitModelHolder.UpdateStreamingAnchorPosition)。 LitModelHolder.Instance.LoadAllObjectsNearTargetPosition(default, useHostPlayerPosition: true).Forget(); }; // Jump to instance (change world/instance) // 跳转到实例(更改世界/实例) // Note: JumpToInstance is currently a stub in CECGameRun, so we skip the call for now // 注意:JumpToInstance 目前在 CECGameRun 中是一个存根,所以我们现在跳过调用 if (CECGameRun.Instance != null && !CECGameRun.Instance.JumpToInstance(idInst, vPos, iParallelWorldID, actDone)) { Debug.LogError($"CECHostPlayer::Goto, Failed to jump to instance {idInst}"); return false; } return true; } public void SetStatusRun(bool value) { if (!isGrounded) { Debug.LogError("Player not in ground"); return; } isRun = value; } public bool IsPlayerMoving() { return m_pWorkMan.IsMoving(); } // Is auto moving ? bool IsAutoMoving() { CECHPWork pWork = m_pWorkMan.GetRunningWork(CECHPWork.Host_work_ID.WORK_MOVETOPOS); if (pWork != null) return (pWork as CECHPWorkMove).GetAutoMove(); else return false; } } }