From eb8980943cbd852370604df3ad66d5bcca8e13a0 Mon Sep 17 00:00:00 2001 From: HungDK <> Date: Mon, 22 Dec 2025 18:06:20 +0700 Subject: [PATCH] Add trace to gather matter and gathering matter --- .../Scripts/Managers/EC_HPWorkTrace.cs | 257 ++++++++++++------ 1 file changed, 168 insertions(+), 89 deletions(-) diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_HPWorkTrace.cs b/Assets/PerfectWorld/Scripts/Managers/EC_HPWorkTrace.cs index 62652cfb66..6b1bf38cf0 100644 --- a/Assets/PerfectWorld/Scripts/Managers/EC_HPWorkTrace.cs +++ b/Assets/PerfectWorld/Scripts/Managers/EC_HPWorkTrace.cs @@ -5,6 +5,7 @@ using BrewMonster.Scripts; using CSNetwork.GPDataType; using System; using System.Runtime.ConstrainedExecution; +using PerfectWorld.Scripts; using Unity.VisualScripting; using UnityEngine; @@ -132,6 +133,10 @@ namespace BrewMonster 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)) @@ -142,7 +147,13 @@ namespace BrewMonster } else { - CECPlayer pPlayer = pObject.GetComponent(); + // 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); @@ -150,13 +161,25 @@ namespace BrewMonster 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; - //return pMatter.CalcDist(vHostPos, true) < pMatter.GetGatherDist(); + 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; } @@ -207,7 +230,11 @@ namespace BrewMonster public override A3DVECTOR3 GetTargetPos() { - return (GetTargetObject() as CECNPC).GetServerPos(); + // 使用 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() @@ -231,8 +258,16 @@ namespace BrewMonster } else if (m_iReason == CECHPWorkTrace.Trace_reason.TRACE_ATTACK) { - if (m_iObjectId == m_pHost.m_idSelTarget && - m_pHost.AttackableJudge(m_iObjectId, m_bForceAttack) == 1) + // 服务器的选中目标同步可能比追踪触碰晚到,导致这里永远不触发攻击。 + // 为了保证追踪到目标后能正常攻击,这里在必要时主动选中目标。 + // 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. + if (m_iObjectId != m_pHost.m_idSelTarget) + { + UnityGameSession.c2s_CmdSelectTarget(m_iObjectId); + 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); @@ -317,7 +352,9 @@ namespace BrewMonster } else { - return (pObject as EC_ElsePlayer).GetServerPos(); + // 同上:用实时位置避免 server pos 滞后导致追踪/触碰失败。 + // Same rationale: use live position to avoid stale server pos breaking trace/touch. + return (pObject as EC_ElsePlayer).GetPos(); } } @@ -342,8 +379,14 @@ namespace BrewMonster } if (m_iReason == CECHPWorkTrace.Trace_reason.TRACE_ATTACK) { - if (m_iObjectId == m_pHost.m_idSelTarget && - m_pHost.AttackableJudge(m_iObjectId, m_bForceAttack) == 1) + // 同 NPC:追踪触碰可能早于服务器选中目标同步,需主动选中以避免攻击不触发。 + // Same as NPC: trace touch can happen before server selection ack; proactively select. + if (m_iObjectId != m_pHost.m_idSelTarget) + { + UnityGameSession.c2s_CmdSelectTarget(m_iObjectId); + 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); @@ -431,70 +474,73 @@ namespace BrewMonster public override bool OnTouched() { bool bActionDone = false; - //if (GPDataTypeHelper.ISMATTERID(m_iObjectId)) - //{ - // if (m_pHost.GetProfession() == PROF_GHOST && m_pHost.IsInvisible()) - // { - // g_pGame.GetGameRun().AddFixedMessage(FIXMSG_CANNOT_USE_WHEN_INVISIBLE); - // return bActionDone; - // } - // CECMatter* pMatter = (CECMatter*)GetTargetObject(); + 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_PICKUP) - // { - // // Check whether we have enougth place to hold this item or money - // a_LogOutput(1, "[NormalATK]- CECTracedMatter- OnTouched- TRACE_PICKUP"); - // if (m_pHost.CanTakeItem(pMatter.GetTemplateID(), 1)) - // { - // // Send pickup asking and wait response command - // g_pGame.GetGameSession().c2s_CmdPickup(m_iObjectId, pMatter.GetTemplateID()); - // bActionDone = true; - // } - // else - // { - // // Print a notify message - // g_pGame.GetGameRun().AddFixedMessage(FIXMSG_PACKISFULL); - // } - // } - // else - // { // m_iReason == TRACE_GATHER - // int tidMatter = pMatter.GetTemplateID(); - // a_LogOutput(1, "[NormalATK]- CECTracedMatter- OnTouched- TRACE_GATHER"); - // // Check mine level requirement - // if (m_pHost.GetBasicProps().iLevel < pMatter.GetLevelReq()) - // { - // g_pGame.GetGameRun().AddFixedMessage(FIXMSG_LEVELTOOLOW); - // return bActionDone; - // } + if (m_iReason == CECHPWorkTrace.Trace_reason.TRACE_PICKUP) + { + // Check whether we have enougth place to hold this item or money + if (m_pHost.CanTakeItem(pMatter.GetTemplateID(), 1)) + { + // Send pickup asking and wait response command + UnityGameSession.RequestPickupItem(m_iObjectId, pMatter.GetTemplateID()); + 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, iIndex, idTool; - // if (m_pHost.FindMineTool(tidMatter, &iPack, &iIndex, &idTool)) - // { - // DATA_TYPE DataType; - // const MINE_ESSENCE* pData = (const MINE_ESSENCE*)g_pGame.GetElementDataMan().get_data_ptr(pMatter.GetTemplateID(), ID_SPACE_ESSENCE, DataType); - // if (DataType != DT_MINE_ESSENCE) - // { - // ASSERT(DataType == DT_MINE_ESSENCE); - // 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 - // g_pGame.GetGameSession().c2s_CmdGatherMaterial(m_iObjectId, iPack, iIndex, idTool, pData.task_in); - // } - // } - // else - // { - // g_pGame.GetGameRun().AddFixedMessage(FIXMSG_NEEDTOOL); - // } - // } - //} + // 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; } @@ -531,6 +577,10 @@ namespace BrewMonster 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 @@ -568,10 +618,10 @@ namespace BrewMonster { 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); - //} + else if (GPDataTypeHelper.ISMATTERID(iTraceObjId)) + { + return new CECTracedMatter(TraceObjectType.TRACE_MATTER, iTraceObjId, m_pHost, iReason); + } return null; } @@ -596,17 +646,20 @@ namespace BrewMonster return true; } - if (m_bCheckTouch) + // 重要:不要用 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()) { - 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; - if (m_pTraceObject.CanTouchFrom(m_pHost.GetPos() + new A3DVECTOR3(0f, m_pHost.m_CDRInfo.vExtent.y, 0f))) - { - OnTouchTarget(); - return true; - } + OnTouchTarget(); + return true; } } m_bCheckTouch = true; @@ -797,7 +850,7 @@ namespace BrewMonster // Operations // On first tick - protected virtual void OnFirstTick() + protected override void OnFirstTick() { m_pHost.m_iMoveMode = (int)MoveMode.MOVE_MOVE; m_bHaveMoved = false; @@ -851,7 +904,7 @@ namespace BrewMonster else if (!m_pHost.m_GndInfo.bOnGround) iMoveMode = (int)GPMoveMode.GP_MOVE_FALL; - RaycastHit lastGroundHit; + //RaycastHit lastGroundHit; //m_pHost.m_GndInfo.bOnGround = m_pHost.GroundCheck(out lastGroundHit); if (m_pHost.m_GndInfo.bOnGround) { @@ -886,10 +939,9 @@ namespace BrewMonster // CECIntelligentRoute::Instance().OnPlayerPosChange(vCurPos); //} - if (cdr.vTPNormal.IsZero()) - { - m_bCheckTouch = false; - } + // 不要在这里关闭 touch 检测:Tick() 的 touch 检测发生在移动之前, + // 如果这里把 m_bCheckTouch 置 false,会导致下一帧永远跳过触发逻辑。 + // Do not disable touch checking here; touch checking happens before movement in Tick(). //if (!m_vCurDirH.IsZero()) //{ @@ -972,6 +1024,33 @@ namespace BrewMonster } 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()