Files
2026-05-19 18:03:25 +07:00

403 lines
16 KiB
C#

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 // 移动模型是否已应用
/// <summary>
/// MonoBehaviour must not be constructed with <c>new</c>; use <c>AddComponent</c> then call this.
/// MonoBehaviour 不能用 <c>new</c> 构造;请用 <c>AddComponent</c> 再调用本方法。
/// </summary>
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);
}
/// <summary>
/// Load optional force-navigate dummy model via Addressables, then clone host visuals.
/// 通过 Addressables 加载可选的强制导航替身模型,再克隆宿主外观。
/// </summary>
public async Task<bool> 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<SkeletonBuilder>(true);
NamedAnimancerComponent animancer = instance.GetComponentInChildren<NamedAnimancerComponent>();
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;
}
/// <summary>
/// Subscribe navigate clone to the same cid animation channel as the host (host unsubscribes first).
/// 与宿主相同 cid 的动画通道:先取消宿主订阅,再初始化导航克隆的 PlayerVisual。
/// </summary>
void SetupNavigatePlayerVisual()
{
if (!TryGetComponent<PlayerVisual>(out var navigateVisual))
return;
if (m_pHostPlayer != null && m_pHostPlayer.TryGetComponent<PlayerVisual>(out var hostVisual))
hostVisual.UnregisterPlayerEventHandlers();
navigateVisual.InitPlayerEventDoneHandler();
}
/// <summary>
/// Restore host PlayerVisual subscriptions when force navigate ends.
/// 强制导航结束时恢复宿主的 PlayerVisual 订阅。
/// </summary>
void TeardownNavigatePlayerVisual()
{
if (TryGetComponent<PlayerVisual>(out var navigateVisual))
navigateVisual.UnregisterPlayerEventHandlers();
if (m_pHostPlayer != null && m_pHostPlayer.TryGetComponent<PlayerVisual>(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;
}
/// <summary>
/// Safe world position for streaming when navigate model is not ready yet.
/// 导航模型未就绪时用于流式加载的安全世界坐标。
/// </summary>
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;
}
}
}