using BrewMonster; using BrewMonster.Managers; using BrewMonster.Network; using BrewMonster.Scripts; using CSNetwork.GPDataType; using System; using System.Runtime.ConstrainedExecution; using PerfectWorld.Scripts; using UnityEngine; /////////////////////////////////////////////////////////////////////////// // // Class CECHPWorkTrace // /////////////////////////////////////////////////////////////////////////// namespace BrewMonster { public static class TraceObjectType { public const int TRACE_NONE = 0, TRACE_PLAYER = 1, TRACE_MATTER = 2, TRACE_NPC = 3, TRACE_TASKNPC = 4; } public abstract class CECTracedObject { //friend CECHPWorkTrace; protected int m_iTraceType; protected int m_iObjectId; protected int m_iReason; protected bool m_bMoreClose; protected CECHostPlayer m_pHost; public CECTracedObject(int type, int id, CECHostPlayer pHost, int ireason) { m_iTraceType = type; m_iObjectId = id; m_iReason = ireason; m_pHost = pHost; m_bMoreClose = false; } public CECTracedObject(CECTracedObject rhs) { m_iTraceType = rhs.m_iTraceType; m_iObjectId = rhs.m_iObjectId; m_iReason = rhs.m_iReason; m_bMoreClose = rhs.m_bMoreClose; m_pHost = rhs.m_pHost; } public abstract bool OnTargetMissing(); public abstract A3DVECTOR3 GetTargetPos(); public abstract bool OnTouched(); public abstract CECTracedObject Clone(); public virtual bool IsTargetMissing() { if (m_iObjectId != 0) { CECObject pObj = EC_ManMessageMono.Instance.GetObject(m_iObjectId, 0); if (pObj) { return false; } } return true; } public A3DVECTOR3? GetMovePos() { A3DVECTOR3 vMovePos; CECObject pObject = EC_ManMessageMono.Instance.GetObject(m_iObjectId, 0); if (pObject) { if (GPDataTypeHelper.ISPLAYERID(m_iObjectId)) { if (m_iObjectId == m_pHost.GetCharacterID()) { vMovePos = m_pHost.GetPos(); } else { vMovePos = (pObject as EC_ElsePlayer).GetServerPos(); } } else if (GPDataTypeHelper.ISNPCID(m_iObjectId)) { vMovePos = (pObject as CECNPC).GetServerPos(); } else { vMovePos = pObject.GetPos(); } return vMovePos; } return null; } public int GetObjectID() { return m_iObjectId; } public bool CanTouchMoreClose() { return m_bMoreClose; } public void SetMoveCloseFlag(bool bMoveClode) { m_bMoreClose = bMoveClode; } public int GetTraceReason() { return m_iReason; } public bool CanTouchFrom(A3DVECTOR3 vHostPos) { while (m_iObjectId != 0) { A3DVECTOR3 vTargetPos = new A3DVECTOR3(0f, 0f, 0f); vTargetPos = GetTargetPos(); int iTouchReason = 0; if (m_iReason == CECHPWorkTrace.Trace_reason.TRACE_ATTACK) iTouchReason = 1; else if (m_iReason == CECHPWorkTrace.Trace_reason.TRACE_SPELL) iTouchReason = 2; else if (m_iReason == CECHPWorkTrace.Trace_reason.TRACE_TALK) iTouchReason = 3; float fMaxCut = m_bMoreClose ? -1.0f : 1.0f; CECObject pObject = EC_ManMessageMono.Instance.GetObject(m_iObjectId, 0); if (pObject == null) { return false; } float fTouchRadius = 0.0f; if (GPDataTypeHelper.ISPLAYERID(m_iObjectId)) { if (m_iReason == CECHPWorkTrace.Trace_reason.TRACE_TALK) { fTouchRadius = 0.0f; } else { // pObject is usually EC_ElsePlayer/CECPlayer, not CECHostPlayer. // Using GetComponent() returns null and breaks touch checks. CECPlayer pPlayer = pObject as CECPlayer; if (pPlayer == null) { return false; } fTouchRadius = pPlayer.GetTouchRadius(); } return m_pHost.CanTouchTarget(vHostPos, vTargetPos, fTouchRadius, iTouchReason, fMaxCut); } else if (GPDataTypeHelper.ISNPCID(m_iObjectId)) { CECNPC pNPC = pObject as CECNPC; if (pNPC == null) { return false; } fTouchRadius = pNPC.GetTouchRadius(); return m_pHost.CanTouchTarget(vHostPos, vTargetPos, fTouchRadius, iTouchReason, fMaxCut); } else if (GPDataTypeHelper.ISMATTERID(m_iObjectId)) { CECMatter pMatter = (pObject) as CECMatter; if (pMatter == null) { return false; } // For matter, use horizontal distance (ignore Y). // Unity's touch check uses host position + capsule extent, which would otherwise // force the player to get much closer than intended. // C++ behavior expects a stand-off distance (default 3.0). return pMatter.CalcDist(vHostPos, false) < pMatter.GetGatherDist(); } break; } return false; } public int GetTraceType() { return m_iTraceType; } public CECObject GetTargetObject() { return EC_ManMessageMono.Instance.GetObject(m_iObjectId, 0); } }; public class CECTracedNPC : CECTracedObject { protected bool m_bForceAttack; public CECTracedNPC(int type, int id, CECHostPlayer pHost, int ireason, bool bForceAttack = false) : base(type, id, pHost, ireason) { m_bForceAttack = bForceAttack; } public CECTracedNPC(CECTracedNPC rhs) : base(rhs) { m_bForceAttack = rhs.m_bForceAttack; } public override bool OnTargetMissing() { bool bRet = false; if (m_iReason == CECHPWorkTrace.Trace_reason.TRACE_ATTACK) { bRet = true; } else if (m_iReason == CECHPWorkTrace.Trace_reason.TRACE_SPELL) { CECSkill pSkill = m_pHost.m_pPrepSkill; if (pSkill == null || pSkill.GetTargetType() != 2) { bRet = true; m_pHost.m_pPrepSkill = null; } } return bRet; } public override A3DVECTOR3 GetTargetPos() { // 使用 Unity transform 的实时位置,而不是服务器缓存位置(可能滞后 / 未及时刷新), // 否则追踪目的地/触碰判定会偏离导致永远触不到。 // Use live transform position (GetPos) instead of cached server position (GetServerPos), // otherwise tracing destination and touch checks can be wrong and never trigger. return (GetTargetObject() as CECNPC).GetPos(); } public override bool OnTouched() { bool bActionDone = false; if (GPDataTypeHelper.ISNPCID(m_iObjectId)) { CECNPC pNPC = (CECNPC)GetTargetObject(); if (m_iReason == CECHPWorkTrace.Trace_reason.TRACE_TALK) { //TODO: Visite other's booth, send hello message to him if ((!m_pHost.IsInBattle() || m_pHost.InSameBattleCamp(pNPC)) /*&& !g_pGame.GetGameRun().GetUIManager().GetInGameUIMan().GetDialog("Win_SkillAction").IsShow()*/) { UnityGameSession.c2s_CmdNPCSevHello(m_iObjectId); bActionDone = true; //a_LogOutput(1, "[NormalATK]- CECTracedNPC- OnTouched- TRACE_TALK"); } } else if (m_iReason == CECHPWorkTrace.Trace_reason.TRACE_ATTACK) { // 服务器的选中目标同步可能比追踪触碰晚到,导致这里永远不触发攻击。 // 为了保证追踪到目标后能正常攻击,这里在必要时主动选中目标。 // Server select-target ack can arrive later than the trace touch moment, which would // make this block forever. Ensure we select the traced target before attacking. // In duel: don't overwrite selection if player explicitly chose another target. bool duelPlayerChoseOther = m_pHost.IsInDuel() && m_pHost.GetDuelOpponentId() == m_iObjectId && m_pHost.m_idSelTarget != 0 && m_pHost.m_idSelTarget != m_iObjectId; if (m_iObjectId != m_pHost.m_idSelTarget) { UnityGameSession.c2s_CmdSelectTarget(m_iObjectId); if (!duelPlayerChoseOther) m_pHost.m_idSelTarget = m_iObjectId; } if (m_pHost.AttackableJudge(m_iObjectId, m_bForceAttack) == 1) { byte byPVPMask = EC_Utility.glb_BuildPVPMask(m_bForceAttack); UnityGameSession.c2s_CmdNormalAttack(byPVPMask); m_pHost.m_bPrepareFight = true; bActionDone = true; //a_LogOutput(1, "[NormalATK]- CECTracedNPC- OnTouched- TRACE_ATTACK"); } } else if (m_iReason == CECHPWorkTrace.Trace_reason.TRACE_SPELL) { //a_LogOutput(1, "[NormalATK]- CECTracedNPC- OnTouched- TRACE_SPELL"); if (!m_pHost.CannotAttack()) { if (m_pHost.CastSkill(m_iObjectId, m_bForceAttack)) bActionDone = true; } else m_pHost.m_pPrepSkill = null; } } return bActionDone; } public override CECTracedObject Clone() { return new CECTracedNPC(this); } public override bool IsTargetMissing() { if (base.IsTargetMissing()) { return true; } CECNPC pNPC = GetTargetObject() as CECNPC; if (pNPC.IsDead()) { return true; } return false; } } public class CECTracedPlayer : CECTracedObject { protected bool m_bForceAttack; public CECTracedPlayer(int type, int id, CECHostPlayer pHost, int ireason, bool bForceAttack = false) : base(type, id, pHost, ireason) { m_bForceAttack = bForceAttack; } public CECTracedPlayer(CECTracedPlayer rhs) : base(rhs) { m_bForceAttack = rhs.m_bForceAttack; } public override bool OnTargetMissing() { bool bRet = false; if (m_iReason == CECHPWorkTrace.Trace_reason.TRACE_ATTACK) { bRet = true; } else if (m_iReason == CECHPWorkTrace.Trace_reason.TRACE_SPELL) { CECSkill pSkill = m_pHost.m_pPrepSkill; if (pSkill == null || pSkill.GetTargetType() != 2) { bRet = true; m_pHost.m_pPrepSkill = null; } } return bRet; } public override A3DVECTOR3 GetTargetPos() { CECObject pObject = EC_ManMessageMono.Instance.GetObject(m_iObjectId, 0); if (m_iObjectId == m_pHost.GetCharacterID()) { return m_pHost.GetPos(); } else { // 同上:用实时位置避免 server pos 滞后导致追踪/触碰失败。 // Same rationale: use live position to avoid stale server pos breaking trace/touch. return (pObject as EC_ElsePlayer).GetPos(); } } public override bool OnTouched() { bool bActionDone = false; if (GPDataTypeHelper.ISPLAYERID(m_iObjectId)) { if (m_iObjectId == 0 || m_iObjectId == m_pHost.GetCharacterID()) { // Handle special case if (!m_pHost.CannotAttack()) { if (m_pHost.CastSkill(m_iObjectId, m_bForceAttack, null)) bActionDone = true; } else { m_pHost.m_pPrepSkill = null; } return bActionDone; } if (m_iReason == CECHPWorkTrace.Trace_reason.TRACE_ATTACK) { // 同 NPC:追踪触碰可能早于服务器选中目标同步,需主动选中以避免攻击不触发。 // Same as NPC: trace touch can happen before server selection ack; proactively select. // In duel: don't overwrite selection if player explicitly chose another target. bool duelPlayerChoseOther = m_pHost.IsInDuel() && m_pHost.GetDuelOpponentId() == m_iObjectId && m_pHost.m_idSelTarget != 0 && m_pHost.m_idSelTarget != m_iObjectId; if (m_iObjectId != m_pHost.m_idSelTarget) { UnityGameSession.c2s_CmdSelectTarget(m_iObjectId); if (!duelPlayerChoseOther) m_pHost.m_idSelTarget = m_iObjectId; } // Duel: always send PVP/force mask when target is duel opponent so server accepts the attack bool bUseForceAttack = m_bForceAttack || (m_pHost.IsInDuel() && m_iObjectId == m_pHost.GetDuelOpponentId()); if (m_pHost.AttackableJudge(m_iObjectId, bUseForceAttack) == 1) { byte byPVPMask = EC_Utility.glb_BuildPVPMask(bUseForceAttack); UnityGameSession.c2s_CmdNormalAttack(byPVPMask); m_pHost.m_bPrepareFight = true; bActionDone = true; //a_LogOutput(1, "[NormalATK]- CECTracedPlayer- OnTouched- TRACE_ATTACK"); } } else if (m_iReason == CECHPWorkTrace.Trace_reason.TRACE_SPELL) { //a_LogOutput(1, "[NormalATK]- CECTracedPlayer- OnTouched- TRACE_SPELL"); if (!m_pHost.CastSkill(m_iObjectId, m_bForceAttack, GetTargetObject())) { m_pHost.m_pPrepSkill = null; } else { bActionDone = true; } } else if (m_iReason == CECHPWorkTrace.Trace_reason.TRACE_TALK) { // Visite other's booth, send hello message to him //a_LogOutput(1, "[NormalATK]- CECTracedPlayer- OnTouched- TRACE_TALK"); UnityGameSession.c2s_CmdNPCSevHello(m_iObjectId); bActionDone = true; } } return bActionDone; } public override CECTracedObject Clone() { return new CECTracedPlayer(this); } public override bool IsTargetMissing() { if (base.IsTargetMissing()) { return true; } CECPlayer pPlayer = GetTargetObject() as CECPlayer; if (pPlayer.IsElsePlayer()) { if (pPlayer.IsDead()) { if (m_iReason == CECHPWorkTrace.Trace_reason.TRACE_SPELL) { CECSkill pSkill = m_pHost.m_pPrepSkill; if (pSkill != null && pSkill.GetTargetType() == 2) { return false; } } return true; } } return false; } }; public class CECTracedMatter : CECTracedObject { public CECTracedMatter(int type, int id, CECHostPlayer pHost, int ireason) : base(type, id, pHost, ireason) { } public CECTracedMatter(CECTracedMatter rhs) : base(rhs) { } public override bool OnTargetMissing() { return false; } public override A3DVECTOR3 GetTargetPos() { return GetTargetObject().GetPos(); } public override bool OnTouched() { bool bActionDone = false; if (GPDataTypeHelper.ISMATTERID(m_iObjectId)) { // if (m_pHost.GetProfession() == BrewMonster.RoleTypes.PROF_GHOST && m_pHost.IsInvisible()) // { // EC_Game.GetGameRun().AddFixedMessage(FIXMSG_CANNOT_USE_WHEN_INVISIBLE); // return bActionDone; // } CECMatter pMatter = (CECMatter)GetTargetObject(); if (m_iReason == CECHPWorkTrace.Trace_reason.TRACE_PICKUP) { // Check whether we have enougth place to hold this item or money int tid = pMatter.GetTemplateID(); bool isMoney = pMatter.IsMoney() || GPDataTypeHelper.ISMONEYTID(tid); // Money pickup should not be blocked by inventory space checks. // Let the server validate (e.g. money cap), and only gate items by bag space. if (isMoney || m_pHost.CanTakeItem(tid, 1)) { // Send pickup asking and wait response command UnityGameSession.RequestPickupItem(m_iObjectId, tid); bActionDone = true; } else { // Print a notify message //g_pGame.GetGameRun().AddFixedMessage(FIXMSG_PACKISFULL); Debug.Log("Khong du cho trong de nhat item"); } } else if (m_iReason == CECHPWorkTrace.Trace_reason.TRACE_GATHER) { // m_iReason == TRACE_GATHER int tidMatter = pMatter.GetTemplateID(); // Check mine level requirement if (m_pHost.GetBasicProps().iLevel < pMatter.GetLevelReq()) { //g_pGame.GetGameRun().AddFixedMessage(FIXMSG_LEVELTOOLOW); Debug.Log("Cap do cua ban khong du de khai thac tai nguyen nay"); return bActionDone; } // Check whether we have a mine tool int iPack = 0; int iIndex = 0; int idTool = 0; if (m_pHost.FindMineTool(tidMatter, ref iPack, ref iIndex, ref idTool)) { DATA_TYPE DataType = DATA_TYPE.DT_INVALID; MINE_ESSENCE pData = (MINE_ESSENCE)ElementDataManProvider.GetElementDataMan().get_data_ptr((uint)pMatter.GetTemplateID(), ID_SPACE.ID_SPACE_ESSENCE, ref DataType); if (DataType != DATA_TYPE.DT_MINE_ESSENCE) { //ASSERT(DataType == DT_MINE_ESSENCE); return bActionDone; } // if (m_pHost.GetCoolTime(GP_CT_PLAYER_GATHER)) // { // g_pGame.GetGameRun().AddFixedMessage(FIXMSG_CMD_INCOOLTIME); // } else { // Send gather asking and wait response command UnityGameSession.c2s_CmdGatherMaterial(m_iObjectId, iPack, iIndex, idTool, (int)pData.task_in); bActionDone = true; } } else { //g_pGame.GetGameRun().AddFixedMessage(FIXMSG_NEEDTOOL); } } } return bActionDone; } public override CECTracedObject Clone() { return new CECTracedMatter(this); } }; public class CECHPWorkTrace : CECHPWork { // Trace reason public static class Trace_reason { public const int TRACE_NONE = -1, TRACE_ATTACK = 0, // Normal attack TRACE_PICKUP = 1, // Pickup object TRACE_TALK = 2, // Go to talk TRACE_SPELL = 3, // Cast magic TRACE_GATHER = 4; // Gather object } // Constructor and Destructor public CECHPWorkTrace(CECHPWorkMan pWorkMan) : base(Host_work_ID.WORK_TRACEOBJECT, pWorkMan) { m_dwMask = Work_mask.MASK_TRACEOBJECT; m_dwTransMask = Work_mask.MASK_STAND | Work_mask.MASK_MOVETOPOS | Work_mask.MASK_FLYOFF | Work_mask.MASK_FREEFALL | Work_mask.MASK_FOLLOW | Work_mask.MASK_USEITEM; Reset(); } // Change trace target //void ChangeTarget(int idTarget, int iReason, bool bUseAutoPF=false); // �趨traceobject public void SetTraceTarget(CECTracedObject pTraceObj, bool bUseAutoPF = false) { ResetUseAutoPF(bUseAutoPF); if (pTraceObj == null) { return; // Invalid trace object / Invalid trace object } if (!pTraceObj.GetTargetObject() || pTraceObj.GetObjectID() == m_pHost.GetCharacterID()) { // This is special case ReplaceTarget(pTraceObj); return; } int idTarget = pTraceObj.GetObjectID(); if (!GPDataTypeHelper.ISPLAYERID(idTarget) && !GPDataTypeHelper.ISNPCID(idTarget) && !GPDataTypeHelper.ISMATTERID(idTarget)) { return; } CECObject pObject = pTraceObj.GetTargetObject(); if (!pObject) { //delete pTraceObj; return; } ReplaceTarget(pTraceObj); if (m_pTraceObject.GetTargetObject()) { A3DVECTOR3 vDirH = pTraceObj.GetTargetPos() - m_pHost.GetPos(); vDirH.y = 0.0f; vDirH.Normalize(); m_vCurDirH = !vDirH.IsZero() ? vDirH : m_vCurDirH = GPDataTypeHelper.g_vAxisZ; } } public CECTracedObject CreatTraceTarget(int iTraceObjId, int iReason, bool bForceAttack = false) { if (GPDataTypeHelper.ISPLAYERID(iTraceObjId)) { return new CECTracedPlayer(TraceObjectType.TRACE_PLAYER, iTraceObjId, m_pHost, iReason, bForceAttack); } else if (GPDataTypeHelper.ISNPCID(iTraceObjId)) { return new CECTracedNPC(TraceObjectType.TRACE_NPC, iTraceObjId, m_pHost, iReason, bForceAttack); } else if (GPDataTypeHelper.ISMATTERID(iTraceObjId)) { return new CECTracedMatter(TraceObjectType.TRACE_MATTER, iTraceObjId, m_pHost, iReason); } return null; } // Tick routine public override bool Tick(float dwDeltaTime) { base.Tick(dwDeltaTime); CheckPrepSkill(); UpdateResetUseAutoPF(); UpdateUseAutoPF(); // m_bFinished flag may be set both in OnFirstTick() and CheckPrepSkill(), // so check it here ! if (m_bFinished) { return true; } if (m_pTraceObject.IsTargetMissing()) { OnTargetMissing(); return true; } // 重要:不要用 m_bCheckTouch 在这里抑制 Touch 检测。 // 之前的移植里 m_bCheckTouch 会在 GroundMove 后被置为 false,导致下一帧永远不进入 OnTouchTarget。 // Important: do not suppress touch checks with m_bCheckTouch here. // In this port, m_bCheckTouch can be forced false after GroundMove, making OnTouchTarget never fire. if (IsGoodTimeToTouch()) { // 注意:这里不能加 vExtent.y(胶囊半高),否则会把主角位置抬高导致 3D 距离变大, // 进而 CanTouchTarget 永远不满足(OnTouchTarget 永远不会触发)。 // Note: Do NOT add vExtent.y (capsule half-height) here; touch uses 3D distance and this // artificially increases the distance, preventing OnTouchTarget() from ever triggering. if (m_pTraceObject.CanTouchFrom(m_pHost.GetPos())) { OnTouchTarget(); return true; } } m_bCheckTouch = true; if (!m_pHost.IsRooting()) { // Continue tracing object float fDeltaTime = dwDeltaTime /** 0.001f*/; if (m_pHost.m_iMoveEnv == CECPlayer.Move_environment.MOVEENV_GROUND) { // 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) { int iAction = m_pHost.GetMoveStandAction(true); m_pHost.PlayAction(iAction, false, 200, false); } Trace_Walk(fDeltaTime); } else // (m_pHost.m_iMoveEnv == CECPlayer::MOVEENV_AIR || m_pHost.m_iMoveEnv == CECPlayer::MOVEENV_WATER) { m_pHost.ResetJump(); // Play appropriate actions int iAction = m_pHost.GetMoveStandAction(true); m_pHost.PlayAction(iAction, false, 200, false); Trace_FlySwim(fDeltaTime); } m_bHaveMoved = true; } return true; } // Reset work public override void Reset() { base.Reset(); m_bHaveMoved = false; m_bMeetSlide = false; m_bCheckTouch = true; m_bReadyCancel = false; m_bMoreClose = false; //m_pPrepSkill = null; m_bForceAttack = false; m_bActionDone = false; ClearResetUseAutoPF(); m_bUseAutoPF = false; m_dwAutoPFNextCheckTime = 0; m_pTraceObject = null; } // Work is cancel public override void Cancel() { if (m_pHost.m_pPrepSkill != null && m_pTraceObject.GetTraceReason() == Trace_reason.TRACE_SPELL) m_pHost.m_pPrepSkill = null; ClearResetUseAutoPF(); if (GetUseAutoPF()) { SetUseAutoPF(false); } //TODO: m_pHost.StopModelMove(); uncomment and add logic //m_pHost.StopModelMove(); base.Cancel(); //AP_ActionEvent(m_bActionDone ? AP_EVENT_TRACEOK : AP_EVENT_MOVEFINISHED, m_pTraceObject.GetTraceReason()); } // 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; CECHPWorkTrace pSrc = (CECHPWorkTrace)pWork; m_bHaveMoved = pSrc.m_bHaveMoved; m_bMeetSlide = pSrc.m_bMeetSlide; m_bCheckTouch = pSrc.m_bCheckTouch; m_bReadyCancel = pSrc.m_bReadyCancel; m_bMoreClose = pSrc.m_bMoreClose; m_vCurDirH = pSrc.m_vCurDirH; //m_pPrepSkill = pSrc.m_pPrepSkill; m_bForceAttack = pSrc.m_bForceAttack; m_bActionDone = pSrc.m_bActionDone; m_bShouldResetUseAutoPF = pSrc.m_bShouldResetUseAutoPF; m_bUseAutoPFResetValue = pSrc.m_bUseAutoPFResetValue; m_bUseAutoPF = pSrc.m_bUseAutoPF; m_dwAutoPFNextCheckTime = pSrc.m_dwAutoPFNextCheckTime; //delete m_pTraceObject; m_pTraceObject = pSrc.m_pTraceObject.Clone(); return true; } // User press cancel button public void PressCancel() { m_bReadyCancel = true; if (m_pTraceObject.GetTraceReason() == Trace_reason.TRACE_SPELL) m_pHost.m_pPrepSkill = null; } // Set move close flag public void SetMoveCloseFlag(bool bMoveClose) { m_pTraceObject.SetMoveCloseFlag(bMoveClose); } // Set / Get force attack flag public void SetForceAttack(bool bTrue) { m_bForceAttack = bTrue; } public bool GetForceAttack() { return m_bForceAttack; } // Set / Get prepared skill public void SetPrepSkill(CECSkill pSkill) { m_pPrepSkill = pSkill; } public CECSkill GetPrepSkill() { /*return m_pPrepSkill;*/ return null; } // Get target ID public int GetTarget() { return m_pTraceObject.GetObjectID(); } // Get trace reason public int GetTraceReason() { return m_pTraceObject.GetTraceReason(); } // AutoPF public void SetUseAutoPF(bool bUse) { m_bUseAutoPF = bUse; //if (!m_bUseAutoPF && CECIntelligentRoute.Instance().IsUsageTrace()) //{ // CECIntelligentRoute.Instance().ResetSearch(); //} } public bool GetUseAutoPF() { return m_bUseAutoPF; } public bool IsAutoPF() { return false; } public void SetActionDone(bool bActionDone) { m_bActionDone = bActionDone; } public void OnTargetMissing() { StopMove(true); if ((m_pTraceObject.GetTraceType() == TraceObjectType.TRACE_NPC) || (m_pTraceObject.GetTraceType() == TraceObjectType.TRACE_PLAYER)) { m_pTraceObject.OnTargetMissing(); } else if (m_pTraceObject.GetTraceReason() == Trace_reason.TRACE_SPELL) { m_pHost.m_pPrepSkill = null; } } public void OnTouchTarget() { StopMove(true); m_bActionDone = m_pTraceObject.OnTouched(); } public bool CanTouch() { //CECSkill pPrepSkill = m_pHost.m_pPrepSkill; CheckPrepSkill(); bool result = m_pTraceObject.CanTouchFrom(m_pHost.GetPos()); //m_pHost.m_pPrepSkill = pPrepSkill; return result; } // Attributes protected bool m_bHaveMoved; // Have moved flag protected bool m_bMeetSlide; // true, meet slide protected bool m_bCheckTouch; // Check whether touch target in this frame protected A3DVECTOR3 m_vCurDirH; // Current move direction protected bool m_bReadyCancel; // true, ready to cancel protected bool m_bMoreClose; // Move close flag protected bool m_bForceAttack; // Force attack flag protected CECSkill m_pPrepSkill; // Skill prepared to be casted protected bool m_bActionDone; // Ŀ����Ϊ�ɹ����� protected bool m_bShouldResetUseAutoPF; protected bool m_bUseAutoPFResetValue; protected bool m_bUseAutoPF; // Use CECIntelligentRoute::Search protected uint m_dwAutoPFNextCheckTime; // �´μ��ʱ�� protected CECTracedObject m_pTraceObject; // ����traceĿ����� // Operations // On first tick protected override void OnFirstTick() { m_pHost.m_iMoveMode = (int)MoveMode.MOVE_MOVE; m_bHaveMoved = false; } // Trace on ground protected bool Trace_Walk(float fDeltaTime) { A3DVECTOR3 vCurPos = m_pHost.GetPos(); A3DVECTOR3 vTargetPos = GetCurMovingDest(); CDR_INFO cdr = m_pHost.m_CDRInfo; if (m_pHost.m_iMoveMode == (int)MoveMode.MOVE_SLIDE) { m_pHost.PlayAction((int)PLAYER_ACTION_TYPE.ACT_JUMP_LOOP, false, 200, false); // This will cause stop moming after we slide down. A3DVECTOR3 vDir = vTargetPos - vCurPos; vDir.y = 0; vDir.Normalize(); float fMaxSpeedV = 0f; m_bMeetSlide = m_pHost.m_MoveCtrl.MeetSlope(vDir, fMaxSpeedV); EC_Utility.a_ClampFloor(cdr.fYVel, -fMaxSpeedV); if (!vDir.IsZero()) m_vCurDirH = vDir; vCurPos = m_pHost.m_MoveCtrl.GroundMove(m_vCurDirH, m_pHost.GetGroundSpeed(), fDeltaTime); m_pHost.SetDirAndUp(EC_Utility.ToVector3(vCurPos - m_pHost.GetPos()), Vector3.up); if (m_pHost.m_MoveCtrl.MoveBlocked() >= 3) { m_pHost.m_MoveCtrl.SetSlideLock(true); m_pHost.m_MoveCtrl.SendStopMoveCmd(EC_Utility.ToVector3(vCurPos), m_pHost.GetGroundSpeed(), (int)GPMoveMode.GP_MOVE_SLIDE); m_bFinished = true; } else { m_pHost.SetPos(EC_Utility.ToVector3(vCurPos)); //if (GetUseAutoPF() && CECIntelligentRoute::Instance().IsMoveOn()) //{ // CECIntelligentRoute::Instance().OnPlayerPosChange(vCurPos); //} m_pHost.m_MoveCtrl.SendMoveCmd(vCurPos, 2, GPDataTypeHelper.g_vOrigin, (cdr.vAbsVelocity), (int)GPMoveMode.GP_MOVE_SLIDE); } } else if (!m_bMeetSlide) { 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; //RaycastHit lastGroundHit; //m_pHost.m_GndInfo.bOnGround = m_pHost.GroundCheck(out lastGroundHit); if (m_pHost.m_GndInfo.bOnGround) { if (m_bReadyCancel) { StopMove(true); return true; } // Ajust direction only when player on ground A3DVECTOR3 vDirH = vTargetPos - vCurPos; //Debug.DrawLine(EC_Utility.ToVector3(vCurPos), EC_Utility.ToVector3(vTargetPos), Color.blue, 10f); A3DVECTOR3 v = A3DFuncs.a3d_Normalize(vDirH); if (Math.Abs(v.y) > 0.9848f) { PressCancel(); return true; } vDirH.y = 0.0f; vDirH.Normalize(); if (!vDirH.IsZero()) m_vCurDirH = vDirH; } vCurPos = m_pHost.m_MoveCtrl.GroundMove(m_vCurDirH, m_pHost.GetGroundSpeed(), fDeltaTime, m_pHost.m_fVertSpeed); m_pHost.SetDirAndUp(EC_Utility.ToVector3(vCurPos - m_pHost.GetPos()), Vector3.up); m_pHost.SetPos(EC_Utility.ToVector3(vCurPos)); //if (GetUseAutoPF() && CECIntelligentRoute::Instance().IsMoveOn()) //{ // CECIntelligentRoute::Instance().OnPlayerPosChange(vCurPos); //} // 不要在这里关闭 touch 检测:Tick() 的 touch 检测发生在移动之前, // 如果这里把 m_bCheckTouch 置 false,会导致下一帧永远跳过触发逻辑。 // Do not disable touch checking here; touch checking happens before movement in Tick(). //if (!m_vCurDirH.IsZero()) //{ // m_pHost.StartModelMove(m_vCurDirH, g_vAxisY, 0); //} if (m_pHost.m_MoveCtrl.MoveBlocked() >= 3) { //m_pHost.m_MoveCtrl.SendStopMoveCmd(EC_Utility.ToVector3(vCurPos), m_pHost.GetGroundSpeed(), iMoveMode); PressCancel(); } else { m_pHost.m_MoveCtrl.SendMoveCmd(vCurPos, 0, vTargetPos,(cdr.vAbsVelocity), iMoveMode); } } else // m_bMeetSlide == true { if (m_bHaveMoved) { m_pHost.m_MoveCtrl.SendStopMoveCmd(EC_Utility.ToVector3(vCurPos), m_pHost.GetGroundSpeed(), (int)GPMoveMode.GP_MOVE_SLIDE); } m_bFinished = true; } return true; } // Trace in air and water public bool Trace_FlySwim(float fDeltaTime) { if (m_bMeetSlide || m_bReadyCancel) { StopMove(true); return true; } if (GetUseAutoPF()) { SetUseAutoPF(false); } A3DVECTOR3 vCurPos = m_pHost.GetPos(); A3DVECTOR3 vTargetPos = GetCurMovingDest(); float fSpeed1 = m_pHost.m_vVelocity.Magnitude(); A3DVECTOR3 vMoveDir = vTargetPos - vCurPos; float fDist = vMoveDir.Normalize(); int iMoveMode; float na = 0.0f, pa = 0.0f, fMaxSpeed; bool bInAir; if (m_pHost.m_iMoveEnv == CECPlayer.Move_environment.MOVEENV_AIR) { bInAir = true; na = CECHostMove.EC_NACCE_AIR; fMaxSpeed = m_pHost.GetFlySpeed(); iMoveMode = (int)GPMoveMode.GP_MOVE_AIR | (int)GPMoveMode.GP_MOVE_RUN; } else { bInAir = false; na = CECHostMove.EC_NACCE_WATER; fMaxSpeed = m_pHost.GetSwimSpeedSev(); iMoveMode = (int)GPMoveMode.GP_MOVE_WATER | (int)GPMoveMode.GP_MOVE_RUN; } ON_AIR_CDR_INFO cdr = m_pHost.m_AirCDRInfo; // 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 (Mathf.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); A3DVECTOR3 vMoveDirH = new A3DVECTOR3(vMoveDir.x, 0.0f, vMoveDir.z); if (!vMoveDirH.IsZero()) { //m_pHost.StartModelMove(vMoveDirH, g_vAxisY, 100); m_pHost.SetDestDirAndUp(vMoveDirH, EC_Utility.ToVector3(GPDataTypeHelper.g_vAxisY), 0.1f); } A3DVECTOR3 vVel2 = vMoveDir * fSpeed2; vCurPos = m_pHost.m_MoveCtrl.AirWaterMove(vMoveDir, fSpeed2, fDeltaTime, bInAir, true); m_pHost.SetPos(EC_Utility.ToVector3(vCurPos)); m_pHost.m_vVelocity = vVel2; if (Mathf.Abs(vMoveDir.y) > 0.9848f) { A3DVECTOR3 v1 = vMoveDir; A3DVECTOR3 v2 = vTargetPos - vCurPos; v1.y = v2.y = 0.0f; if (A3DVECTOR3.DotProduct(v1, v2) < 0.0f) { PressCancel(); return true; } } if (m_pHost.m_MoveCtrl.MoveBlocked() >= 3) { m_pHost.m_MoveCtrl.SendStopMoveCmd(EC_Utility.ToVector3(vCurPos), fMaxSpeed, iMoveMode); m_bFinished = true; } else m_pHost.m_MoveCtrl.SendMoveCmd(vCurPos, 1, vTargetPos, vVel2, iMoveMode); return true; } // Stop move when touch target public void StopMove(bool bFinish) { if (m_bHaveMoved || !m_pHost.m_MoveCtrl.IsStop()) { m_pHost.m_MoveCtrl.SendStopMoveCmd(); } m_pHost.m_vVelocity.Clear(); //m_pHost.StopModelMove(); m_pHost.PlayAction((int)PLAYER_ACTION_TYPE.ACT_STAND, true, 1, false); if (bFinish) { m_bFinished = true; } } // Handle the case that target died when host is tracing it public bool OnTargetDied(CECObject pTarget) { return true; } // Is valid time to touch target ? public bool IsGoodTimeToTouch() { if (m_pHost.IsJumping()) { return false; } return true; } // Check prepare skill public void CheckPrepSkill() { if (!m_bReadyCancel) { if (m_pTraceObject.GetTraceReason() == Trace_reason.TRACE_SPELL) { m_pHost.m_pPrepSkill = m_pPrepSkill; } else if (m_pHost.m_pPrepSkill != null) m_pHost.m_pPrepSkill = null; } } public bool GetTargetCurPos(A3DVECTOR3 pos) { return true; } public A3DVECTOR3 GetCurMovingDest() { // 对于采集/拾取物体:保持一定距离,避免角色模型顶住碰撞体抖动 // For gather/pickup matters: keep a stand-off distance to avoid jittering against colliders. if (m_pTraceObject != null && m_pTraceObject.GetTraceType() == TraceObjectType.TRACE_MATTER) { CECMatter matter = m_pTraceObject.GetTargetObject() as CECMatter; if (matter != null) { float keepDist = matter.GetGatherDist(); if (keepDist > 0.01f) { A3DVECTOR3 hostPos = m_pHost.GetPos(); A3DVECTOR3 targetPos = matter.GetPos(); A3DVECTOR3 deltaH = targetPos - hostPos; deltaH.y = 0.0f; float distH = deltaH.MagnitudeH(); if (distH > keepDist + 0.05f && distH > 1e-4f) { deltaH.Normalize(); A3DVECTOR3 dest = targetPos - deltaH * keepDist; // keep Y stable; GroundMove will resolve final Y anyway dest.y = hostPos.y; return dest; } } } } return m_pTraceObject.GetTargetPos(); } public void UpdateUseAutoPF() { } public void ReplaceTarget(CECTracedObject ptraceobj) { //if (m_pTraceObject) //{ // delete m_pTraceObject; //} m_pTraceObject = ptraceobj; } public void ResetUseAutoPF(bool bUseAutoPF) { m_bShouldResetUseAutoPF = true; m_bUseAutoPFResetValue = bUseAutoPF; } public void UpdateResetUseAutoPF() { if (!m_bShouldResetUseAutoPF) { return; } //# if SHOW_AUTOMOVE_FOOTPRINTS // g_AutoPFFollowPoints.clear(); // g_AutoPFPathPoints.clear(); //#endif //CECIntelligentRoute::Instance().SetUsage(CECIntelligentRoute::enumUsageWorkTrace); //CECIntelligentRoute::Instance().ResetSearch(); SetUseAutoPF(m_bUseAutoPFResetValue); ClearResetUseAutoPF(); } public void ClearResetUseAutoPF() { m_bShouldResetUseAutoPF = false; m_bUseAutoPFResetValue = false; } }; }