using BrewMonster; using BrewMonster.Scripts; using System.Threading.Tasks; using UnityEngine; using CSNetwork.GPDataType; using Cysharp.Threading.Tasks; using Animancer; namespace BrewMonster.Scripts { // CECHostNavigatePlayer class - Basic implementation for navigation player // CECHostNavigatePlayer类 - 导航玩家的基本实现 public class CECHostNavigatePlayer : CECClonePlayer, ITickable { protected CECNavigateCtrl m_pNavigateCtrl; // Force navigate // 强制导航 protected CECHostPlayer m_pHostPlayer = null; // Reference to host player // 对宿主玩家的引用 protected string m_szNavigateModelFile; protected CECModel m_pNavigateModel; protected CECModel playerModel; protected bool m_bNavigateModelApplied; // Whether navigate model has been applied // 移动模型是否已应用 /// /// MonoBehaviour must not be constructed with new; use AddComponent then call this. /// MonoBehaviour 不能用 new 构造;请用 AddComponent 再调用本方法。 /// public void InitializeHost(CECHostPlayer pHost) { m_szNavigateModelFile = null; m_bNavigateModelApplied = false; m_pNavigateModel = null; m_iCID = (int)Class_ID.OCID_HOST_NAVIGATER; m_bCastShadow = true; m_bShowCustomize = true; m_pHostPlayer = pHost; m_pNavigateCtrl = pHost != null ? new CECNavigateCtrl(pHost) : null; } public A3DVECTOR3 GetDir() { if (m_pHostPlayer != null) { // Get direction from host's transform // 从宿主的变换获取方向 Transform hostTransform = m_pHostPlayer.transform; if (hostTransform != null) { Vector3 forward = hostTransform.forward; return new A3DVECTOR3(forward.x, forward.y, forward.z); } } return new A3DVECTOR3(0, 0, 1); } public A3DVECTOR3 GetUp() { Transform hostTransform = m_pHostPlayer.transform; if (hostTransform != null) { Vector3 up = hostTransform.up; return new A3DVECTOR3(up.x, up.y, up.z); } return new A3DVECTOR3(0, 1, 0); } public void ChangeModelMoveDirAndUp(A3DVECTOR3 vDir, A3DVECTOR3 vUp) { if (m_pHostPlayer != null && m_pHostPlayer.transform != null) { // Update host's rotation based on direction // 根据方向更新宿主的旋转 Vector3 dir = new Vector3(vDir.x, vDir.y, vDir.z); Vector3 up = new Vector3(vUp.x, vUp.y, vUp.z); if (dir.magnitude > 0.01f) { Quaternion rotation = Quaternion.LookRotation(dir, up); m_pHostPlayer.transform.rotation = rotation; } } } public void SetNavigateModelFile(string szFile) { m_szNavigateModelFile = szFile; } public CECNavigateCtrl GetNavigateCtrl() { return m_pNavigateCtrl; } // Handle navigation event // 处理导航事件 public void OnNavigateEvent(int task, int e) { if (m_pNavigateCtrl == null) { return; } if (e == (int)CECNavigateCtrl.NavigateEvent.EM_PREPARE) { m_pNavigateCtrl.OnPrepareNavigate(task); } else if (e == (int)CECNavigateCtrl.NavigateEvent.EM_BEGIN) { m_pNavigateCtrl.OnBeginNavigate(); } else if (e == (int)CECNavigateCtrl.NavigateEvent.EM_END) { m_pNavigateCtrl.OnEndNavigate(); } } public bool LoadConfig() { m_pNavigateCtrl = new CECNavigateCtrl(m_pHostPlayer); if (m_pNavigateCtrl != null) return m_pNavigateCtrl.LoadConfig("configs\\force_navigate.txt"); else { BMLogger.LogError("CECHostNavigatePlayer::Init, Failed to load config."); } return false; } public override CECModel GetRenderModel() { return m_pNavigateModel != null ? m_pNavigateModel : base.GetRenderModel(); } public bool IsNavigateMoving() { if (m_pNavigateCtrl != null && m_pNavigateCtrl.GetBezierWalker() != null) return m_pNavigateCtrl.GetBezierWalker().IsWalking(); return false; } public override void Release() { TeardownNavigatePlayerVisual(); base.Release(); if (m_pNavigateModel != null && m_pNavigateModel.m_pPlayerModel != null) { Transform hhRide = m_pNavigateModel.m_pPlayerModel.transform.Find("HH_ride"); if (hhRide != null) { Destroy(hhRide.gameObject); } Destroy(m_pNavigateModel.m_pPlayerModel); m_pNavigateModel = null; } if (m_pNavigateCtrl != null) { m_pNavigateCtrl = null; } m_bNavigateModelApplied = false; TickInvoker.Instance.UnregisterTickable(this); } /// /// Load optional force-navigate dummy model via Addressables, then clone host visuals. /// 通过 Addressables 加载可选的强制导航替身模型,再克隆宿主外观。 /// public async Task Init() { if (m_pHostPlayer == null) { return false; } int debugStep = 0; m_bNavigateModelApplied = false; // Load navigate model // 加载导航模型 if (!string.IsNullOrEmpty(m_szNavigateModelFile)) { if (m_pNavigateModel != null) { if (m_pNavigateModel.m_pPlayerModel != null) { Transform hhRide = m_pNavigateModel.m_pPlayerModel.transform.Find("HH_ride"); if (hhRide != null) foreach (Transform child in hhRide) { Destroy(child.gameObject); } Destroy(m_pNavigateModel.m_pPlayerModel); } m_pNavigateModel = null; } string address = AFile.NormalizePath(m_szNavigateModelFile.ToLower(), true); GameObject prefab = await AddressableManager.Instance.LoadPrefabAsync(address); if (prefab == null) { BMLogger.LogError($"CECHostNavigatePlayer::InitAsync, Failed to load navigate model: {address}"); } else { GameObject instance = ObjectSpawner.Instance.InstantiateObject(prefab); instance.transform.localPosition = Vector3.zero; instance.transform.localRotation = Quaternion.identity; instance.transform.localScale = Vector3.one; m_pNavigateModel = new CECModel(); m_pNavigateModel.m_pPlayerModel = instance; SkeletonBuilder skeletonBuilder = instance.GetComponentInChildren(true); NamedAnimancerComponent animancer = instance.GetComponentInChildren(); if (skeletonBuilder != null && animancer != null) { m_pNavigateModel.SetSkeletonBuilder(skeletonBuilder); m_pNavigateModel.SetNamedAnimancerComponent(animancer); m_pNavigateModel.SetTransform(instance.transform); m_pNavigateModel.InitializeSkeletonBuilder(); } else { m_pNavigateModel.SetTransform(instance.transform); } AddressableManager.Instance.ReleaseAsset(address); } } CameraController.Instance.UpdateFollowObject(m_pNavigateModel.transform); TickInvoker.Instance.RegisterTickable(this); // Clone host player model/equipment // 克隆宿主玩家模型与装备 bool loaded = await LoadFrom(m_pHostPlayer, false); if (loaded) SetupNavigatePlayerVisual(); return loaded; } /// /// Subscribe navigate clone to the same cid animation channel as the host (host unsubscribes first). /// 与宿主相同 cid 的动画通道:先取消宿主订阅,再初始化导航克隆的 PlayerVisual。 /// void SetupNavigatePlayerVisual() { if (!TryGetComponent(out var navigateVisual)) return; if (m_pHostPlayer != null && m_pHostPlayer.TryGetComponent(out var hostVisual)) hostVisual.UnregisterPlayerEventHandlers(); navigateVisual.InitPlayerEventDoneHandler(); } /// /// Restore host PlayerVisual subscriptions when force navigate ends. /// 强制导航结束时恢复宿主的 PlayerVisual 订阅。 /// void TeardownNavigatePlayerVisual() { if (TryGetComponent(out var navigateVisual)) navigateVisual.UnregisterPlayerEventHandlers(); if (m_pHostPlayer != null && m_pHostPlayer.TryGetComponent(out var hostVisual)) hostVisual.InitPlayerEventDoneHandler(); } public bool IsReadyNavigate() { return IsAllResReady() && m_bNavigateModelApplied; } public bool PlayNavigateAction() { if (m_pNavigateModel != null) { string szPetAct = null; szPetAct = CECNPC.GetBaseActionName((int)PLAYER_ACTION_TYPE.ACT_WALK); m_pNavigateModel.PlayActionByName(szPetAct, 1.0f, false, 200, true, 0); } PLAYER_ACTION action = m_PlayerActions[(int)PLAYER_ACTION_TYPE.ACT_STAND]; string szAct = $"{action.data.ActionPrefix}_骑乘_通用"; PlayNonSkillActionWithName(szAct); return true; } public bool PlayNonSkillActionWithName(string szAct) { if (m_pActionController != null) { return m_pActionController.PlayNonSkillActionWithName(GetMoveStandAction(false, IsFighting()), szAct); } return false; } public override bool Tick(uint dwDeltatime) { base.Tick(dwDeltatime); if (!m_bNavigateModelApplied /*&& IsAllResReady()*/) { //TOdo: implement IsAllresReady ApplyNavigateModel(); // if(!IsShapeChanged()) // ApplyNavigateModel(); // else if (IsShapeChanged() && IsShapeModelChanged() && m_pNavigateModel != null) // ApplyNavigateModel(); } return true; } public Vector3 GetNavigateModelPosition() { if (m_pNavigateModel == null || m_pNavigateModel.m_pPlayerModel == null) return m_pHostPlayer.transform.position; return m_pNavigateModel.m_pPlayerModel.transform.position; } /// /// Safe world position for streaming when navigate model is not ready yet. /// 导航模型未就绪时用于流式加载的安全世界坐标。 /// public bool TryGetNavigateModelPosition(out Vector3 position) { position = default; if (m_pNavigateModel == null || m_pNavigateModel.m_pPlayerModel == null) return false; position = m_pNavigateModel.m_pPlayerModel.transform.position; return true; } // A3DAABB CECHostNavigatePlayer::GetShadowAABB(){ // A3DAABB shadowAABB; // shadowAABB.Clear(); // shadowAABB.Merge(m_aabb); // // if(m_pNavigateModel) // shadowAABB.Merge(m_pNavigateModel->GetModelAABB()); // else if (IsRidingOnPet() && m_pPetModel && m_pPetModel->GetA3DSkinModel()) // shadowAABB.Merge(m_pPetModel->GetModelAABB()); // else if (GetPlayerModel() && GetPlayerModel()->GetA3DSkinModel()) // shadowAABB.Merge(GetPlayerModel()->GetModelAABB()); // // return shadowAABB; // } public override void SetDirAndUp(A3DVECTOR3 vDir, A3DVECTOR3 vUp) { base.SetDirAndUp(vDir, vUp); if (m_pNavigateModel != null) { Vector3 dir = new Vector3(vDir.x, vDir.y, vDir.z); Vector3 up = new Vector3(vUp.x, vUp.y, vUp.z); if (dir.magnitude > 0.01f) { Quaternion lookRot = Quaternion.LookRotation(dir, up); m_pNavigateModel.m_pPlayerModel.transform.rotation = lookRot; } m_bAdjustOrient = false; } } public void ResetHookPosition() { if (m_pNavigateModel != null) { m_pNavigateModel.ResetHookPosition(_hh_ride); } } protected override bool ShouldLoadEquipment(int index) { return index != InventoryConst.EQUIPIVTR_FLYSWORD; } protected override void OnCloneSimpleProperty() { // m_pNavigateModel needs mount hook on cloned model; duplicate attach/release — do not load riding pet again here // m_pNavigateModel 需要挂接克隆模型挂点,避免重复释放,故不再加载骑宠 m_RidingPet.Reset(); } public override void SetPos(Vector3 vPos) { base.SetPos(vPos); if (m_pNavigateModel != null) { m_pNavigateModel.m_pPlayerModel.transform.position = vPos; } } private bool ApplyNavigateModel() { if ( !GetMajorModel() || m_pNavigateModel == null || m_bNavigateModelApplied) { BMLogger.Log($"GetMajorModel(){GetMajorModel() == true } m_NavigateMode {m_pNavigateModel == null} m_bNavigateModelApplied {m_bNavigateModelApplied} "); return false; } A3DVECTOR3 vCurPos = GetPos(); m_aabbServer.Center = vCurPos + new A3DVECTOR3(0.0f, m_aabbServer.Extents.y, 0.0f); m_aabbServer.CompleteMinsMaxs(); SetUseGroundNormal(true); // Hang character model on force-navigate model // 把角色模型挂到强制移动模型上 // int iIndex; // A3DSkeletonHook* pHook = GetMajorModel()->GetA3DSkinModel()->GetSkeleton()->GetHook(_cc_ride, &iIndex); // if (pHook) pHook->SetFixDirFlag(true); m_pNavigateModel.AddChildModel(_hanger_ride, false, _hh_ride, GetPlayerModel(), _cc_ride); // if(IsShapeModelChanged()) // m_pNavigateModel.AddChildModel(_hanger_ride, false, _hh_ride, GetPlayerModel(), "HH_feijian"); // else // m_pNavigateModel.AddChildModel(_hanger_ride, false, _hh_ride, GetMajorModel(), _cc_ride); //m_pNavigateModel->GetA3DSkinModel()->Update(0); // PlayAction(GetMoveStandAction(false, IsFighting()), 1.0f, true); PlayNavigateAction(); m_bNavigateModelApplied = true; OnNavigateEvent(0, (int)CECNavigateCtrl.NavigateEvent.EM_BEGIN); return true; } } }