Files
test/Assets/PerfectWorld/Scripts/Managers/EC_HPWorkTrace.cs
T

1190 lines
44 KiB
C#

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<CECHostPlayer>() 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;
}
};
}