392 lines
16 KiB
C#
392 lines
16 KiB
C#
using BrewMonster;
|
|
using BrewMonster.Scripts;
|
|
using System.Threading.Tasks;
|
|
using UnityEngine;
|
|
using CSNetwork.GPDataType;
|
|
using Cysharp.Threading.Tasks;
|
|
namespace BrewMonster.Scripts
|
|
{
|
|
// CECHostNavigatePlayer class - Basic implementation for navigation player // CECHostNavigatePlayer类 - 导航玩家的基本实现
|
|
public class CECHostNavigatePlayer : CECClonePlayer
|
|
{
|
|
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_CLONED_PLAYER;
|
|
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);
|
|
if (skeletonBuilder != null)
|
|
{
|
|
m_pNavigateModel.SetSkeletonBuilder(skeletonBuilder);
|
|
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 // 克隆宿主玩家模型与装备
|
|
BMLogger.Log($"CECHostNavigatePlayer::InitAsync, debugStep: {debugStep++}");
|
|
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);
|
|
EventBus.PublishChannel(m_PlayerInfo.cid, new PlayActionEvent(szPetAct, 200, true));
|
|
}
|
|
|
|
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)
|
|
{
|
|
Debug.Log($"CECHostNavigatePlayer::PlayNonSkillActionWithName, szAct: {szAct} of object {m_pActionController.PlayerModel.transform.parent.name}");
|
|
return m_pActionController.PlayNonSkillActionWithName(GetMoveStandAction(false, IsFighting()), szAct);
|
|
}
|
|
return false;
|
|
}
|
|
public override bool Tick(uint dwDeltatime)
|
|
{
|
|
base.Tick(dwDeltatime);
|
|
Debug.Log($"CECHostNavigatePlayer::Tick, m_bNavigateModelApplied: {m_bNavigateModelApplied}");
|
|
if (!m_bNavigateModelApplied /*&& 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;
|
|
}
|
|
}
|
|
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)
|
|
// 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;
|
|
}
|
|
}
|
|
}
|