Fix unstable router, water and air route
This commit is contained in:
@@ -70,6 +70,15 @@ namespace BrewMonster.Scripts
|
||||
|
||||
protected bool m_bResetAutoPF;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
const float AutoPF_WorldDestContinueDistH = 4.0f;
|
||||
|
||||
/// <summary>Horizontal radius to treat DEST_2D as arrived (swim/fly overshoot logic often never fires).</summary>
|
||||
const float Dest2D_ArrivalDistH = 2.5f;
|
||||
|
||||
public CECHPWorkMove(CECHPWorkMan pWorkMan) : base(Host_work_ID.WORK_MOVETOPOS, pWorkMan)
|
||||
{
|
||||
m_dwMask = Work_mask.MASK_MOVETOPOS;
|
||||
@@ -111,15 +120,19 @@ namespace BrewMonster.Scripts
|
||||
}
|
||||
else if (iDestType == Types.DEST_2D || iDestType == Types.DEST_3D)
|
||||
{
|
||||
m_vCurDir = vMoveDest - new A3DVECTOR3(m_pHost.transform.position.x, m_pHost.transform.position.y, m_pHost.transform.position.z);
|
||||
m_vCurDir = vMoveDest - m_pHost.GetPos();
|
||||
m_vCurDir.y = 0.0f;
|
||||
m_vCurDir.Normalize();
|
||||
if (m_vCurDir.Normalize() > 1e-4f)
|
||||
OrientHostHorizontal(m_vCurDir);
|
||||
}
|
||||
else if (IsAutoPF())
|
||||
{
|
||||
m_vCurDir = CECIntelligentRoute.Instance().GetCurDest() - m_pHost.GetPos();
|
||||
// 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;
|
||||
m_vCurDir.Normalize();
|
||||
if (m_vCurDir.Normalize() > 1e-4f)
|
||||
OrientHostHorizontal(m_vCurDir);
|
||||
if (m_bUseAutoMoveDialog)
|
||||
{
|
||||
// �˴����� m_bUseAutoMoveDialog���� SetUseAutoMoveDialog ��˵��
|
||||
@@ -132,6 +145,13 @@ namespace BrewMonster.Scripts
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
//{
|
||||
@@ -161,18 +181,12 @@ namespace BrewMonster.Scripts
|
||||
{
|
||||
if (global::BrewMonster.Scripts.CECIntelligentRoute.Instance().IsIdle())
|
||||
{
|
||||
// C++: AutoPF not ready yet, wait next tick.
|
||||
// C++:自动寻路未就绪,等待下一帧。
|
||||
return true;
|
||||
}
|
||||
if (m_pHost.IsFlying())
|
||||
{
|
||||
// C++: if flying, reset and switch back to DEST_2D.
|
||||
// C++:飞行状态下重置并切回 DEST_2D。
|
||||
global::BrewMonster.Scripts.CECIntelligentRoute.Instance().ResetSearch();
|
||||
m_bSwitchTo2D = true;
|
||||
// C++: AutoPF not ready yet, wait next tick for SwitchTo2D (EC_HPWorkMove.cpp).
|
||||
// C++:智能寻路模式未成功时,等待下个 Tick 切换到 DEST_2D 模式。
|
||||
return true;
|
||||
}
|
||||
// Unity extension: do not clear AutoPF when flying — Tick_FlySwim follows waypoints in air/water.
|
||||
// 原版 C++ 此处会 Reset+DEST_2D;空中/水中目标需要保留路径并在 Tick_FlySwim 沿路径移动。
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -411,12 +425,24 @@ namespace BrewMonster.Scripts
|
||||
|
||||
public void SetUseAutoMoveDialog(bool bUseAutoMoveDialog)
|
||||
{
|
||||
|
||||
m_bUseAutoMoveDialog = bUseAutoMoveDialog;
|
||||
}
|
||||
|
||||
public bool GetUseAutoMoveDialog() { return m_bUseAutoMoveDialog; }
|
||||
|
||||
/// <summary>Matches C++ CECHPWorkMove::GetAutoMove — must not always return true or input stays "auto" forever.</summary>
|
||||
public bool GetAutoMove()
|
||||
{
|
||||
return true;
|
||||
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; }
|
||||
@@ -475,6 +501,73 @@ namespace BrewMonster.Scripts
|
||||
SetTaskNPCInfo(tid, taskid);
|
||||
SetUseAutoMoveDialog(true);
|
||||
}
|
||||
|
||||
#if ENABLE_CEC_INTELLIGENT_ROUTE
|
||||
/// <returns>True if switched to direct 2D leg — caller must not Finish() this frame.</returns>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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
|
||||
/// <summary>After Search(), face the first path node so swim/air/walk doesn't use previous heading.</summary>
|
||||
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()
|
||||
{
|
||||
@@ -578,6 +671,9 @@ namespace BrewMonster.Scripts
|
||||
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)
|
||||
{
|
||||
@@ -786,18 +882,15 @@ namespace BrewMonster.Scripts
|
||||
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
|
||||
{
|
||||
// NOTE: Use Vector3 overload to avoid signature mismatch across ports.
|
||||
// 注意:使用 Vector3 重载以避免移植过程中签名不匹配。
|
||||
m_pHost.m_MoveCtrl.SendMoveCmd(
|
||||
EC_Utility.ToVector3(vCurPos),
|
||||
EC_Utility.ToVector3(cdr.vAbsVelocity),
|
||||
iMoveMode,
|
||||
false);
|
||||
// C++: SendMoveCmd(vCurPos, 1, vCurDest, cdr.vAbsVelocity, iMoveMode)
|
||||
m_pHost.m_MoveCtrl.SendMoveCmd(vCurPos, 1, vCurDest, cdr.vAbsVelocity, iMoveMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -938,6 +1031,9 @@ namespace BrewMonster.Scripts
|
||||
}
|
||||
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;
|
||||
@@ -1227,6 +1323,110 @@ namespace BrewMonster.Scripts
|
||||
}
|
||||
else if (IsAutoPF())
|
||||
{
|
||||
#if ENABLE_CEC_INTELLIGENT_ROUTE
|
||||
var route = global::BrewMonster.Scripts.CECIntelligentRoute.Instance();
|
||||
// Unity: ground AutoPF is in Tick_Walk; swim/fly use Tick_FlySwim. C++ resets here and forces DEST_2D,
|
||||
// which breaks routes when the path or target is in water/air — follow path nodes like DEST_2D swim/fly.
|
||||
bool envAirOrWater = m_pHost.m_iMoveEnv == (int)MoveEnvironment.MOVEENV_WATER
|
||||
|| m_pHost.m_iMoveEnv == (int)MoveEnvironment.MOVEENV_AIR;
|
||||
if (envAirOrWater && route.IsUsageMove())
|
||||
{
|
||||
if (route.IsPathFinished())
|
||||
{
|
||||
if (TryContinueAutoPFToWorldDestDirect())
|
||||
return true;
|
||||
Finish();
|
||||
m_pHost.m_MoveCtrl.SendStopMoveCmd(EC_Utility.ToVector3(vCurPos), fMaxSpeed,
|
||||
iMoveMode | (int)GPMoveMode.GP_MOVE_RUN);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!route.IsMoveOn())
|
||||
{
|
||||
route.ResetSearch();
|
||||
m_bSwitchTo2D = 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 vCurDest = route.GetCurDest();
|
||||
A3DVECTOR3 vMoveDirH = vCurDest - vCurPos;
|
||||
vMoveDirH.y = 0.0f;
|
||||
float fDistH = vMoveDirH.Normalize();
|
||||
|
||||
if (fDistH < 1e-4f)
|
||||
{
|
||||
route.OnPlayerPosChange(vCurPos);
|
||||
if (route.IsPathFinished())
|
||||
{
|
||||
if (TryContinueAutoPFToWorldDestDirect())
|
||||
return true;
|
||||
Finish();
|
||||
m_pHost.m_MoveCtrl.SendStopMoveCmd(EC_Utility.ToVector3(vCurPos), fMaxSpeed,
|
||||
iMoveMode | (int)GPMoveMode.GP_MOVE_RUN);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
float pa = 0.0f;
|
||||
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;
|
||||
else if (fSpeed2H > fMaxSpeed)
|
||||
fSpeed2H = fMaxSpeed;
|
||||
|
||||
Glide(fDistH / Mathf.Max(fMaxSpeed, 0.01f), vMoveDirH, fDeltaTime, bInAir);
|
||||
|
||||
vMoveDirH = m_pHost.GetModelMoveDir();
|
||||
vMoveDirH.y = 0;
|
||||
vMoveDirH.Normalize();
|
||||
|
||||
float fSpeed2V = CalcFlySwimVertSpeed(fSpeed1V, vPushDir.y, CECHostMove.EC_PUSH_ACCE, fDeltaTime);
|
||||
A3DVECTOR3 vVel2 = vMoveDirH * fSpeed2H + GPDataTypeHelper.g_vAxisY * fSpeed2V;
|
||||
|
||||
vCurPos = m_pHost.m_MoveCtrl.AirWaterMove(vVel2, fDeltaTime, bInAir);
|
||||
|
||||
if (m_pHost.m_MoveCtrl.MoveBlocked() >= 3)
|
||||
{
|
||||
vVel2.Clear();
|
||||
Finish();
|
||||
m_pHost.m_MoveCtrl.SendStopMoveCmd(EC_Utility.ToVector3(vCurPos), fMaxSpeed,
|
||||
iMoveMode | (int)GPMoveMode.GP_MOVE_RUN);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pHost.SetPos(EC_Utility.ToVector3(vCurPos));
|
||||
m_pHost.m_vVelocity = vVel2;
|
||||
route.OnPlayerPosChange(vCurPos);
|
||||
if (route.IsPathFinished())
|
||||
{
|
||||
if (TryContinueAutoPFToWorldDestDirect())
|
||||
return true;
|
||||
Finish();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
CECIntelligentRoute.Instance().ResetSearch();
|
||||
m_bSwitchTo2D = true;
|
||||
}
|
||||
@@ -1308,22 +1508,8 @@ namespace BrewMonster.Scripts
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (m_pHost.IsFlying())
|
||||
{
|
||||
if (CECUIManager.Instance != null)
|
||||
{
|
||||
// string message = $"Please deactive the fly mode to start the path finding.";
|
||||
|
||||
// CECUIManager.Instance.ShowMessageBox(
|
||||
// "Fly Mode", // 飞行模式
|
||||
// message, // 消息
|
||||
// BrewMonster.MessageBoxType.YesButton
|
||||
// );
|
||||
// Finish();
|
||||
//return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// C++ skips Search while flying (forces DEST_2D). Unity: allow Search so aerial targets get a 2D path.
|
||||
// 原版飞行时不做 Search;此处允许寻路,使空中起点/空中目标也能得到 XZ 路径。
|
||||
|
||||
// Brush test is not fully ported yet; pass null for now (static movemap only).
|
||||
// BrushTest 暂未完整移植;当前先传 null(仅静态 movemap)。
|
||||
@@ -1334,34 +1520,13 @@ namespace BrewMonster.Scripts
|
||||
|
||||
if (ret == CECIntelligentRoute.SearchResult.enumSearchNoPath)
|
||||
{
|
||||
// Calculate map coordinates from world coordinates
|
||||
// 从世界坐标计算地图坐标
|
||||
// Map coord formula: (world / 10) + offset (X: +400, Z: +550)
|
||||
// 地图坐标公式:(世界坐标 / 10) + 偏移量 (X: +400, Z: +550)
|
||||
// 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;
|
||||
|
||||
// Show popup notification to player that path cannot be found
|
||||
// 显示弹窗通知玩家无法找到路径
|
||||
if (CECUIManager.Instance != null)
|
||||
{
|
||||
// string message = $"Cannot find path to target position.\nPlease move manually to target location.\n\nMap Coordinates: ({mapX}, {mapZ}, ↑{mapY})";
|
||||
// string messageCN = $"无法找到到目标位置的路径。\n请手动移动到目标位置。\n\n地图坐标: ({mapX}, {mapZ}, ↑{mapY})";
|
||||
//
|
||||
// // CECUIManager.Instance.ShowMessageBox(
|
||||
// // "Path Not Found", // 路径未找到
|
||||
// // message, // English message with map coordinates
|
||||
// // BrewMonster.MessageBoxType.YesButton
|
||||
// // );
|
||||
// CECUIManager.Instance.ShowMessageBoxYes("Path Not Found", message, null, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"[CECIntelligentRoute] Cannot find path to target position. Map Coordinates: ({mapX}, {mapZ}, ↑{mapY}). Please move manually.");
|
||||
}
|
||||
|
||||
Finish();
|
||||
Debug.LogWarning(
|
||||
$"[CECIntelligentRoute] No path; switching to DEST_2D. Map coords: ({mapX}, {mapZ}, ↑{mapY}).");
|
||||
break;
|
||||
}
|
||||
else if (ret != CECIntelligentRoute.SearchResult.enumSearchSuccess)
|
||||
@@ -1369,6 +1534,7 @@ namespace BrewMonster.Scripts
|
||||
break;
|
||||
}
|
||||
bSwitchTo2D = false;
|
||||
OrientHostToFirstAutoPFWaypoint();
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user