diff --git a/3dtank.com/play/static/js/unpacked/hornet_bundle.js b/3dtank.com/play/static/js/unpacked/hornet_bundle.js index 54ae4d7..a923702 100644 --- a/3dtank.com/play/static/js/unpacked/hornet_bundle.js +++ b/3dtank.com/play/static/js/unpacked/hornet_bundle.js @@ -6587,6 +6587,7 @@ ignoreFence: false, skippedTank: 0, skippedFence: 0, + byType: {}, // cat/type → 被忽略次数(诊断) patched: false }; @@ -6672,13 +6673,41 @@ return !IsTankShape(Shape); } - // 「栅栏类」= 静态几何里除地面/缓坡以外的部分: - // · 三角形:法线接近水平(墙/栅栏/悬崖立面)→ 忽略;法线朝上(地面/缓坡)→ 保留 - // · 非三角形静态碰撞代理(盒/柱等):地图里这类基本都不是可站立地面 → 忽略 + var FPhysicsPosition = Fld('Physics.Position', 'bzr_1'); // 本地坦克世界位置 + + // 「栅栏类」= 静态几何中「明显高于车体中心」的那部分: + // · 三角形:|法线 z| < 0.6(立面)且最高点高于车体中心 1m → 墙/栅栏 + // · 非三角形(盒/柱等代理):中心高于车体中心 1m → 障碍 + // · 地面/缓坡(最高点不高于车体中心)→ 一律保留,避免掉出地图 + function LocalTankZ() { + var Phys = GetLocalPhysics(); + var P = Phys && Phys[FPhysicsPosition]; + return (P && typeof P.h20_1 === 'number') ? P.h20_1 : null; + } + + function ShapeMaxZ(Shape) { + if (IsTriangle(Shape)) { + var A = Shape[FV0], B = Shape[FV1], C = Shape[FV2]; + if (!A || !B || !C) return null; + var Max = Math.max(A.h20_1, B.h20_1, C.h20_1); + return isFinite(Max) ? Max : null; + } + var Body = Shape[FShapeBody]; + var State = Body && Body[FBodyState]; + var P = State && State[FBodyPosVec]; + return (P && typeof P.h20_1 === 'number') ? P.h20_1 : null; + } + function IsFenceLike(Shape) { if (!IsStaticShape(Shape)) return false; - if (!IsTriangle(Shape)) return true; - return IsSteepTriangle(Shape); + var TankZ = LocalTankZ(); + if (TankZ === null) return false; + var MaxZ = ShapeMaxZ(Shape); + if (MaxZ === null) return false; + if (MaxZ < TankZ + 100) return false; // 不高于车体中心 1m → 地面/缓坡/低矮物 + if (!IsTriangle(Shape)) return true; // 高于车体中心的盒/柱代理 → 障碍 + var Nz = TriangleNormalZ(Shape); + return Nz !== null && Math.abs(Nz) < 0.6; // 立面才算墙/栅栏 } // ===== 诊断:识别「正在和什么物体重叠」===== @@ -6799,9 +6828,13 @@ // ② 忽略栅栏(静态障碍;地面/缓坡保留) if (Stat.ignoreFence && Local) { - if ((TankA && OwnerOf(ShapeA) === Local && IsFenceLike(ShapeB)) || - (TankB && OwnerOf(ShapeB) === Local && IsFenceLike(ShapeA))) { + var FenceShape = null; + if (TankA && OwnerOf(ShapeA) === Local) FenceShape = ShapeB; + else if (TankB && OwnerOf(ShapeB) === Local) FenceShape = ShapeA; + if (FenceShape && IsFenceLike(FenceShape)) { Stat.skippedFence++; + var Key = FenceShape[FShapeCategory] + '/' + FenceShape[FShapeType]; + Stat.byType[Key] = (Stat.byType[Key] || 0) + 1; return true; } // 诊断:本车与任意非坦克形状配对但没跳过 → 记录样本(含被挡住的栅栏) @@ -6931,7 +6964,8 @@ GetStat: function () { return { running: Stat.running, mode: Stat.mode, ignoreFence: Stat.ignoreFence, - skippedTank: Stat.skippedTank, skippedFence: Stat.skippedFence, patched: Stat.patched + skippedTank: Stat.skippedTank, skippedFence: Stat.skippedFence, + byType: Stat.byType, patched: Stat.patched }; } }; diff --git a/3dtank_bundle.js b/3dtank_bundle.js index 54ae4d7..a923702 100644 --- a/3dtank_bundle.js +++ b/3dtank_bundle.js @@ -6587,6 +6587,7 @@ ignoreFence: false, skippedTank: 0, skippedFence: 0, + byType: {}, // cat/type → 被忽略次数(诊断) patched: false }; @@ -6672,13 +6673,41 @@ return !IsTankShape(Shape); } - // 「栅栏类」= 静态几何里除地面/缓坡以外的部分: - // · 三角形:法线接近水平(墙/栅栏/悬崖立面)→ 忽略;法线朝上(地面/缓坡)→ 保留 - // · 非三角形静态碰撞代理(盒/柱等):地图里这类基本都不是可站立地面 → 忽略 + var FPhysicsPosition = Fld('Physics.Position', 'bzr_1'); // 本地坦克世界位置 + + // 「栅栏类」= 静态几何中「明显高于车体中心」的那部分: + // · 三角形:|法线 z| < 0.6(立面)且最高点高于车体中心 1m → 墙/栅栏 + // · 非三角形(盒/柱等代理):中心高于车体中心 1m → 障碍 + // · 地面/缓坡(最高点不高于车体中心)→ 一律保留,避免掉出地图 + function LocalTankZ() { + var Phys = GetLocalPhysics(); + var P = Phys && Phys[FPhysicsPosition]; + return (P && typeof P.h20_1 === 'number') ? P.h20_1 : null; + } + + function ShapeMaxZ(Shape) { + if (IsTriangle(Shape)) { + var A = Shape[FV0], B = Shape[FV1], C = Shape[FV2]; + if (!A || !B || !C) return null; + var Max = Math.max(A.h20_1, B.h20_1, C.h20_1); + return isFinite(Max) ? Max : null; + } + var Body = Shape[FShapeBody]; + var State = Body && Body[FBodyState]; + var P = State && State[FBodyPosVec]; + return (P && typeof P.h20_1 === 'number') ? P.h20_1 : null; + } + function IsFenceLike(Shape) { if (!IsStaticShape(Shape)) return false; - if (!IsTriangle(Shape)) return true; - return IsSteepTriangle(Shape); + var TankZ = LocalTankZ(); + if (TankZ === null) return false; + var MaxZ = ShapeMaxZ(Shape); + if (MaxZ === null) return false; + if (MaxZ < TankZ + 100) return false; // 不高于车体中心 1m → 地面/缓坡/低矮物 + if (!IsTriangle(Shape)) return true; // 高于车体中心的盒/柱代理 → 障碍 + var Nz = TriangleNormalZ(Shape); + return Nz !== null && Math.abs(Nz) < 0.6; // 立面才算墙/栅栏 } // ===== 诊断:识别「正在和什么物体重叠」===== @@ -6799,9 +6828,13 @@ // ② 忽略栅栏(静态障碍;地面/缓坡保留) if (Stat.ignoreFence && Local) { - if ((TankA && OwnerOf(ShapeA) === Local && IsFenceLike(ShapeB)) || - (TankB && OwnerOf(ShapeB) === Local && IsFenceLike(ShapeA))) { + var FenceShape = null; + if (TankA && OwnerOf(ShapeA) === Local) FenceShape = ShapeB; + else if (TankB && OwnerOf(ShapeB) === Local) FenceShape = ShapeA; + if (FenceShape && IsFenceLike(FenceShape)) { Stat.skippedFence++; + var Key = FenceShape[FShapeCategory] + '/' + FenceShape[FShapeType]; + Stat.byType[Key] = (Stat.byType[Key] || 0) + 1; return true; } // 诊断:本车与任意非坦克形状配对但没跳过 → 记录样本(含被挡住的栅栏) @@ -6931,7 +6964,8 @@ GetStat: function () { return { running: Stat.running, mode: Stat.mode, ignoreFence: Stat.ignoreFence, - skippedTank: Stat.skippedTank, skippedFence: Stat.skippedFence, patched: Stat.patched + skippedTank: Stat.skippedTank, skippedFence: Stat.skippedFence, + byType: Stat.byType, patched: Stat.patched }; } }; diff --git a/tools/hornet_noclip.js b/tools/hornet_noclip.js index 529f876..39f36ae 100644 --- a/tools/hornet_noclip.js +++ b/tools/hornet_noclip.js @@ -63,6 +63,7 @@ ignoreFence: false, skippedTank: 0, skippedFence: 0, + byType: {}, // cat/type → 被忽略次数(诊断) patched: false }; @@ -148,13 +149,41 @@ return !IsTankShape(Shape); } - // 「栅栏类」= 静态几何里除地面/缓坡以外的部分: - // · 三角形:法线接近水平(墙/栅栏/悬崖立面)→ 忽略;法线朝上(地面/缓坡)→ 保留 - // · 非三角形静态碰撞代理(盒/柱等):地图里这类基本都不是可站立地面 → 忽略 + var FPhysicsPosition = Fld('Physics.Position', 'bzr_1'); // 本地坦克世界位置 + + // 「栅栏类」= 静态几何中「明显高于车体中心」的那部分: + // · 三角形:|法线 z| < 0.6(立面)且最高点高于车体中心 1m → 墙/栅栏 + // · 非三角形(盒/柱等代理):中心高于车体中心 1m → 障碍 + // · 地面/缓坡(最高点不高于车体中心)→ 一律保留,避免掉出地图 + function LocalTankZ() { + var Phys = GetLocalPhysics(); + var P = Phys && Phys[FPhysicsPosition]; + return (P && typeof P.h20_1 === 'number') ? P.h20_1 : null; + } + + function ShapeMaxZ(Shape) { + if (IsTriangle(Shape)) { + var A = Shape[FV0], B = Shape[FV1], C = Shape[FV2]; + if (!A || !B || !C) return null; + var Max = Math.max(A.h20_1, B.h20_1, C.h20_1); + return isFinite(Max) ? Max : null; + } + var Body = Shape[FShapeBody]; + var State = Body && Body[FBodyState]; + var P = State && State[FBodyPosVec]; + return (P && typeof P.h20_1 === 'number') ? P.h20_1 : null; + } + function IsFenceLike(Shape) { if (!IsStaticShape(Shape)) return false; - if (!IsTriangle(Shape)) return true; - return IsSteepTriangle(Shape); + var TankZ = LocalTankZ(); + if (TankZ === null) return false; + var MaxZ = ShapeMaxZ(Shape); + if (MaxZ === null) return false; + if (MaxZ < TankZ + 100) return false; // 不高于车体中心 1m → 地面/缓坡/低矮物 + if (!IsTriangle(Shape)) return true; // 高于车体中心的盒/柱代理 → 障碍 + var Nz = TriangleNormalZ(Shape); + return Nz !== null && Math.abs(Nz) < 0.6; // 立面才算墙/栅栏 } // ===== 诊断:识别「正在和什么物体重叠」===== @@ -275,9 +304,13 @@ // ② 忽略栅栏(静态障碍;地面/缓坡保留) if (Stat.ignoreFence && Local) { - if ((TankA && OwnerOf(ShapeA) === Local && IsFenceLike(ShapeB)) || - (TankB && OwnerOf(ShapeB) === Local && IsFenceLike(ShapeA))) { + var FenceShape = null; + if (TankA && OwnerOf(ShapeA) === Local) FenceShape = ShapeB; + else if (TankB && OwnerOf(ShapeB) === Local) FenceShape = ShapeA; + if (FenceShape && IsFenceLike(FenceShape)) { Stat.skippedFence++; + var Key = FenceShape[FShapeCategory] + '/' + FenceShape[FShapeType]; + Stat.byType[Key] = (Stat.byType[Key] || 0) + 1; return true; } // 诊断:本车与任意非坦克形状配对但没跳过 → 记录样本(含被挡住的栅栏) @@ -407,7 +440,8 @@ GetStat: function () { return { running: Stat.running, mode: Stat.mode, ignoreFence: Stat.ignoreFence, - skippedTank: Stat.skippedTank, skippedFence: Stat.skippedFence, patched: Stat.patched + skippedTank: Stat.skippedTank, skippedFence: Stat.skippedFence, + byType: Stat.byType, patched: Stat.patched }; } };