// Enable IntelligentRoute AutoPF integration (ported from original PW client). // 启用智能寻路 AutoPF 集成(从原版完美客户端移植)。 #define ENABLE_CEC_INTELLIGENT_ROUTE using CSNetwork.GPDataType; using System; using System.Collections.Generic; using UnityEngine; using BrewMonster.Managers; using Types = BrewMonster.Scripts.CECHPWorkMove.DestTypes; using BrewMonster.Network; namespace BrewMonster.Scripts { public class CECHPWorkMove : CECHPWork { public static class DestTypes { public const int DEST_2D = 0, DEST_3D = 1, DEST_DIR = 2, DEST_PUSH = 3, DEST_STANDJUMP = 4, DEST_AUTOPF = 5; // Movement type } const float A3D_PI = 3.1415926535f; static float DEG2RAD(float deg) => ((deg) * A3D_PI / 180.0f); static float pitch_ang_wing => DEG2RAD(45.0f); static float pitch_ang_fly_sword => DEG2RAD(25.0f); static float lean_max_ang => DEG2RAD(45.0f); static float ang_vel_fly => 1.0f / DEG2RAD(60.0f); static float ang_vel_swim => 1.0f / DEG2RAD(180.0f); static float pitch_co_wing => pitch_ang_wing / A3D_PI; static float pitch_co_fly_sword => pitch_ang_fly_sword / A3D_PI; const float sidle_co = .5f; static float sidle_co_r => 1.0f - sidle_co; static float push_pitch_vel_wing => pitch_ang_wing / 0.5f; static float push_pitch_vel_fly_sword => pitch_ang_fly_sword / 0.5f; private const uint MoveInputMask = 0x0F; // MD_FORWARD | MD_RIGHT | MD_BACK | MD_LEFT protected A3DVECTOR3 m_vMoveDest; // Move destination position or direction protected int m_iDestType; // Destination type protected bool m_bHaveDest; // true, have destination protected bool m_bMeetSlide; // true, meet slide protected A3DVECTOR3 m_vCurDir; // Current move direction protected bool m_bReadyCancel; // true, ready to cancel protected bool m_bGliding; // glide protected float m_fGlideTime; protected float m_fGlideSpan; protected float m_fGlideAng; protected float m_fGlideVel; // glide angular vel protected float m_fGlidePitch; // glide pitch angle protected float m_fCurPitch; protected float m_fPushPitch; protected float m_fPushLean; protected A3DVECTOR3 vDir = new A3DVECTOR3(); protected bool m_bUseAutoMoveDialog; // Auto move protected float m_fAutoHeight; // Height of auto moving destination protected bool m_bAutoLand; // Auto land when arrive at destination protected bool m_bAutoFly; // Auto fly protected bool m_bReachedHeight;// Player reached specified height protected bool m_bAutoFlyPending; // Mark whether a fly command had been executed protected int m_iNPCTempleId; protected int m_iTaskId; protected bool m_bSwitchTo2D; protected bool m_bResetAutoPF; /// /// RMap marks deep water as blocked; AutoPF snaps goals to nearest land. If the real mission point /// (m_vMoveDest) is still farther horizontally (e.g. in water), continue with DEST_2D after AutoPF ends. /// const float AutoPF_WorldDestContinueDistH = 0.1f; /// Horizontal radius to treat DEST_2D as arrived (swim/fly overshoot logic often never fires). const float Dest2D_ArrivalDistH = 0.1f; public CECHPWorkMove(CECHPWorkMan pWorkMan) : base(Host_work_ID.WORK_MOVETOPOS, pWorkMan) { m_dwMask = Work_mask.MASK_MOVETOPOS; m_dwTransMask = Work_mask.MASK_STAND | Work_mask.MASK_TRACEOBJECT | Work_mask.MASK_FOLLOW; Reset(); } public CECHPWorkMove(int iWorkID, CECHPWorkMan pWorkMan) : base(iWorkID, pWorkMan) { } // Set destination position or direction public void SetDestination(int iDestType, A3DVECTOR3 vMoveDest) { m_iDestType = iDestType; m_vMoveDest = vMoveDest; m_bHaveDest = true; m_bGliding = false; //m_pHost.SetAdjustOrient(false); // 2014-9-10 ���ı��� CECHPWorkMove ������ʱ�򶼻���ô˺������ᵼ������ʱ��ͨ�� SetDestDirAndUp ���ö�����ij������ʧЧ�� // ���ַ���Ϊ�����ɡ�����ӽ�������ͷź����ϰ�ס'A'��'D'���ƶ�������ס���ţ�����������ʩ������ƫ��Ŀ��������ƶ��������γ��ԣ� //���������Ϣ m_iTaskId = 0; m_iNPCTempleId = 0; ResetUseAutoPF(); if (iDestType == Types.DEST_DIR) { m_vCurDir = vMoveDest; if (m_bUseAutoMoveDialog) { m_bUseAutoMoveDialog = false; m_bAutoLand = false; m_fAutoHeight = -1.0f; m_bAutoFly = false; m_bReachedHeight = true; m_bAutoFlyPending = false; } } else if (iDestType == Types.DEST_2D || iDestType == Types.DEST_3D) { m_vCurDir = vMoveDest - m_pHost.GetPos(); m_vCurDir.y = 0.0f; if (m_vCurDir.Normalize() > 1e-4f) OrientHostHorizontal(m_vCurDir); } else if (IsAutoPF()) { // Search() has not run yet — GetCurDest() is wrong here; use goal direction until first waypoint. // Search() 尚未执行时 GetCurDest() 不可靠;先用目标方向,寻路成功后再对准第一个节点。 m_vCurDir = vMoveDest - m_pHost.GetPos(); m_vCurDir.y = 0.0f; if (m_vCurDir.Normalize() > 1e-4f) OrientHostHorizontal(m_vCurDir); if (m_bUseAutoMoveDialog) { // �˴����� m_bUseAutoMoveDialog���� SetUseAutoMoveDialog ��˵�� m_bUseAutoMoveDialog = false; m_bAutoLand = false; m_fAutoHeight = -1.0f; m_bAutoFly = false; m_bReachedHeight = true; m_bAutoFlyPending = false; } } // Swim/fly velocity carry-over skews the first AirWaterMove/GroundMove frame on new click. if (iDestType == Types.DEST_2D || iDestType == Types.DEST_3D || iDestType == Types.DEST_AUTOPF) { m_pHost.m_vVelocity.x = 0f; m_pHost.m_vVelocity.z = 0f; } // TO DO: fix later //if (m_pHost.m_pMoveTargetGFX) //{ // if (iDestType != DEST_PUSH) // m_pHost.m_pMoveTargetGFX.Stop(); //} } void ResetUseAutoPF() { m_bResetAutoPF = true; } // Tick routine public override bool Tick(float dwDeltaTime) { UpdateResetUseAutoPF(); if (m_bSwitchTo2D) { SwitchToDest2D(); m_bSwitchTo2D = false; return true; } #if ENABLE_CEC_INTELLIGENT_ROUTE if (IsAutoPF()) { if (global::BrewMonster.Scripts.CECIntelligentRoute.Instance().IsIdle()) { // C++: AutoPF not ready yet, wait next tick for SwitchTo2D (EC_HPWorkMove.cpp). // C++:智能寻路模式未成功时,等待下个 Tick 切换到 DEST_2D 模式。 return true; } // EC_HPWorkMove.cpp: mid-air AutoPF → reset route and switch to DEST_2D next tick. if (m_pHost.IsFlying()) { global::BrewMonster.Scripts.CECIntelligentRoute.Instance().ResetSearch(); m_bSwitchTo2D = true; return true; } } #endif base.Tick(dwDeltaTime); // Draw red line showing the path the player has to move // 绘制红色线条显示玩家需要移动的路径 if (m_bHaveDest) { A3DVECTOR3 curPos = m_pHost.GetPos(); Vector3 vCurPos = new Vector3(curPos.x, curPos.y, curPos.z); #if ENABLE_CEC_INTELLIGENT_ROUTE if (IsAutoPF() && global::BrewMonster.Scripts.CECIntelligentRoute.Instance().IsMoveOn()) { // Draw AutoPF path in red // 绘制 AutoPF 路径(红色) List path = global::BrewMonster.Scripts.CECIntelligentRoute.Instance().GetFullPath(); if (path != null && path.Count > 1) { // Draw lines connecting all path nodes (red) // 绘制连接所有路径节点的线(红色) for (int i = 0; i < path.Count - 1; i++) { Vector3 from = path[i]; Vector3 to = path[i + 1]; Debug.DrawLine(from, to, Color.red, 0.1f, false); } // Draw line from current position to first path node (red) // 绘制从当前位置到第一个路径节点的线(红色) if (path.Count > 0) { Debug.DrawLine(vCurPos, path[0], Color.red, 0.1f, false); } } } else #endif { // Draw straight line to destination (red) // 绘制到目的地的直线(红色) Vector3 vDest = new Vector3(m_vMoveDest.x, m_vMoveDest.y, m_vMoveDest.z); Debug.DrawLine(vCurPos, vDest, Color.red, 0.1f, false); } } if (m_pHost.IsRooting()) return true; if (m_pHost.IsDead()) { Finish(); return true; } if (m_bUseAutoMoveDialog) { if (m_pHost.IsFlying()) { m_bAutoFly = false; m_bAutoFlyPending = false; } if (m_bAutoFly && !m_bAutoFlyPending && !m_pHost.IsFlying()) { if (m_pHost.CmdFly(true)) { m_bAutoFly = false; m_bAutoFlyPending = true; } } } else { // Make sure 'Win_AutoPlay' dialog doesn't show up /* CECGameUIMan pGameUI = g_pGame.GetGameRun().GetUIManager().GetInGameUIMan(); pGameUI.AutoMoveShowDialog(false);*/ } // 如果目标是任务 NPC(模板ID),当接近时切换到 WorkTrace(对话/交互)。 // English: If destination is a task NPC (template id), handoff to WorkTrace (talk/interact) when close. if ((m_vMoveDest - m_pHost.GetPos()).MagnitudeH() <= 5.0f) { if (m_iNPCTempleId != 0) { // Find live NPC in scene by template id. // 按模板ID在场景中查找活体 NPC。 CECNPC pNPC = EC_ManMessageMono.Instance != null ? EC_ManMessageMono.Instance.CECNPCMan.FindNPCByTemplateID(m_iNPCTempleId) : null; if (pNPC != null && m_pHost.SelectTarget(pNPC.GetNPCID())) { CECHPWorkTrace pWork = m_pWorkMan.CreateNPCTraceWork(pNPC, m_iTaskId); if (pWork != null) { // Prevent auto land before switching. // 防止在切换前自动降落。 m_bAutoLand = false; Finish(); m_pWorkMan.SetPostTickCommand(new CECHPWorkPostTickRunWorkCommand(pWork, true)); return true; } } } } //m_pHost.SetGroundInfoClient(); float fDeltaTime = dwDeltaTime; if (m_pHost.m_iMoveEnv == (int)MoveEnvironment.MOVEENV_GROUND || m_pHost.m_iMoveEnv == (int)MoveEnvironment.MOVEENV_WATER && m_pHost.IsJumping() && (m_pHost.m_CDRInfo.vAbsVelocity.y > 0 || m_pHost.m_CDRInfo.fYVel > 0)) { // Play appropriate actions if (!m_pHost.IsJumping() && !m_pHost.IsPlayingAction((int)PLAYER_ACTION_TYPE.ACT_TRICK_RUN) && m_pHost.m_iMoveMode != (int)MoveMode.MOVE_SLIDE && !m_bMeetSlide) { int iAction = m_pHost.GetMoveStandAction(true); m_pHost.PlayAction(iAction, false); } Tick_Walk(fDeltaTime); } else // (m_pHost.m_iMoveEnv == CECPlayer::MOVEENV_AIR || m_pHost.m_iMoveEnv == CECPlayer::MOVEENV_WATER) { m_pHost.ResetJump(); // Play appropriate actions if (!m_bGliding) { int iAction = m_pHost.GetMoveStandAction(true); m_pHost.PlayAction(iAction, false); } Tick_FlySwim(fDeltaTime); } return true; } // Reset work public override void Reset() { base.Reset(); m_iDestType = DestTypes.DEST_2D; m_bHaveDest = false; m_bMeetSlide = false; m_bReadyCancel = false; m_bGliding = false; m_fGlideTime = 0; m_fCurPitch = 0; m_fPushPitch = 0; m_fPushLean = 0; m_bUseAutoMoveDialog = false; m_fAutoHeight = -1.0f; m_bAutoLand = false; m_bAutoFly = false; m_bReachedHeight = true; m_bAutoFlyPending = false; m_iNPCTempleId = 0; m_iTaskId = 0; m_bSwitchTo2D = false; m_bResetAutoPF = false; } // Work is cancel public override void Cancel() { //if (m_pHost.m_pMoveTargetGFX) // m_pHost.m_pMoveTargetGFX.Stop(); //A3DVECTOR3 vDir = m_pHost.GetDir(); //vDir.y = 0; //vDir.Normalize(); //if (!vDir.IsZero()) //{ // m_pHost.StopModelMove(vDir, g_vAxisY, 0); //} ClearResetUseAutoPF(); //if (CECIntelligentRoute::Instance().IsUsageMove()) //{ // if (!CECIntelligentRoute::Instance().IsIdle()) // { // CECIntelligentRoute::Instance().ResetSearch(); // m_bSwitchTo2D = true; // Æô¶¯·ÉÐÐÖжϺó»Ö¸´Ê±¡¢ÐèÒªÇл»µ½ DEST_2D ģʽ // } //} base.Cancel(); //AP_ActionEvent(AP_EVENT_MOVEFINISHED); } // This work is do player moving ? public override bool IsMoving() { return true; } // Copy work data public override bool CopyData(CECHPWork pWork) { if (!base.CopyData(pWork)) return false; CECHPWorkMove pSrc = pWork as CECHPWorkMove; m_iDestType = pSrc.m_iDestType; m_bHaveDest = pSrc.m_bHaveDest; m_bMeetSlide = pSrc.m_bMeetSlide; m_bReadyCancel = pSrc.m_bReadyCancel; m_bGliding = pSrc.m_bGliding; m_fGlideTime = pSrc.m_fGlideTime; m_fGlideSpan = pSrc.m_fGlideSpan; m_fGlideAng = pSrc.m_fGlideAng; m_fGlideVel = pSrc.m_fGlideVel; m_fGlidePitch = pSrc.m_fGlidePitch; m_fCurPitch = pSrc.m_fCurPitch; m_fPushPitch = pSrc.m_fPushPitch; m_fPushLean = pSrc.m_fPushLean; m_iDestType = pSrc.m_iDestType; m_vMoveDest = pSrc.m_vMoveDest; m_vCurDir = pSrc.m_vCurDir; m_bUseAutoMoveDialog = pSrc.m_bUseAutoMoveDialog; m_fAutoHeight = pSrc.m_fAutoHeight; m_bAutoLand = pSrc.m_bAutoLand; m_bAutoFly = pSrc.m_bAutoFly; m_bReachedHeight = pSrc.m_bReachedHeight; m_bAutoFlyPending = pSrc.m_bAutoFlyPending; m_iNPCTempleId = pSrc.m_iNPCTempleId; m_iTaskId = pSrc.m_iTaskId; m_bSwitchTo2D = pSrc.m_bSwitchTo2D; m_bResetAutoPF = pSrc.m_bResetAutoPF; return true; } // Play move target effect public void PlayMoveTargetGFX(A3DVECTOR3 vPos, A3DVECTOR3 vNormal) { } // User press cancel button public void PressCancel() { m_bReadyCancel = true; } public void SetUseAutoMoveDialog(bool bUseAutoMoveDialog) { m_bUseAutoMoveDialog = bUseAutoMoveDialog; } public bool GetUseAutoMoveDialog() { return m_bUseAutoMoveDialog; } /// Matches C++ CECHPWorkMove::GetAutoMove — must not always return true or input stays "auto" forever. public bool GetAutoMove() { if (m_bUseAutoMoveDialog) return true; #if ENABLE_CEC_INTELLIGENT_ROUTE if (IsAutoPF()) { var r = global::BrewMonster.Scripts.CECIntelligentRoute.Instance(); return r.IsUsageMove() && !r.IsIdle(); } #endif return false; } void SetAutoLand(bool bAutoLand) { m_bAutoLand = bAutoLand; } bool GetAutoLand() { return m_bAutoLand; } void SetAutoHeight(float fHeight) { m_fAutoHeight = fHeight; m_bAutoFly = true; m_bReachedHeight = false; } float GetAutoHeight() { return m_fAutoHeight; } bool IsAutoFly() { return m_bAutoFly; } bool IsAutoPF() { return m_iDestType == Types.DEST_AUTOPF; } // Finish work public void Finish() { m_bFinished = true; Cancel(); // Close 'Win_AutoPlay' dialog if it exists //CECGameUIMan* pGameUI = g_pGame.GetGameRun().GetUIManager().GetInGameUIMan(); //pGameUI.AutoMoveShowDialog(false); if (m_bUseAutoMoveDialog) { if (m_bAutoLand) { m_bAutoLand = false; if (EC_Game.GetGameRun().GetHostPlayer().IsFlying()) EC_Game.GetGameRun().GetHostPlayer().CmdFly(false); } m_bUseAutoMoveDialog = false; m_fAutoHeight = -1.0f; m_bAutoFly = false; m_bReachedHeight = true; } //�������׷������ m_iNPCTempleId = 0; m_iTaskId = 0; } public void SetTaskNPCInfo(int tid, int taskid) { m_iNPCTempleId = tid; m_iTaskId = taskid; } void SwitchToDest2D() { int tid, taskid; tid = m_iNPCTempleId; taskid = m_iTaskId; //CECGameUIMan* pGameUI = g_pGame.GetGameRun().GetUIManager().GetInGameUIMan(); //pGameUI.SetAutoMoveShowDialogTarget((int)m_vMoveDest.x, (int)m_vMoveDest.z); SetDestination(CECHPWorkMove.DestTypes.DEST_2D, m_vMoveDest); SetTaskNPCInfo(tid, taskid); SetUseAutoMoveDialog(true); } #if ENABLE_CEC_INTELLIGENT_ROUTE /// True if switched to direct 2D leg — caller must not Finish() this frame. bool TryContinueAutoPFToWorldDestDirect() { A3DVECTOR3 p = m_pHost.GetPos(); float dx = m_vMoveDest.x - p.x; float dz = m_vMoveDest.z - p.z; if (dx * dx + dz * dz <= AutoPF_WorldDestContinueDistH * AutoPF_WorldDestContinueDistH) return false; int tid = m_iNPCTempleId; int taskid = m_iTaskId; global::BrewMonster.Scripts.CECIntelligentRoute.Instance().ResetSearch(); SetDestination(DestTypes.DEST_2D, m_vMoveDest); SetTaskNPCInfo(tid, taskid); SetUseAutoMoveDialog(true); return true; } #endif /// /// Swim/fly DEST_2D often never satisfies the overshoot branch; ground can miss when vTPNormal is zero. /// Finishes the move work so GetAutoMove() goes false and the player can steer again. /// bool TryFinishDest2DIfArrived(A3DVECTOR3 vCurPos, float stopSpeed, int iMoveMode, bool flySwimAddRun) { if (m_iDestType != DestTypes.DEST_2D) return false; float dx = m_vMoveDest.x - vCurPos.x; float dz = m_vMoveDest.z - vCurPos.z; if (dx * dx + dz * dz > Dest2D_ArrivalDistH * Dest2D_ArrivalDistH) return false; Finish(); int mode = flySwimAddRun ? (iMoveMode | (int)GPMoveMode.GP_MOVE_RUN) : iMoveMode; m_pHost.m_MoveCtrl.SendStopMoveCmd(EC_Utility.ToVector3(vCurPos), stopSpeed, mode); return true; } void OrientHostHorizontal(A3DVECTOR3 dirHorizontal) { if (dirHorizontal.IsZero()) return; Vector3 d = EC_Utility.ToVector3(dirHorizontal); d.y = 0f; if (d.sqrMagnitude < 1e-8f) return; d.Normalize(); m_pHost.SetDirAndUp(d, Vector3.up); } #if ENABLE_CEC_INTELLIGENT_ROUTE /// After Search(), face the first path node so swim/air/walk doesn't use previous heading. void OrientHostToFirstAutoPFWaypoint() { var route = global::BrewMonster.Scripts.CECIntelligentRoute.Instance(); if (!route.IsMoveOn()) return; A3DVECTOR3 d = route.GetCurDest() - m_pHost.GetPos(); d.y = 0f; if (d.Normalize() < 1e-4f) return; m_vCurDir = d; OrientHostHorizontal(d); } #endif // On first tick protected override void OnFirstTick() { m_pHost.m_iMoveMode = Move_Mode.MOVE_MOVE; //PlayMoveTargetGFX(); if (m_pHost.m_iMoveEnv != CECPlayer.Move_environment.MOVEENV_AIR) m_pHost.ShowWing(false); if (!m_pHost.IsJumping()) { int iAction = m_pHost.GetMoveStandAction(true); m_pHost.PlayAction(iAction, false); } } // Tick routine of walking on ground protected bool Tick_Walk(float fDeltaTime) { A3DVECTOR3 vCurPos = m_pHost.GetPos(); ref CDR_INFO cdr = ref m_pHost.m_CDRInfo; if (m_pHost.m_iMoveMode == (int)MoveMode.MOVE_SLIDE) { m_pHost.PlayAction((int)PLAYER_ACTION_TYPE.ACT_JUMP_LOOP, false); A3DVECTOR3 vDir; if (m_iDestType == DestTypes.DEST_DIR) { vDir = m_vCurDir; } else if (m_iDestType == DestTypes.DEST_PUSH) { vDir = GetCurrentModelDir(); } #if ENABLE_CEC_INTELLIGENT_ROUTE else if (IsAutoPF()) { vDir = global::BrewMonster.Scripts.CECIntelligentRoute.Instance().GetCurDest() - vCurPos; vDir.y = 0.0f; vDir.Normalize(); } #endif else { vDir = m_vMoveDest - vCurPos; vDir.y = 0.0f; vDir.Normalize(); } float fMaxSpeedV = 0.0f; m_bMeetSlide = m_pHost.m_MoveCtrl.MeetSlope(vDir, fMaxSpeedV); cdr.fYVel = EC_Utility.a_ClampFloor(cdr.fYVel, -fMaxSpeedV); if (m_pHost.m_GndInfo.bOnGround) m_vCurDir = vDir; vCurPos = m_pHost.m_MoveCtrl.GroundMove(m_vCurDir, m_pHost.GetGroundSpeed(), fDeltaTime); if (m_pHost.m_MoveCtrl.MoveBlocked() >= 3) { m_pHost.m_MoveCtrl.SetSlideLock(true); cdr.fYVel = 0.0f; Finish(); m_pHost.m_MoveCtrl.SendStopMoveCmd(EC_Utility.ToVector3(vCurPos), m_pHost.GetGroundSpeed(), (int)GPMoveMode.GP_MOVE_SLIDE); } else { m_pHost.SetPos(EC_Utility.ToVector3(vCurPos)); #if ENABLE_CEC_INTELLIGENT_ROUTE if (IsAutoPF() && global::BrewMonster.Scripts.CECIntelligentRoute.Instance().IsMoveOn()) global::BrewMonster.Scripts.CECIntelligentRoute.Instance().OnPlayerPosChange(vCurPos); #endif #if SHOW_AUTOMOVE_FOOTPRINTS if (IsAutoPF() || m_iDestType == DestTypes.DEST_2D) g_AutoPFFollowPoints.Add(vCurPos); #endif m_pHost.m_MoveCtrl.SendMoveCmd( vCurPos, 2, GPDataTypeHelper.g_vOrigin, (cdr.vAbsVelocity), (int)GPMoveMode.GP_MOVE_SLIDE); } } else if (!m_bMeetSlide) { float fSpeed = m_pHost.GetGroundSpeed(); int iMoveMode = m_pHost.m_bWalkRun ? (int)GPMoveMode.GP_MOVE_RUN : (int)GPMoveMode.GP_MOVE_WALK; if (m_pHost.IsJumping()) iMoveMode = (int)GPMoveMode.GP_MOVE_JUMP; else if (!m_pHost.m_GndInfo.bOnGround) iMoveMode = (int)GPMoveMode.GP_MOVE_FALL; if (m_bReadyCancel && m_pHost.m_GndInfo.bOnGround) { Finish(); m_pHost.m_MoveCtrl.SendStopMoveCmd(EC_Utility.ToVector3(vCurPos), fSpeed, iMoveMode); return true; } if ((GetMoveRelDirMask() & (uint)((CECHostPlayer.MOVE_DIR.MD_LEFT | CECHostPlayer.MOVE_DIR.MD_RIGHT | CECHostPlayer.MOVE_DIR.MD_FORWARD | CECHostPlayer.MOVE_DIR.MD_BACK))) != 0) m_iDestType = DestTypes.DEST_PUSH; if (m_iDestType == DestTypes.DEST_2D) { if (TryFinishDest2DIfArrived(vCurPos, fSpeed, iMoveMode, false)) return true; float fDist; if (m_pHost.m_GndInfo.bOnGround) { m_vCurDir = m_vMoveDest - vCurPos; m_vCurDir.y = 0.0f; fDist = m_vCurDir.Normalize(); } else { fDist = (m_vMoveDest - vCurPos).MagnitudeH(); } vCurPos = m_pHost.m_MoveCtrl.GroundMove(m_vCurDir, fSpeed, fDeltaTime, m_pHost.m_fVertSpeed); UpdateFacingFromDelta(vCurPos); if (m_pHost.m_MoveCtrl.MoveBlocked() >= 3) { cdr.fYVel = 0.0f; Finish(); m_pHost.m_MoveCtrl.SendStopMoveCmd(EC_Utility.ToVector3(vCurPos), fSpeed, iMoveMode); } else if (cdr.vTPNormal != new A3DVECTOR3(0)) { A3DVECTOR3 vMoveDelta = vCurPos - m_pHost.GetPos(); vMoveDelta.y = 0.0f; float fMoveDist = vMoveDelta.Normalize(); if (fMoveDist >= fDist) { Finish(); m_bUseAutoMoveDialog = false; m_pHost.SetPos(EC_Utility.ToVector3(vCurPos)); m_pHost.m_MoveCtrl.SendStopMoveCmd(EC_Utility.ToVector3(vCurPos), fSpeed, iMoveMode); } else { m_pHost.SetPos(EC_Utility.ToVector3(vCurPos)); m_pHost.m_MoveCtrl.SendMoveCmd( vCurPos, 0, m_vMoveDest, (cdr.vAbsVelocity), iMoveMode); } #if SHOW_AUTOMOVE_FOOTPRINTS g_AutoPFFollowPoints.Add(vCurPos); #endif } else { #if SHOW_AUTOMOVE_FOOTPRINTS g_AutoPFFollowPoints.Add(vCurPos); #endif m_pHost.SetPos(EC_Utility.ToVector3(vCurPos)); m_pHost.m_MoveCtrl.SendMoveCmd( vCurPos, 1, m_vMoveDest, (cdr.vAbsVelocity), iMoveMode); } } else if (m_iDestType == DestTypes.DEST_DIR) { vCurPos = m_pHost.m_MoveCtrl.GroundMove(m_vCurDir, fSpeed -0.5f, fDeltaTime, m_pHost.m_fVertSpeed); UpdateFacingFromDelta(vCurPos); m_pHost.SetPos(EC_Utility.ToVector3(vCurPos)); if (m_pHost.m_MoveCtrl.MoveBlocked() >= 3) { cdr.fYVel = 0.0f; Finish(); m_pHost.m_MoveCtrl.SendStopMoveCmd(EC_Utility.ToVector3(vCurPos), fSpeed, iMoveMode); } else if (m_pHost.m_GndInfo.bOnGround) { m_pHost.m_MoveCtrl.SendMoveCmd( vCurPos, 2, GPDataTypeHelper.g_vOrigin, (cdr.vAbsVelocity), iMoveMode); } else { m_pHost.m_MoveCtrl.SendMoveCmd( vCurPos, 1, m_vMoveDest, (cdr.vAbsVelocity), iMoveMode); } } else if (m_iDestType == DestTypes.DEST_STANDJUMP) { if (!m_pHost.IsJumping()) { Finish(); m_pHost.m_MoveCtrl.SendStopMoveCmd(EC_Utility.ToVector3(vCurPos), fSpeed, iMoveMode); } else { vCurPos = m_pHost.m_MoveCtrl.GroundMove(GPDataTypeHelper.g_vOrigin, 0.0f, fDeltaTime, m_pHost.m_fVertSpeed); m_pHost.SetPos(EC_Utility.ToVector3(vCurPos)); if (m_pHost.m_MoveCtrl.MoveBlocked() >= 3) { m_pHost.ResetJump(); Finish(); m_pHost.m_MoveCtrl.SendStopMoveCmd(EC_Utility.ToVector3(vCurPos), fSpeed, iMoveMode); } else { m_pHost.m_MoveCtrl.SendMoveCmd( vCurPos, 0, m_vMoveDest, (cdr.vAbsVelocity), iMoveMode); } } } else if (m_iDestType == DestTypes.DEST_PUSH) { Vector3 vMoveDir = Vector3.zero;//EC_Utility.ToVector3(GPDataTypeHelper.g_vOrigin); fSpeed = m_pHost.GetGroundSpeed(); bool bFinish = false; if (m_pHost.GetPushDir(ref vMoveDir, (uint)(CECHostPlayer.MOVE_DIR.MD_FORWARD | CECHostPlayer.MOVE_DIR.MD_BACK | CECHostPlayer.MOVE_DIR.MD_LEFT | CECHostPlayer.MOVE_DIR.MD_RIGHT), fDeltaTime)) { if (vMoveDir != Vector3.zero) { m_pHost.SetRotationHP(vMoveDir); } vCurPos = m_pHost.m_MoveCtrl.GroundMove(EC_Utility.ToA3DVECTOR3(vMoveDir), fSpeed, fDeltaTime, m_pHost.m_fVertSpeed); //BoxCastDrawer.DrawCenterAxis(EC_Utility.ToVector3(vCurPos), Quaternion.identity, 1f, 3f); m_pHost.SetPos(EC_Utility.ToVector3(vCurPos)); } else { if (!m_bUseAutoMoveDialog) bFinish = true; else m_iDestType = DestTypes.DEST_2D; } if (bFinish || m_pHost.m_MoveCtrl.MoveBlocked() >= 3) { if (m_pHost.m_MoveCtrl.MoveBlocked() >= 3) { cdr.fYVel = 0.0f; } Finish(); m_pHost.m_vVelocity.Clear(); m_pHost.m_MoveCtrl.SendStopMoveCmd(EC_Utility.ToVector3(vCurPos), fSpeed, iMoveMode); } else { m_pHost.m_vVelocity = EC_Utility.ToA3DVECTOR3(vMoveDir) * fSpeed; m_pHost.m_MoveCtrl.SendMoveCmd( vCurPos, 2, GPDataTypeHelper.g_vOrigin, m_pHost.m_vVelocity, iMoveMode); } } //else if (m_iDestType == DestTypes.DEST_PUSH) //{ // m_iDestType = DestTypes.DEST_2D; //} #if ENABLE_CEC_INTELLIGENT_ROUTE else if (IsAutoPF()) { float fDist = 0.0f; A3DVECTOR3 vCurDest = global::BrewMonster.Scripts.CECIntelligentRoute.Instance().GetCurDest(); if (m_pHost.m_GndInfo.bOnGround) { m_vCurDir = vCurDest - vCurPos; m_vCurDir.y = 0.0f; fDist = m_vCurDir.Normalize(); } else { fDist = (vCurDest - vCurPos).MagnitudeH(); } vCurPos = m_pHost.m_MoveCtrl.GroundMove(m_vCurDir, fSpeed, fDeltaTime, m_pHost.m_fVertSpeed); if (!m_vCurDir.IsZero()) OrientHostHorizontal(m_vCurDir); if (m_pHost.m_MoveCtrl.MoveBlocked() >= 3) { cdr.fYVel = 0.0f; Finish(); m_pHost.m_MoveCtrl.SendStopMoveCmd(EC_Utility.ToVector3(vCurPos), fSpeed, iMoveMode); } else { m_pHost.SetPos(EC_Utility.ToVector3(vCurPos)); global::BrewMonster.Scripts.CECIntelligentRoute.Instance().OnPlayerPosChange(vCurPos); if (global::BrewMonster.Scripts.CECIntelligentRoute.Instance().IsPathFinished()) { if (TryContinueAutoPFToWorldDestDirect()) return true; Finish(); m_pHost.m_MoveCtrl.SendStopMoveCmd(EC_Utility.ToVector3(vCurPos), fSpeed, iMoveMode); } else { // C++: SendMoveCmd(vCurPos, 1, vCurDest, cdr.vAbsVelocity, iMoveMode) m_pHost.m_MoveCtrl.SendMoveCmd(vCurPos, 1, vCurDest, cdr.vAbsVelocity, iMoveMode); } } } #else else if (IsAutoPF()) { m_bSwitchTo2D = true; } #endif } else { m_pHost.m_MoveCtrl.SendStopMoveCmd(EC_Utility.ToVector3(vCurPos), m_pHost.GetGroundSpeed(), (int)GPMoveMode.GP_MOVE_SLIDE); Finish(); } return true; } // Tick routine of flying or swimming protected bool Tick_FlySwim(float fDeltaTime) { A3DVECTOR3 vCurPos = m_pHost.GetPos(); int iMoveMode = (m_pHost.m_iMoveEnv == (int)MoveEnvironment.MOVEENV_AIR) ? (int)GPMoveMode.GP_MOVE_AIR : (int)GPMoveMode.GP_MOVE_WATER; float na, fMaxSpeed; bool bInAir; //Debug.LogError("Tick_FlySwim m_pHost.m_dwMoveRelDir = " + m_pHost.m_dwMoveRelDir); if (m_pHost.m_iMoveEnv == (int)MoveEnvironment.MOVEENV_AIR) { bInAir = true; na = CECHostMove.EC_NACCE_AIR; fMaxSpeed = m_pHost.GetFlySpeed(); } else { bInAir = false; na = CECHostMove.EC_NACCE_WATER; fMaxSpeed = m_pHost.GetSwimSpeedSev(); } if (m_bReadyCancel || m_bMeetSlide) { m_pHost.m_MoveCtrl.SendStopMoveCmd(EC_Utility.ToVector3(vCurPos), fMaxSpeed, iMoveMode | (int)GPMoveMode.GP_MOVE_RUN); Finish(); return true; } // for auto move if (m_bUseAutoMoveDialog && m_fAutoHeight > 0.0f && m_iDestType != DestTypes.DEST_PUSH) { if (m_pHost.m_dwMoveRelDir == 0) { if ((int)(vCurPos.y / 10.0f) == (int)m_fAutoHeight) { if (!m_bReachedHeight) { m_pHost.m_vVelocity.y = 0.0f; m_pHost.m_dwMoveRelDir &= ~((uint)(CECHostPlayer.MOVE_DIR.MD_ABSUP | CECHostPlayer.MOVE_DIR.MD_ABSDOWN)); m_bReachedHeight = true; } } else { if (!m_bReachedHeight) { if (vCurPos.y < m_fAutoHeight * 10.0f) { m_pHost.m_dwMoveRelDir &= ~((uint)(CECHostPlayer.MOVE_DIR.MD_ABSDOWN)); m_pHost.m_dwMoveRelDir |= ((uint)CECHostPlayer.MOVE_DIR.MD_ABSUP); } else if (vCurPos.y > m_fAutoHeight * 10.0f) { m_pHost.m_dwMoveRelDir &= ~((uint)(CECHostPlayer.MOVE_DIR.MD_ABSUP)); m_pHost.m_dwMoveRelDir |= (uint)CECHostPlayer.MOVE_DIR.MD_ABSDOWN; } } else // Auto adjust height, so we should refresh move height { m_fAutoHeight = vCurPos.y / 10.0f; } } } else // Player manually set auto move height { m_bReachedHeight = true; m_fAutoHeight = vCurPos.y / 10.0f; } } if ((m_pHost.m_dwMoveRelDir & (uint)(CECHostPlayer.MOVE_DIR.MD_LEFT | CECHostPlayer.MOVE_DIR.MD_RIGHT | CECHostPlayer.MOVE_DIR.MD_FORWARD | CECHostPlayer.MOVE_DIR.MD_BACK)) != 0) m_iDestType = DestTypes.DEST_PUSH; ON_AIR_CDR_INFO cdr = m_pHost.m_AirCDRInfo; if (m_iDestType == DestTypes.DEST_DIR) { Vector3 vPushDir = Vector3.zero; m_pHost.GetPushDir(ref vPushDir, (uint)CECHostPlayer.MOVE_DIR.MD_ALL, 0); vPushDir.x = vPushDir.z = 0.0f; float fSpeed1H = m_pHost.m_vVelocity.MagnitudeH(); float fSpeed1V = m_pHost.m_vVelocity.y; A3DVECTOR3 vMoveDirH = m_vMoveDest; float pa = CECHostMove.EC_PUSH_ACCE; float fSpeed2H = fSpeed1H + (pa + na) * fDeltaTime; if (Math.Abs(pa - 0) < float.Epsilon && fSpeed2H < 0.0f) fSpeed2H = 0.0f; // Only resistance couldn't generate negative speed else if (fSpeed2H > fMaxSpeed) fSpeed2H = fMaxSpeed; Glide(5.0f, vMoveDirH, fDeltaTime, bInAir); vMoveDirH = m_pHost.GetModelMoveDir(); vMoveDirH.y = 0; vMoveDirH.Normalize(); // Vertical speed float fSpeed2V = CalcFlySwimVertSpeed(fSpeed1V, vPushDir.y, CECHostMove.EC_PUSH_ACCE, fDeltaTime); A3DVECTOR3 vVel2 = vMoveDirH * fSpeed2H + GPDataTypeHelper.g_vAxisY * fSpeed2V; // Air/water move vCurPos = m_pHost.m_MoveCtrl.AirWaterMove(vVel2, fDeltaTime, bInAir); m_pHost.SetPos(EC_Utility.ToVector3(vCurPos)); m_pHost.m_vVelocity = vVel2; if (m_pHost.m_MoveCtrl.MoveBlocked() >= 3) { Finish(); m_pHost.m_MoveCtrl.SendStopMoveCmd(EC_Utility.ToVector3(vCurPos), fMaxSpeed, iMoveMode | (int)GPMoveMode.GP_MOVE_RUN); } else m_pHost.m_MoveCtrl.SendMoveCmd(vCurPos, 0, m_vMoveDest, vVel2, iMoveMode | (int)GPMoveMode.GP_MOVE_RUN); } else if (m_iDestType == DestTypes.DEST_2D) { if (TryFinishDest2DIfArrived(vCurPos, fMaxSpeed, iMoveMode, true)) return true; Vector3 vPushDir = Vector3.zero; m_pHost.GetPushDir(ref vPushDir, (uint)CECHostPlayer.MOVE_DIR.MD_ALL, 0f); vPushDir.x = vPushDir.z = 0.0f; float fSpeed1H = m_pHost.m_vVelocity.MagnitudeH(); float fSpeed1V = m_pHost.m_vVelocity.y; A3DVECTOR3 vMoveDirH = m_vMoveDest - vCurPos; vMoveDirH.y = 0.0f; float fDistH = vMoveDirH.Normalize(); float pa = 0.0f; // Calculate the distance to reduce velocity to 0 float s = -0.5f * fSpeed1H * fSpeed1H / na; if (fDistH > s - 0.01f) pa = CECHostMove.EC_PUSH_ACCE; float fSpeed2H = fSpeed1H + (pa + na) * fDeltaTime; if (Math.Abs(pa - 0f) < float.Epsilon && fSpeed2H < 0.0f) fSpeed2H = 0.0f; // Only resistance couldn't generate negative speed else if (fSpeed2H > fMaxSpeed) fSpeed2H = fMaxSpeed; Glide(fDistH / fMaxSpeed, vMoveDirH, fDeltaTime, bInAir); vMoveDirH = m_pHost.GetModelMoveDir(); vMoveDirH.y = 0; vMoveDirH.Normalize(); // Vertical speed float fSpeed2V = CalcFlySwimVertSpeed(fSpeed1V, vPushDir.y, CECHostMove.EC_PUSH_ACCE, fDeltaTime); A3DVECTOR3 vVel2 = vMoveDirH * fSpeed2H + GPDataTypeHelper.g_vAxisY * fSpeed2V; // Air/water move vCurPos = m_pHost.m_MoveCtrl.AirWaterMove(vVel2, fDeltaTime, bInAir); if (m_pHost.m_MoveCtrl.MoveBlocked() >= 3) { vVel2.Clear(); Finish(); } else { // Reached destination ? A3DVECTOR3 vMoveDelta = vCurPos - m_pHost.GetPos(); vMoveDelta.y = 0.0f; float fMoveDistH = vMoveDelta.Normalize(); if (fMoveDistH >= fDistH) { vVel2.x = vVel2.z = 0.0f; if (Math.Abs(vVel2.y - 0f) < float.Epsilon) { Finish(); } else { if (m_bUseAutoMoveDialog) { Finish(); vVel2.y = 0.0f; } m_iDestType = DestTypes.DEST_PUSH; } } } m_pHost.SetPos(EC_Utility.ToVector3(vCurPos)); m_pHost.m_vVelocity = vVel2; if (m_bFinished) m_pHost.m_MoveCtrl.SendStopMoveCmd(EC_Utility.ToVector3(vCurPos), vVel2.Magnitude(), iMoveMode | (int)GPMoveMode.GP_MOVE_RUN); else m_pHost.m_MoveCtrl.SendMoveCmd(vCurPos, 0, m_vMoveDest, vVel2, iMoveMode | (int)GPMoveMode.GP_MOVE_RUN); } else if (m_iDestType == DestTypes.DEST_3D) { float fSpeed1 = m_pHost.m_vVelocity.Magnitude(); A3DVECTOR3 vMoveDir = m_vMoveDest - vCurPos; float fDist = vMoveDir.Normalize(); float pa = 0.0f; // Calculate the distance to reduce velocity to 0 float s = -0.5f * fSpeed1 * fSpeed1 / na; if (fDist > s - 0.01f) pa = CECHostMove.EC_PUSH_ACCE; float fSpeed2 = fSpeed1 + (pa + na) * fDeltaTime; if (Math.Abs(pa - 0f) < float.Epsilon && fSpeed2 < 0.0f) fSpeed2 = 0.0f; // Only resistance couldn't generate negative speed AAssist.a_Clamp(ref fSpeed2, -fMaxSpeed, fMaxSpeed); Vector3 vMoveDirH = new Vector3(vMoveDir.x, 0.0f, vMoveDir.z); if (vMoveDirH != Vector3.zero) { //m_pHost.StartModelMove(vMoveDirH, g_vAxisY, 100); //m_pHost.ChangeModelTargetDirAndUp(vMoveDirH, g_vAxisY); m_pHost.SetRotationHP(vMoveDirH); } // Air/water move // A3DVECTOR3 vVel1 = vMoveDir * fSpeed1; A3DVECTOR3 vVel2 = vMoveDir * fSpeed2; vCurPos = m_pHost.m_MoveCtrl.AirWaterMove(vMoveDir, fSpeed2, fDeltaTime, bInAir, false); if (m_pHost.m_MoveCtrl.MoveBlocked() >= 3) { vVel2.Clear(); Finish(); } else { // Reached destination ? A3DVECTOR3 vMoveDelta = vCurPos - m_pHost.GetPos(); float fMoveDist = vMoveDelta.Normalize(); if (fMoveDist >= fDist) { vVel2.Clear(); Finish(); m_bUseAutoMoveDialog = false; } } m_pHost.SetPos(EC_Utility.ToVector3(vCurPos)); m_pHost.m_vVelocity = vVel2; if (m_bFinished) m_pHost.m_MoveCtrl.SendStopMoveCmd(EC_Utility.ToVector3(vCurPos), fMaxSpeed, iMoveMode | (int)GPMoveMode.GP_MOVE_RUN); else m_pHost.m_MoveCtrl.SendMoveCmd(vCurPos, 1, m_vMoveDest, vVel2, iMoveMode | (int)GPMoveMode.GP_MOVE_RUN); } else if (m_iDestType == DestTypes.DEST_PUSH) { Vector3 vPushDir = Vector3.zero; bool bPush = m_pHost.GetPushDir(ref vPushDir, (uint)(CECHostPlayer.MOVE_DIR.MD_FORWARD | CECHostPlayer.MOVE_DIR.MD_BACK | CECHostPlayer.MOVE_DIR.MD_LEFT | CECHostPlayer.MOVE_DIR.MD_RIGHT), fDeltaTime); if (!bPush) { //vPushDir = m_pHost.GetCameraCoord().GetDir(); vPushDir.y = 0; vPushDir.Normalize(); } int nPitchDir = 0; if ((m_pHost.m_dwMoveRelDir & (int)(CECHostPlayer.MOVE_DIR.MD_LEFT | CECHostPlayer.MOVE_DIR.MD_RIGHT)) == 0) { A3DVECTOR3 vOldDir = m_pHost.GetModelMoveDir(); vOldDir.y = 0; vOldDir.Normalize(); A3DVECTOR3 vNewDir = EC_Utility.ToA3DVECTOR3(vPushDir); vNewDir.y = 0; vNewDir.Normalize(); float fAngle = A3DVECTOR3.DotProduct(vOldDir, vNewDir); if (fAngle < 1.0f - 1e-4) { A3DVECTOR3 vUp_fAngle = A3DVECTOR3.CrossProduct(vOldDir, vNewDir); if (vUp_fAngle.y > 0) nPitchDir = 1; else nPitchDir = -1; if ((m_pHost.m_dwMoveRelDir & (uint)CECHostPlayer.MOVE_DIR.MD_BACK) != 0) nPitchDir = -nPitchDir; } } if (m_pHost.m_dwMoveRelDir != 0) { if ((m_pHost.m_dwMoveRelDir & ~(uint)(CECHostPlayer.MOVE_DIR.MD_ABSDOWN | CECHostPlayer.MOVE_DIR.MD_ABSUP)) != 0) { float fPitchDelta = (/*m_pHost.UsingWing()*/m_pHost.GetWingType() == enumWingType.WINGTYPE_WING ? push_pitch_vel_wing : push_pitch_vel_fly_sword) * fDeltaTime; if ((m_pHost.m_dwMoveRelDir & (uint)CECHostPlayer.MOVE_DIR.MD_LEFT) != 0 || nPitchDir == -1) { if ((m_pHost.m_dwMoveRelDir & (uint)CECHostPlayer.MOVE_DIR.MD_BACK) != 0) m_fPushPitch -= fPitchDelta; else m_fPushPitch += fPitchDelta; } else if ((m_pHost.m_dwMoveRelDir & (uint)CECHostPlayer.MOVE_DIR.MD_RIGHT) != 0 || nPitchDir == 1) { if ((m_pHost.m_dwMoveRelDir & (uint)CECHostPlayer.MOVE_DIR.MD_BACK) != 0) m_fPushPitch += fPitchDelta; else m_fPushPitch -= fPitchDelta; } else if (m_fPushPitch > fPitchDelta) m_fPushPitch -= fPitchDelta; else if (m_fPushPitch < -fPitchDelta) m_fPushPitch += fPitchDelta; else m_fPushPitch = 0; // TO DO: fix after //A3DVECTOR3 vRight = m_pHost.GetCameraCoord().GetRight(); //float fLean = -Math.Asin(m_pHost.GetCameraCoord().GetDir().y); //AAssist.a_Clamp(ref fLean, -lean_max_ang, lean_max_ang); //if (Math.Abs(fLean) > DEG2RAD(3.0f)) //{ // vPushDir = a3d_RotatePosAroundAxis(vPushDir, vRight, fLean); // vUp = a3d_RotatePosAroundAxis(g_vAxisY, vRight, fLean); //} //else // vUp = EC_Utility.ToVector3(GPDataTypeHelper.g_vAxisY); //float pitch_ang = /*m_pHost.UsingWing()*/ m_pHost.GetWingType() == enumWingType.WINGTYPE_WING ? pitch_ang_wing : pitch_ang_fly_sword; //AAssist.a_Clamp(ref m_fPushPitch, -pitch_ang, pitch_ang); //if (Math.Abs(m_fPushPitch) > DEG2RAD(4.0f)) // vUp = a3d_RotatePosAroundAxis(vUp, vPushDir, m_fPushPitch); //m_pHost.StartModelMove(vPushDir, vUp, 0); if (vPushDir != Vector3.zero) { m_pHost.SetRotationHPWithTime(vPushDir, 0.1f); } } // if (bPush) if (bPush || (m_pHost.m_dwMoveRelDir & (uint)(CECHostPlayer.MOVE_DIR.MD_ABSDOWN | CECHostPlayer.MOVE_DIR.MD_ABSUP)) != 0) { // float pa = bPush ? EC_PUSH_ACCE : 0.0f; float pa = CECHostMove.EC_PUSH_ACCE; float na1 = m_pHost.m_iMoveEnv == (int)MoveEnvironment.MOVEENV_AIR ? CECHostMove.EC_NACCE_AIR : CECHostMove.EC_NACCE_WATER; float fAccel = pa + na1; float fSpeed1 = m_pHost.m_vVelocity.Magnitude(); float fSpeed2 = fSpeed1 + fAccel * fDeltaTime; AAssist.a_Clamp(ref fSpeed2, 0.0f, fMaxSpeed); // Air/water move Vector3 vVelDir = Vector3.zero; if (bPush) vVelDir = vPushDir; if ((m_pHost.m_dwMoveRelDir & (uint)CECHostPlayer.MOVE_DIR.MD_ABSDOWN) != 0) { vVelDir += -EC_Utility.ToVector3(GPDataTypeHelper.g_vAxisY); vVelDir.Normalize(); } else if ((m_pHost.m_dwMoveRelDir & (uint)CECHostPlayer.MOVE_DIR.MD_ABSUP) != 0) { vVelDir += EC_Utility.ToVector3(GPDataTypeHelper.g_vAxisY); vVelDir.Normalize(); } // A3DVECTOR3 vVel = vPushDir * fSpeed2; Vector3 vVel = vVelDir * fSpeed2; vCurPos = m_pHost.m_MoveCtrl.AirWaterMove(EC_Utility.ToA3DVECTOR3(vVel), fDeltaTime, bInAir); if (m_pHost.m_MoveCtrl.MoveBlocked() >= 3) { Finish(); m_pHost.m_vVelocity.Clear(); m_pHost.m_MoveCtrl.SendStopMoveCmd(EC_Utility.ToVector3(vCurPos), fMaxSpeed, iMoveMode | (int)GPMoveMode.GP_MOVE_RUN); } else { m_pHost.SetPos(EC_Utility.ToVector3(vCurPos)); if (m_bUseAutoMoveDialog) { m_fAutoHeight = vCurPos.y / 10.0f; } m_pHost.m_vVelocity = EC_Utility.ToA3DVECTOR3(vVel); m_pHost.m_MoveCtrl.SendMoveCmd(vCurPos, 2, GPDataTypeHelper.g_vOrigin, EC_Utility.ToA3DVECTOR3(vVel), iMoveMode | (int)GPMoveMode.GP_MOVE_RUN); } } } else { if (!m_bUseAutoMoveDialog) Finish(); else m_iDestType = DestTypes.DEST_2D; m_fPushPitch = 0; m_pHost.m_vVelocity.Clear(); m_pHost.m_MoveCtrl.SendStopMoveCmd(EC_Utility.ToVector3(vCurPos), fMaxSpeed, iMoveMode | (int)GPMoveMode.GP_MOVE_RUN); } } else if (m_iDestType == DestTypes.DEST_STANDJUMP) { // If host player fly off when jumping up, code will go here. In the // case, just stop move work is well. Finish(); } else if (IsAutoPF()) { // EC_HPWorkMove.cpp Tick_FlySwim: AutoPF in air/water does not walk waypoints — drop path and use DEST_2D. #if ENABLE_CEC_INTELLIGENT_ROUTE global::BrewMonster.Scripts.CECIntelligentRoute.Instance().ResetSearch(); #endif m_bSwitchTo2D = true; } return true; } // Start gliding protected void Glide(float fMoveTime, A3DVECTOR3 vMoveDirH, float fDeltaTime, bool bFly) { } // Calculate vertical speed when fly or swim protected float CalcFlySwimVertSpeed(float fSpeed1, float fPushDir, float fPushAccel, float fDeltaTime) { float pa = Math.Abs(fPushDir - 0.0f) > float.Epsilon ? fPushAccel : 0.0f; float na = 0.0f, fMaxSpeed; if (m_pHost.m_iMoveEnv == CECPlayer.Move_environment.MOVEENV_AIR) { na = CECHostMove.EC_NACCE_AIR; fMaxSpeed = m_pHost.GetFlySpeed(); } else { na = CECHostMove.EC_NACCE_WATER; fMaxSpeed = m_pHost.GetSwimSpeedSev(); } // When player free fall into water, fSpeed1 may be >= fMaxSpeed if (Math.Abs(fSpeed1) > fMaxSpeed) { na *= 2.0f; if (fPushDir * fSpeed1 > 0.0f) pa = 0.0f; } // Vertical accelerate float fAccel = 0.0f; if (fPushDir > 0.0f) fAccel = pa + na; else if (fPushDir < 0.0f) fAccel = -(pa + na); else if (fSpeed1 > 0.0f) fAccel = na; else if (fSpeed1 < 0.0f) fAccel = -na; // Vertical speed float fSpeed2 = fSpeed1 + fAccel * fDeltaTime; if (Math.Abs(pa - 0.0f) < float.Epsilon && fSpeed2 * fSpeed1 < 0.0f) fSpeed2 = 0.0f; // If accelerate and speed on same direction, limit speed if (fAccel * fSpeed2 > 0.0f) AAssist.a_Clamp(ref fSpeed2, -fMaxSpeed, fMaxSpeed); return fSpeed2; } protected void ClearResetUseAutoPF() { m_bResetAutoPF = false; } protected void UpdateResetUseAutoPF() { if (!m_bResetAutoPF) { return; } #if ENABLE_CEC_INTELLIGENT_ROUTE global::BrewMonster.Scripts.CECIntelligentRoute.Instance().SetUsage(global::BrewMonster.Scripts.CECIntelligentRoute.Usage.enumUsageWorkMove); global::BrewMonster.Scripts.CECIntelligentRoute.Instance().ResetSearch(); if (IsAutoPF()) { bool bSwitchTo2D = true; while (true) { // EC_HPWorkMove.cpp UpdateResetUseAutoPF: skip Search while flying (bSwitchTo2D stays true). if (m_pHost.IsFlying()) break; // Brush test is not fully ported yet; pass null for now (static movemap only). // BrushTest 暂未完整移植;当前先传 null(仅静态 movemap)。 var ret = global::BrewMonster.Scripts.CECIntelligentRoute.Instance().Search( m_pHost.GetPos(), m_vMoveDest, null); if (ret == CECIntelligentRoute.SearchResult.enumSearchNoPath) { // C++ UpdateResetUseAutoPF: Search != success → break; bSwitchTo2D stays true (fallback straight line). // Do not Finish() — same as EC_HPWorkMove.cpp (no path still switches to DEST_2D). int mapX = Mathf.RoundToInt(m_vMoveDest.x / 10.0f) + 400; int mapY = Mathf.RoundToInt(m_vMoveDest.y / 10.0f); int mapZ = Mathf.RoundToInt(m_vMoveDest.z / 10.0f) + 550; Debug.LogWarning( $"[CECIntelligentRoute] No path; switching to DEST_2D. Map coords: ({mapX}, {mapZ}, ↑{mapY})."); break; } else if (ret != CECIntelligentRoute.SearchResult.enumSearchSuccess) { break; } bSwitchTo2D = false; OrientHostToFirstAutoPFWaypoint(); break; } if (bSwitchTo2D) { m_bSwitchTo2D = true; } } #endif ClearResetUseAutoPF(); } protected virtual uint GetMoveRelDirMask() { // TODO: hook up CECHostPlayer move-direction flags when available. return m_pHost.m_dwMoveRelDir; } private void UpdateFacingFromDelta(A3DVECTOR3 nextPos) { A3DVECTOR3 prevPos = m_pHost.GetPos(); Vector3 delta = EC_Utility.ToVector3(nextPos - prevPos); delta.y = 0.0f; if (delta.sqrMagnitude > 1e-6f) { delta.Normalize(); m_pHost.SetDirAndUp(delta, Vector3.up); } } private A3DVECTOR3 GetCurrentModelDir() { Vector3 forward = m_pHost != null ? m_pHost.transform.forward : Vector3.forward; return new A3DVECTOR3(forward.x, forward.y, forward.z); } } }