using BrewMonster.Network; using BrewMonster.Scripts; using CSNetwork.GPDataType; using System; using UnityEngine; using BrewMonster.Scripts.World; namespace BrewMonster { public class CECHostMove { ushort m_wMoveStamp; float m_fMoveTime; CECHostPlayer m_pHost; bool m_bStop; const float MOVECMD_INTERVAL = .5f; A3DVECTOR3 m_vLastSevPos; STOPMOVE m_DelayedStop; ulong cmdstopdelayCounter = 500; ulong cmdmovedelayCounter = 500; ulong m_dwLastTime; Vector3 m_vLastPos; float m_fAverSpeedH; int m_iBlockedCnt; // Move blocked counter bool m_bSlideLock; // Locked when slide float m_fBlockTime; // Block time counter float m_fBlockMove; // Block move counter A3DVECTOR3 m_vBlockMove; bool m_bLocalMove; // true, Moving info isn't sent to server public A3DVECTOR3 m_vFlashTPNormal; // Negative accelerate in water and air public const float EC_NACCE_AIR = -5.0f; public const float EC_NACCE_WATER = -5.0f; // Push accelerate public const float EC_PUSH_ACCE = 8.0f; // Normal push accelerate in air and water public const float EC_FLYOFF_ACCE = 10.0f; // Fly off accelerate public const float EC_SLOPE_Y = 0.5f; public const float EC_GRAVITY = 9.8f; // Move length minimum threshold public const float MIN_MOVELEN_IN_AIR_WATER = 0.5f; public const float MIN_MOVELEN_ON_GROUND = 0.5f; public const float MIN_MOVELEN_FOR_DETECT_VIBRATION = 0.05f; public CECHostMove(CECHostPlayer pHost) { m_wMoveStamp = 0; m_fMoveTime = 0.0f; m_pHost = pHost; m_bStop = false; cmdstopdelayCounter = 500; m_DelayedStop = new STOPMOVE(); } public void Tick(ulong dwDeltaTime) { cmdstopdelayCounter += dwDeltaTime; cmdmovedelayCounter += dwDeltaTime; if (cmdstopdelayCounter >= 500 && m_DelayedStop.bValid) { var m = m_DelayedStop; // a_GetTime() . Environment.TickCount (ms) ulong dwCurrent = (ulong)Mathf.RoundToInt(Time.time * 1000); // iTime: thời gian tích lũy (ms) + delta từ timestamp đến hiện tại int iTime = (int)((m.fTime * 1000f) + ((dwCurrent > m.dwTimeStamp) ? (dwCurrent - m.dwTimeStamp) : 0)); // tính tốc độ trung bình (giây = iTime * 0.001f) float fSpeed = l_CalcAverageSpeed(EC_Utility.ToVector3(m_vLastSevPos), m.vPos, iTime * 0.001f, m.fSpeed); int iMoveMode = m.iMoveMode; // make a potential check with tuojigua iMoveMode |= (int)GPMoveMode.GP_MOVE_DEAD; UnityGameSession.Instance.c2s_SendCmdStopMove( m.vPos, fSpeed, iMoveMode, m.byDir, m_wMoveStamp++, iTime ); // Record this position m_vLastSevPos = EC_Utility.ToA3DVECTOR3(m.vPos); Reset(); } m_dwLastTime = Math.Max(m_dwLastTime, 1); Vector3 vOffset = m_pHost.transform.position - m_vLastPos; m_fAverSpeedH = EC_Utility.MagnitudeH(vOffset) * 1000.0f / m_dwLastTime; m_vLastPos = m_pHost.transform.position; m_dwLastTime = dwDeltaTime; } public void SendStopMoveCmd() { int iMoveMode = (int)GPMoveMode.GP_MOVE_RUN; float fSpeed = 0f; switch (m_pHost.GetMoveEnv()) { case CECPlayer.Move_environment.MOVEENV_AIR: iMoveMode |= (int)GPMoveMode.GP_MOVE_AIR; fSpeed = m_pHost.GetFlySpeed(); break; //case EC_Player.Move_environment.MOVEENV_WATER: // iMoveMode |= (int)GPMoveMode.GP_MOVE_WATER; // fSpeed = m_pHost.GetSwimSpeedSev(); // break; default: fSpeed = m_pHost.GetGroundSpeed(); break; } SendStopMoveCmd(EC_Utility.ToVector3(m_pHost.GetPos()), fSpeed, iMoveMode); } public void SetHostLastPos(A3DVECTOR3 vPos) { m_vLastPos = EC_Utility.ToVector3(vPos); } public void SendStopMoveCmd(in Vector3 vPos, float fSpeed, int iMoveMode) { iMoveMode |= (int)GPMoveMode.GP_MOVE_DEAD; if (cmdstopdelayCounter >= 500) { Vector3 vDir = m_pHost.transform.forward; byte byDir = EC_Utility.glb_CompressDirH(vDir.x, vDir.z); fSpeed = l_CalcAverageSpeed(EC_Utility.ToVector3(m_vLastSevPos), vPos, m_fMoveTime, fSpeed); int iTime = (int)(m_fMoveTime * 1000); UnityGameSession.Instance.c2s_SendCmdStopMove(vPos, fSpeed, iMoveMode, byDir, m_wMoveStamp++, iTime); m_vLastSevPos = EC_Utility.ToA3DVECTOR3(vPos); Reset(); } else { Vector3 vDir = m_pHost.transform.forward; byte byDir = EC_Utility.glb_CompressDirH(vDir.x, vDir.z); if (!m_DelayedStop.bValid) { m_DelayedStop.dwTimeStamp = (ulong)Mathf.RoundToInt(Time.time * 1000); m_DelayedStop.fTime = m_fMoveTime; } m_DelayedStop.bValid = true; m_DelayedStop.byDir = byDir; m_DelayedStop.vPos = vPos; m_DelayedStop.iMoveMode = iMoveMode; m_DelayedStop.fSpeed = fSpeed; } m_fMoveTime = 0.0f; } public void SendMoveCmd(in Vector3 vCurPos, in Vector3 vVel, int iMoveMode, bool bForceSend = false) { Vector3 vMoveDir = vVel; float fSpeed = vMoveDir.magnitude; SendMoveCmd(vCurPos, fSpeed, iMoveMode, bForceSend); } public void SendMoveCmd(A3DVECTOR3 vCurPos, int iDestType, A3DVECTOR3 vDest, A3DVECTOR3 vVel, int iMoveMode, bool bForceSend = false) { A3DVECTOR3 vMoveDir = vVel; float fSpeed = vMoveDir.Normalize(); SendMoveCmd(vCurPos, iDestType, vDest, vMoveDir, fSpeed, iMoveMode, bForceSend); } void SendMoveCmd(A3DVECTOR3 vCurPos, int iDestType, A3DVECTOR3 vDest, A3DVECTOR3 vMoveDir, float fSpeed, int iMoveMode, bool bForceSend) { Vector3 pos = new Vector3(vCurPos.x, vCurPos.y, vCurPos.z); SendMoveCmd(pos, fSpeed, iMoveMode, bForceSend); } void SendMoveCmd(in Vector3 vCurPos, float fSpeed, int iMoveMode, bool bForceSend) { if (m_bStop) { // m_CmdTimeCnt.Reset(); cmdmovedelayCounter = (ulong)(m_fMoveTime * 1000); m_bStop = false; } if (!bForceSend && cmdmovedelayCounter < 500) return; int iTime = (int)(m_fMoveTime * 1000); if (iTime < 200) { if (iTime == 0 || !bForceSend) { // if time is too little, wait again cmdmovedelayCounter = (ulong)(iTime); return; } } cmdmovedelayCounter = 0; m_DelayedStop.bValid = false; fSpeed = l_CalcAverageSpeed(EC_Utility.ToVector3(m_vLastSevPos), vCurPos, m_fMoveTime, fSpeed); m_fMoveTime = 0.0f; iMoveMode |= (int)GPMoveMode.GP_MOVE_DEAD; UnityGameSession.Instance.c2s_CmdPlayerMove(vCurPos, vCurPos, iTime/* MOVECMD_INTERVAL */, fSpeed, iMoveMode, m_wMoveStamp++); m_vLastSevPos = EC_Utility.ToA3DVECTOR3(vCurPos); } float l_CalcAverageSpeed(in Vector3 p1, in Vector3 p2, float fTime, float fDefSpeed) { if (Mathf.Approximately(fTime, 0f)) return fDefSpeed; Vector3 d = p2 - p1; float fSpeed = d.magnitude / fTime; if (fTime < 0.05f || fSpeed > 50.0f) { // tương đương ASSERT(0) trong C++, ở đây có thể Debug.LogWarning UnityEngine.Debug.LogWarning("CalcAverageSpeed: invalid input, fallback to default speed."); return fDefSpeed; } return fSpeed; } public void Reset() { cmdstopdelayCounter = 0; m_bStop = true; m_fBlockTime = 0.0f; m_fBlockMove = 0.0f; m_DelayedStop.bValid = false; } public void GroundMove(float ftime) { m_fMoveTime += ftime; } public void SetLastSevPos(A3DVECTOR3 vPos) { m_vLastSevPos = vPos; } struct STOPMOVE { public bool bValid; // Valid flag public Vector3 vPos; public float fSpeed; public int iMoveMode; public byte byDir; public ulong dwTimeStamp; public float fTime; }; // Check whether host meet a slope // vMoveDirH: normalized horizontal moving direction // fMaxSpeed (out): maximum vertical speed public bool MeetSlope(A3DVECTOR3 vMoveDirH, float fMaxSpeedV) { A3DVECTOR3 vTangent = m_pHost.m_GndInfo.vGndNormal; float d = vTangent.MagnitudeH(); float tan = d / (float)Math.Abs(vTangent.y); float max = m_pHost.GetGroundSpeed() * tan * 0.96f; // Prevent max is too small, tan60 = 1.732 EC_Utility.a_ClampFloor(max, m_pHost.GetGroundSpeed() * 1.732f); fMaxSpeedV = EC_Utility.a_Min(max, 19.5f); // fMaxSpeedV = 100.0f; vTangent.y = 0.0f; vTangent.Normalize(); if (A3DVECTOR3.DotProduct(vMoveDirH, vTangent) <= -0.85f) return true; return false; } public A3DVECTOR3 GroundMove(A3DVECTOR3 vDirH, float fSpeedH, float fTime, float fSpeedV = 0f, float fGravity = 9.8f) { A3DVECTOR3 vRealDirH = vDirH; if (Math.Abs(vRealDirH.y) > 0.0001) { vRealDirH.y = 0.0f; vRealDirH.Normalize(); } // OnGroundMove only accept positive speed value if (fSpeedH < 0.0f) { vRealDirH = -vDirH; fSpeedH = -fSpeedH; } int idInst = EC_Game.GetGameRun().GetWorld().GetInstanceID(); CECInstance pInstance = EC_Game.GetGameRun().GetInstance(idInst); if (pInstance.GetLimitJump()) fGravity *= 4.0f; Vector3 posStart = EC_Utility.ToVector3(m_pHost.m_aabbServer.Center); CDR_INFO cdr = m_pHost.m_CDRInfo; cdr.vCenter = m_pHost.m_aabbServer.Center; cdr.vXOZVelDir = vRealDirH; cdr.fSpeed = fSpeedH; cdr.t = fTime; cdr.fGravityAccel = fGravity; cdr.fYVel += fSpeedV; EC_CDR.OnGroundMove(ref cdr); m_pHost.m_CDRInfo = cdr; //if (g_pGame.GetGameRun().GetWorld().GetAssureMove()) // g_pGame.GetGameRun().GetWorld().GetAssureMove().AssureMove(m_pHost.m_aabbServer.Center, cdr.vCenter); if (cdr.vTPNormal != new A3DVECTOR3(0)) m_pHost.SetGroundNormal(cdr.vTPNormal); else m_pHost.SetGroundNormal(GPDataTypeHelper.g_vAxisY); A3DVECTOR3 vNewPos = cdr.vCenter - GPDataTypeHelper.g_vAxisY * m_pHost.m_aabbServer.Extents.y; m_iBlockedCnt = 0; m_fBlockMove += (vNewPos - m_pHost.GetPos()).Magnitude(); m_vBlockMove += vNewPos - m_pHost.GetPos(); m_fBlockTime += fTime; //TO DO: in c++ set 1.0f. Convert to c#, this logic is wrong. If set time is 2.0, game run correct if ((m_fBlockTime) >= 2.0f) { if (m_fBlockMove < GPDataTypeHelper.MIN_MOVELEN_ON_GROUND || m_vBlockMove.Magnitude() < GPDataTypeHelper.MIN_MOVELEN_FOR_DETECT_VIBRATION) { m_iBlockedCnt = 5; } m_fBlockTime = 0.0f; m_fBlockMove = 0.0f; m_vBlockMove.Clear(); } m_fMoveTime += fTime; m_pHost.m_CDRInfo.vTPNormal = cdr.vTPNormal; //Debug.LogError("speed = " + (Vector3.Distance(posStart, EC_Utility.ToVector3(vNewPos)) / fTime)); return vNewPos; } public int MoveBlocked() { return m_iBlockedCnt; } public void SetSlideLock(bool bLock) { m_bSlideLock = bLock; } // Is stoping ? public bool IsStop() { return m_bStop; } // Flash move public A3DVECTOR3 FlashMove(A3DVECTOR3 vDir, float fSpeed, float fDist) { // Copy cdr data from host CDR_INFO cdr = m_pHost.m_CDRInfo; A3DVECTOR3 vStartPos = m_vLastSevPos + EC_Utility.ToA3DVECTOR3(Vector3.up) * cdr.vExtent.y; if (fDist == 0f || fSpeed == 0f) return m_vLastSevPos; // Get horizonal directional A3DVECTOR3 vRealDirH = vDir; if (vRealDirH.y != 0f) { vRealDirH.y = 0.0f; vRealDirH.Normalize(); } const int iNumSeg = 6; float[] aFactors = { 0.3f, 0.3f, 0.15f, 0.1f, 0.1f, 0.5f }; float fTotalTime = fDist / fSpeed; A3DVECTOR3 vCurPos = vStartPos; A3DVECTOR3 vCurTPNormal = cdr.vTPNormal; cdr.vCenter = vStartPos; cdr.fYVel = 0.0f; for (int i = 0; i < iNumSeg; i++) { cdr.vXOZVelDir = vRealDirH; cdr.fSpeed = fSpeed; cdr.t = fTotalTime * aFactors[i]; cdr.fGravityAccel = 9.8f; // EC_GRAVITY EC_CDR.OnGroundMove(ref cdr); var world = CECGameRun.Instance?.GetWorld(); if (world?.GetAssureMove() != null) world.GetAssureMove().NoAssureMove(); if ((cdr.vCenter - vStartPos).Magnitude() >= fDist * 0.98f) { // Use the result of last time cdr.vCenter = vCurPos; cdr.vTPNormal = vCurTPNormal; break; } if ((cdr.vCenter - vCurPos).Magnitude() < 0.3f) break; // Record result of this time vCurPos = cdr.vCenter; vCurTPNormal = cdr.vTPNormal; } // Calculate new position vCurPos = cdr.vCenter - GPDataTypeHelper.g_vAxisY * cdr.vExtent.y; m_vFlashTPNormal = cdr.vTPNormal; A3DVECTOR3 vCurrentPlayerPos = m_pHost.GetPos(); return vCurPos; } // Get host's last position sent to server public Vector3 GetLastSevPos() { return EC_Utility.ToVector3(m_vLastSevPos); } public bool GetSlideLock() { return m_bSlideLock; } // Air/Water move public A3DVECTOR3 AirWaterMove(A3DVECTOR3 vSpeed, float fTime, bool bInAir, bool bTrace = false) { A3DVECTOR3 vDir = vSpeed; float fSpeed = vDir.Normalize(); return AirWaterMove(vDir, fSpeed, fTime, bInAir, bTrace); } // Air/Water move public A3DVECTOR3 AirWaterMove(A3DVECTOR3 vDir, float fSpeed, float fTime, bool bInAir, bool bTrace/* false */) { A3DVECTOR3 vRealDir = vDir; // OnAirMove only accept positive speed value if (fSpeed < 0.0f) { vRealDir = -vDir; fSpeed = -fSpeed; } float fMaxSpeed = bInAir ? m_pHost.GetFlySpeed() : m_pHost.GetSwimSpeedSev(); if (fSpeed > fMaxSpeed) fSpeed = fMaxSpeed; ON_AIR_CDR_INFO cdr = m_pHost.m_AirCDRInfo; cdr.vCenter = m_pHost.m_aabbServer.Center; cdr.vVelDir = vRealDir; cdr.fSpeed = fSpeed; cdr.fHeightThresh = bInAir ? m_pHost.m_MoveConst.fMinAirHei : m_pHost.m_MoveConst.fMinWaterHei; cdr.t = fTime; cdr.bOnAir = bInAir; // If player is tracing something, let he fly/swim low enough // if (bTrace) // cdr.fHeightThresh = 0.2f; EC_CDR.OnAirMove(ref cdr); m_pHost.m_AirCDRInfo = cdr; // TO DO: fix later //if(EC_Game.GetGameRun().GetWorld().GetAssureMove() ) // EC_Game.GetGameRun().GetWorld().GetAssureMove().NoAssureMove(); m_pHost.SetGroundNormal(GPDataTypeHelper.g_vAxisY); A3DVECTOR3 vNewPos = cdr.vCenter - GPDataTypeHelper.g_vAxisY * m_pHost.m_aabbServer.Extents.y; m_iBlockedCnt = 0; m_fBlockMove += (vNewPos - m_pHost.GetPos()).Magnitude(); m_vBlockMove += vNewPos - m_pHost.GetPos(); if ((m_fBlockTime += fTime) >= 1.0f) { if (m_fBlockMove < MIN_MOVELEN_IN_AIR_WATER || m_vBlockMove.Magnitude() < MIN_MOVELEN_FOR_DETECT_VIBRATION) { m_iBlockedCnt = 5; } m_fBlockTime = 0.0f; m_fBlockMove = 0.0f; m_vBlockMove.Clear(); } m_fMoveTime += fTime; return vNewPos; } // Set / Get move stamp public void SetMoveStamp(ushort wStamp) { m_wMoveStamp = wStamp; } } public struct CDR_INFO { //the aabb public A3DVECTOR3 vCenter; //@note : the caller should make sure ext(.x, .y, .z) > 0. By Kuiwu[22/9/2005] public A3DVECTOR3 vExtent; public float fStepHeight; // Velocity Info public A3DVECTOR3 vXOZVelDir; public float fYVel; public float fSpeed; // time span ( sec ) public float t; // Gravity acceleration public float fGravityAccel; // the Climb Slope Thresh public float fSlopeThresh; // Tangent plane Info public A3DVECTOR3 vTPNormal; // Absolute Velocity: output for forcast! public A3DVECTOR3 vAbsVelocity; //the moving dist public float fMoveDist; }; }