feat(noclip): 新增穿坦克工具并接入面板开关

清掉本地坦克碰撞形状 mask 的坦克位(bit0),可穿过其他玩家坦克;地形静态几何(category bit4)碰撞保持不变。引擎状态切换会重写 mask,运行期每帧补一次,停止时按原始快照精确还原。
This commit is contained in:
TermiNexus committed 2026-10-20 20:06:24 +08:00
1 parent fcc4901a1f
commit 6e79057856
5 files changed
+555 -11

No files matched your search

@@ -5808,6 +5808,187 @@
})();
// ===== hornet_noclip.js =====
// hornet_noclip.js — 穿坦克(移动中忽略玩家障碍物,强行行走)
// 放在控制面板「穿坦克」开关里,只影响**本地坦克**,不改服务端/其他玩家。
//
// 碰撞过滤(module_55088 的 ki.q9q / ki.x9o,全物理世界唯一判定处):
// if (0 === (a.y9j_1 & b.z9j_1) || 0 === (b.y9j_1 & a.z9j_1)) return; // 0 → 不产生碰撞
// · y9j_1 = 形状的 category 位组("我是哪一类")
// · z9j_1 = 形状的 mask 位组("我愿意和哪几类碰")
// 实测位定义(module_87072):
// bit0(1) = 坦克(车体 hull-main 与各部件统一 y9j_1 = 1)
// bit4(16) = 地图静态几何(s6() 造地形:y9j_1 = 16, z9j_1 = 1055)
// 坦克 mask 恒含 16 → 清掉 bit0 后:撞墙照常,**只穿坦克**。
//
// 为什么每帧补一次:
// 引擎在坦克状态切换时会重写 mask —— tjt() 调 n155(11)(把 bit0 加回来),
// onClientTankState 的 2/5 分支调 n155(1)。所以运行期间每帧把 bit0 清掉。
//
// 停止:按进入时快照的原始 mask 精确还原(不猜引擎默认值)。
(function () {
'use strict';
var Lib = window.__hornetLib__;
if (!Lib) {
console.error('[穿坦克] 请先加载 hornet_lib.js');
return;
}
// 坦克 category 位(bit0)
var TANK_CATEGORY_BIT = 1;
var Stat = {
running: false,
shapes: 0, // 本帧处理的碰撞形状数
applied: 0 // 本次开启累计生效帧数
};
var CurrentPhysics = null; // 当前被处理的 tankPhysics 组件(ejt)
var MaskSnapshot = new Map(); // 形状 -> 进入时原始 mask
var LoggedPhysics = new WeakSet(); // 已打印过日志的组件
// 判定是否坦克物理组件(ejt / 组件名 tankPhysics)
// 特征:zzq_1 是车体主碰撞形状(x9j_1 === "hull-main"),且带 n155 / m155 / pbk
function IsTankPhysics(Comp) {
if (!Comp) return false;
if (typeof Comp.n155 !== 'function' || typeof Comp.m155 !== 'function') return false;
if (typeof Comp.pbk !== 'function') return false;
var Hull = Comp.zzq_1;
return !!(Hull && Hull.x9j_1 === 'hull-main');
}
// 在实体上找坦克物理组件(不走 FindComponentByField:zzq_1 字段名在其它类里也有复用)
function FindTankPhysics(Tank) {
if (!Tank || !Tank.qz1_1 || !Tank.qz1_1.s2a_1) return null;
var Found = null;
try {
Tank.qz1_1.s2a_1.forEach(function (ValueList) {
if (Found) return;
try {
var Comp = (ValueList && typeof ValueList.g1 === 'function') ? ValueList.g1(0) : ValueList;
if (IsTankPhysics(Comp)) Found = Comp;
} catch (e) {}
});
} catch (e) {}
return Found;
}
// 本地坦克:拥有中的坦克优先;观战/死亡时退回当前观察目标(此时不参与战斗,影响可忽略)
function GetLocalTank() {
var Gm = window.__gameMode__;
if (!Gm) return null;
try { if (Gm.u10b_1) return Gm.u10b_1; } catch (e) {}
try { if (Gm.t10b_1) return Gm.t10b_1; } catch (e) {}
return null;
}
// 还原快照里的原始 mask
function ReleaseSnapshot() {
MaskSnapshot.forEach(function (Mask, Shape) {
try {
if (Shape && typeof Mask === 'number') Shape.z9j_1 = Mask;
} catch (e) {}
});
MaskSnapshot.clear();
CurrentPhysics = null;
Stat.shapes = 0;
}
// 对本地坦克所有碰撞形状清掉「坦克位」
function ApplyMask() {
var Tank = GetLocalTank();
if (!Tank) { if (CurrentPhysics) ReleaseSnapshot(); return; }
var Phys = FindTankPhysics(Tank);
if (!Phys) { if (CurrentPhysics) ReleaseSnapshot(); return; }
// 换车/重生:物理组件被重建 → 先还原旧的,再接管新的
if (Phys !== CurrentPhysics) {
if (CurrentPhysics) ReleaseSnapshot();
CurrentPhysics = Phys;
}
var Body = null;
try { Body = Phys.pbk(); } catch (e) { Body = null; }
var Shapes = Body && Body.y9i_1;
if (!Shapes || typeof Shapes.e1 !== 'function' || typeof Shapes.g1 !== 'function') return;
var Count = Shapes.e1() | 0;
var Touched = 0;
for (var i = 0; i < Count; i++) {
var Shape = null;
try { Shape = Shapes.g1(i); } catch (e) { Shape = null; }
if (!Shape || typeof Shape.z9j_1 !== 'number') continue;
// 首次见到该形状时记录原始 mask,保证 Stop 能精确还原
if (!MaskSnapshot.has(Shape)) MaskSnapshot.set(Shape, Shape.z9j_1);
Shape.z9j_1 = Shape.z9j_1 & ~TANK_CATEGORY_BIT;
Touched++;
}
Stat.shapes = Touched;
if (Touched > 0) {
Stat.applied++;
if (!LoggedPhysics.has(Phys)) {
LoggedPhysics.add(Phys);
// 原始 mask 一并打印:若这里没有任何 mask 含 bit0,说明坦克碰撞位变了,需要重新核对
var Masks = [];
MaskSnapshot.forEach(function (Mask) { Masks.push(Mask); });
var HasTankBit = false;
for (var k = 0; k < Masks.length; k++) { if (Masks[k] & TANK_CATEGORY_BIT) { HasTankBit = true; break; } }
console.log('[穿坦克] 已生效:本地坦克 ' + Touched + ' 块碰撞形状,原始 mask=[' + Masks.join(',') + '] ' +
(HasTankBit ? '(含坦克位 bit0,已清除)' : '⚠ 未发现坦克位 bit0,可能版本字段有变'));
}
}
}
function Loop() {
if (!Stat.running) return;
requestAnimationFrame(Loop);
try { ApplyMask(); } catch (e) {
// 引擎中途换场景/重建物理时可能抛错,忽略即可(下一帧会重新尝试)
}
}
function _start() {
if (Stat.running) return;
Stat.running = true;
Stat.applied = 0;
ApplyMask();
requestAnimationFrame(Loop);
console.log('[穿坦克] 已启动(只穿坦克,地形碰撞保留)');
}
function _stop() {
if (!Stat.running) return;
Stat.running = false;
ReleaseSnapshot();
console.log('[穿坦克] 已停止,碰撞已还原');
}
var Tool = {
Start: function () { _start(); },
Stop: function () { _stop(); },
GetRunning: function () { return Stat.running; },
Status: function () {
if (!Stat.running) return '已停止';
return Stat.shapes > 0 ? ('穿坦克 ' + Stat.shapes + ' 块') : '等待坦克';
}
};
window.__hornetTools__ = window.__hornetTools__ || {};
window.__hornetTools__.NoClip = Tool;
window.__hornetNoClip__ = {
IsTankPhysics: IsTankPhysics,
GetStat: function () {
return { running: Stat.running, shapes: Stat.shapes, applied: Stat.applied };
}
};
console.log('[3DTank] 穿坦克模块已加载');
})();
// ===== hornet_panel.js =====
// ===== 3DTank 控制面板 =====
// 功能:Alt+T 切换面板显示,控制各工具开关
@@ -5960,14 +6141,15 @@
{ key: 'AimWarning', label: '瞄准警告', obj: tools.AimWarning },
{ key: 'FreeLook', label: '自由视角', obj: tools.FreeLook },
{ key: 'WarSandbox', label: '战争沙盘', obj: tools.WarSandbox },
{ key: 'Mouse', label: '鼠标稳定', obj: tools.Mouse }
{ key: 'Mouse', label: '鼠标稳定', obj: tools.Mouse },
{ key: 'NoClip', label: '穿坦克', obj: tools.NoClip }
];
}
// ===== 工具启用状态(持久化 + 默认启用)=====
// 默认除「自由视角」外全部开启:自由视角会直接接管相机,默认开启过于突兀
var ToolKeys = ['Radar', 'Wireframe', 'Rearview', 'AimWarning', 'FreeLook', 'WarSandbox', 'Mouse'];
var ToolDefaults = { Radar: true, Wireframe: true, Rearview: true, AimWarning: true, FreeLook: false, WarSandbox: true, Mouse: true };
var ToolKeys = ['Radar', 'Wireframe', 'Rearview', 'AimWarning', 'FreeLook', 'WarSandbox', 'Mouse', 'NoClip'];
var ToolDefaults = { Radar: true, Wireframe: true, Rearview: true, AimWarning: true, FreeLook: false, WarSandbox: true, Mouse: true, NoClip: false };
var ToolEnabled = {};
try {
var __savedEnabled = JSON.parse(localStorage.getItem('hornet_tool_enabled') || '{}');
+185 -3
View File
@@ -5808,6 +5808,187 @@
})();
// ===== hornet_noclip.js =====
// hornet_noclip.js — 穿坦克(移动中忽略玩家障碍物,强行行走)
// 放在控制面板「穿坦克」开关里,只影响**本地坦克**,不改服务端/其他玩家。
//
// 碰撞过滤(module_55088 的 ki.q9q / ki.x9o,全物理世界唯一判定处):
// if (0 === (a.y9j_1 & b.z9j_1) || 0 === (b.y9j_1 & a.z9j_1)) return; // 0 → 不产生碰撞
// · y9j_1 = 形状的 category 位组("我是哪一类")
// · z9j_1 = 形状的 mask 位组("我愿意和哪几类碰")
// 实测位定义(module_87072):
// bit0(1) = 坦克(车体 hull-main 与各部件统一 y9j_1 = 1)
// bit4(16) = 地图静态几何(s6() 造地形:y9j_1 = 16, z9j_1 = 1055)
// 坦克 mask 恒含 16 → 清掉 bit0 后:撞墙照常,**只穿坦克**。
//
// 为什么每帧补一次:
// 引擎在坦克状态切换时会重写 mask —— tjt() 调 n155(11)(把 bit0 加回来),
// onClientTankState 的 2/5 分支调 n155(1)。所以运行期间每帧把 bit0 清掉。
//
// 停止:按进入时快照的原始 mask 精确还原(不猜引擎默认值)。
(function () {
'use strict';
var Lib = window.__hornetLib__;
if (!Lib) {
console.error('[穿坦克] 请先加载 hornet_lib.js');
return;
}
// 坦克 category 位(bit0)
var TANK_CATEGORY_BIT = 1;
var Stat = {
running: false,
shapes: 0, // 本帧处理的碰撞形状数
applied: 0 // 本次开启累计生效帧数
};
var CurrentPhysics = null; // 当前被处理的 tankPhysics 组件(ejt)
var MaskSnapshot = new Map(); // 形状 -> 进入时原始 mask
var LoggedPhysics = new WeakSet(); // 已打印过日志的组件
// 判定是否坦克物理组件(ejt / 组件名 tankPhysics)
// 特征:zzq_1 是车体主碰撞形状(x9j_1 === "hull-main"),且带 n155 / m155 / pbk
function IsTankPhysics(Comp) {
if (!Comp) return false;
if (typeof Comp.n155 !== 'function' || typeof Comp.m155 !== 'function') return false;
if (typeof Comp.pbk !== 'function') return false;
var Hull = Comp.zzq_1;
return !!(Hull && Hull.x9j_1 === 'hull-main');
}
// 在实体上找坦克物理组件(不走 FindComponentByField:zzq_1 字段名在其它类里也有复用)
function FindTankPhysics(Tank) {
if (!Tank || !Tank.qz1_1 || !Tank.qz1_1.s2a_1) return null;
var Found = null;
try {
Tank.qz1_1.s2a_1.forEach(function (ValueList) {
if (Found) return;
try {
var Comp = (ValueList && typeof ValueList.g1 === 'function') ? ValueList.g1(0) : ValueList;
if (IsTankPhysics(Comp)) Found = Comp;
} catch (e) {}
});
} catch (e) {}
return Found;
}
// 本地坦克:拥有中的坦克优先;观战/死亡时退回当前观察目标(此时不参与战斗,影响可忽略)
function GetLocalTank() {
var Gm = window.__gameMode__;
if (!Gm) return null;
try { if (Gm.u10b_1) return Gm.u10b_1; } catch (e) {}
try { if (Gm.t10b_1) return Gm.t10b_1; } catch (e) {}
return null;
}
// 还原快照里的原始 mask
function ReleaseSnapshot() {
MaskSnapshot.forEach(function (Mask, Shape) {
try {
if (Shape && typeof Mask === 'number') Shape.z9j_1 = Mask;
} catch (e) {}
});
MaskSnapshot.clear();
CurrentPhysics = null;
Stat.shapes = 0;
}
// 对本地坦克所有碰撞形状清掉「坦克位」
function ApplyMask() {
var Tank = GetLocalTank();
if (!Tank) { if (CurrentPhysics) ReleaseSnapshot(); return; }
var Phys = FindTankPhysics(Tank);
if (!Phys) { if (CurrentPhysics) ReleaseSnapshot(); return; }
// 换车/重生:物理组件被重建 → 先还原旧的,再接管新的
if (Phys !== CurrentPhysics) {
if (CurrentPhysics) ReleaseSnapshot();
CurrentPhysics = Phys;
}
var Body = null;
try { Body = Phys.pbk(); } catch (e) { Body = null; }
var Shapes = Body && Body.y9i_1;
if (!Shapes || typeof Shapes.e1 !== 'function' || typeof Shapes.g1 !== 'function') return;
var Count = Shapes.e1() | 0;
var Touched = 0;
for (var i = 0; i < Count; i++) {
var Shape = null;
try { Shape = Shapes.g1(i); } catch (e) { Shape = null; }
if (!Shape || typeof Shape.z9j_1 !== 'number') continue;
// 首次见到该形状时记录原始 mask,保证 Stop 能精确还原
if (!MaskSnapshot.has(Shape)) MaskSnapshot.set(Shape, Shape.z9j_1);
Shape.z9j_1 = Shape.z9j_1 & ~TANK_CATEGORY_BIT;
Touched++;
}
Stat.shapes = Touched;
if (Touched > 0) {
Stat.applied++;
if (!LoggedPhysics.has(Phys)) {
LoggedPhysics.add(Phys);
// 原始 mask 一并打印:若这里没有任何 mask 含 bit0,说明坦克碰撞位变了,需要重新核对
var Masks = [];
MaskSnapshot.forEach(function (Mask) { Masks.push(Mask); });
var HasTankBit = false;
for (var k = 0; k < Masks.length; k++) { if (Masks[k] & TANK_CATEGORY_BIT) { HasTankBit = true; break; } }
console.log('[穿坦克] 已生效:本地坦克 ' + Touched + ' 块碰撞形状,原始 mask=[' + Masks.join(',') + '] ' +
(HasTankBit ? '(含坦克位 bit0,已清除)' : '⚠ 未发现坦克位 bit0,可能版本字段有变'));
}
}
}
function Loop() {
if (!Stat.running) return;
requestAnimationFrame(Loop);
try { ApplyMask(); } catch (e) {
// 引擎中途换场景/重建物理时可能抛错,忽略即可(下一帧会重新尝试)
}
}
function _start() {
if (Stat.running) return;
Stat.running = true;
Stat.applied = 0;
ApplyMask();
requestAnimationFrame(Loop);
console.log('[穿坦克] 已启动(只穿坦克,地形碰撞保留)');
}
function _stop() {
if (!Stat.running) return;
Stat.running = false;
ReleaseSnapshot();
console.log('[穿坦克] 已停止,碰撞已还原');
}
var Tool = {
Start: function () { _start(); },
Stop: function () { _stop(); },
GetRunning: function () { return Stat.running; },
Status: function () {
if (!Stat.running) return '已停止';
return Stat.shapes > 0 ? ('穿坦克 ' + Stat.shapes + ' 块') : '等待坦克';
}
};
window.__hornetTools__ = window.__hornetTools__ || {};
window.__hornetTools__.NoClip = Tool;
window.__hornetNoClip__ = {
IsTankPhysics: IsTankPhysics,
GetStat: function () {
return { running: Stat.running, shapes: Stat.shapes, applied: Stat.applied };
}
};
console.log('[3DTank] 穿坦克模块已加载');
})();
// ===== hornet_panel.js =====
// ===== 3DTank 控制面板 =====
// 功能:Alt+T 切换面板显示,控制各工具开关
@@ -5960,14 +6141,15 @@
{ key: 'AimWarning', label: '瞄准警告', obj: tools.AimWarning },
{ key: 'FreeLook', label: '自由视角', obj: tools.FreeLook },
{ key: 'WarSandbox', label: '战争沙盘', obj: tools.WarSandbox },
{ key: 'Mouse', label: '鼠标稳定', obj: tools.Mouse }
{ key: 'Mouse', label: '鼠标稳定', obj: tools.Mouse },
{ key: 'NoClip', label: '穿坦克', obj: tools.NoClip }
];
}
// ===== 工具启用状态(持久化 + 默认启用)=====
// 默认除「自由视角」外全部开启:自由视角会直接接管相机,默认开启过于突兀
var ToolKeys = ['Radar', 'Wireframe', 'Rearview', 'AimWarning', 'FreeLook', 'WarSandbox', 'Mouse'];
var ToolDefaults = { Radar: true, Wireframe: true, Rearview: true, AimWarning: true, FreeLook: false, WarSandbox: true, Mouse: true };
var ToolKeys = ['Radar', 'Wireframe', 'Rearview', 'AimWarning', 'FreeLook', 'WarSandbox', 'Mouse', 'NoClip'];
var ToolDefaults = { Radar: true, Wireframe: true, Rearview: true, AimWarning: true, FreeLook: false, WarSandbox: true, Mouse: true, NoClip: false };
var ToolEnabled = {};
try {
var __savedEnabled = JSON.parse(localStorage.getItem('hornet_tool_enabled') || '{}');
+3 -2
View File
@@ -24,8 +24,9 @@ FILES = [
"hornet_freelook.js", # 7. 自由视角模块 -> __hornetTools__.FreeLook
"hornet_warsandbox.js", # 8. 战争沙盘模块 -> __hornetTools__.WarSandbox
"hornet_mouse.js", # 9. 鼠标输入稳定器 -> __hornetTools__.Mouse
"hornet_panel.js", # 10. 控制面板(Alt+T + HUD)
"hornet_prefetch.js", # 11. 车库资源预热(战局空闲时后台加载车库所需资源)
"hornet_noclip.js", # 10. 穿坦克(忽略玩家碰撞)-> __hornetTools__.NoClip
"hornet_panel.js", # 11. 控制面板(Alt+T + HUD)
"hornet_prefetch.js", # 12. 车库资源预热(战局空闲时后台加载车库所需资源)
]
HEADER = """// ============================================================
+178
View File
@@ -0,0 +1,178 @@
// hornet_noclip.js — 穿坦克(移动中忽略玩家障碍物,强行行走)
// 放在控制面板「穿坦克」开关里,只影响**本地坦克**,不改服务端/其他玩家。
//
// 碰撞过滤(module_55088 的 ki.q9q / ki.x9o,全物理世界唯一判定处):
// if (0 === (a.y9j_1 & b.z9j_1) || 0 === (b.y9j_1 & a.z9j_1)) return; // 0 → 不产生碰撞
// · y9j_1 = 形状的 category 位组("我是哪一类")
// · z9j_1 = 形状的 mask 位组("我愿意和哪几类碰")
// 实测位定义(module_87072):
// bit0(1) = 坦克(车体 hull-main 与各部件统一 y9j_1 = 1)
// bit4(16) = 地图静态几何(s6() 造地形:y9j_1 = 16, z9j_1 = 1055)
// 坦克 mask 恒含 16 → 清掉 bit0 后:撞墙照常,**只穿坦克**。
//
// 为什么每帧补一次:
// 引擎在坦克状态切换时会重写 mask —— tjt() 调 n155(11)(把 bit0 加回来),
// onClientTankState 的 2/5 分支调 n155(1)。所以运行期间每帧把 bit0 清掉。
//
// 停止:按进入时快照的原始 mask 精确还原(不猜引擎默认值)。
(function () {
'use strict';
var Lib = window.__hornetLib__;
if (!Lib) {
console.error('[穿坦克] 请先加载 hornet_lib.js');
return;
}
// 坦克 category 位(bit0)
var TANK_CATEGORY_BIT = 1;
var Stat = {
running: false,
shapes: 0, // 本帧处理的碰撞形状数
applied: 0 // 本次开启累计生效帧数
};
var CurrentPhysics = null; // 当前被处理的 tankPhysics 组件(ejt)
var MaskSnapshot = new Map(); // 形状 -> 进入时原始 mask
var LoggedPhysics = new WeakSet(); // 已打印过日志的组件
// 判定是否坦克物理组件(ejt / 组件名 tankPhysics)
// 特征:zzq_1 是车体主碰撞形状(x9j_1 === "hull-main"),且带 n155 / m155 / pbk
function IsTankPhysics(Comp) {
if (!Comp) return false;
if (typeof Comp.n155 !== 'function' || typeof Comp.m155 !== 'function') return false;
if (typeof Comp.pbk !== 'function') return false;
var Hull = Comp.zzq_1;
return !!(Hull && Hull.x9j_1 === 'hull-main');
}
// 在实体上找坦克物理组件(不走 FindComponentByField:zzq_1 字段名在其它类里也有复用)
function FindTankPhysics(Tank) {
if (!Tank || !Tank.qz1_1 || !Tank.qz1_1.s2a_1) return null;
var Found = null;
try {
Tank.qz1_1.s2a_1.forEach(function (ValueList) {
if (Found) return;
try {
var Comp = (ValueList && typeof ValueList.g1 === 'function') ? ValueList.g1(0) : ValueList;
if (IsTankPhysics(Comp)) Found = Comp;
} catch (e) {}
});
} catch (e) {}
return Found;
}
// 本地坦克:拥有中的坦克优先;观战/死亡时退回当前观察目标(此时不参与战斗,影响可忽略)
function GetLocalTank() {
var Gm = window.__gameMode__;
if (!Gm) return null;
try { if (Gm.u10b_1) return Gm.u10b_1; } catch (e) {}
try { if (Gm.t10b_1) return Gm.t10b_1; } catch (e) {}
return null;
}
// 还原快照里的原始 mask
function ReleaseSnapshot() {
MaskSnapshot.forEach(function (Mask, Shape) {
try {
if (Shape && typeof Mask === 'number') Shape.z9j_1 = Mask;
} catch (e) {}
});
MaskSnapshot.clear();
CurrentPhysics = null;
Stat.shapes = 0;
}
// 对本地坦克所有碰撞形状清掉「坦克位」
function ApplyMask() {
var Tank = GetLocalTank();
if (!Tank) { if (CurrentPhysics) ReleaseSnapshot(); return; }
var Phys = FindTankPhysics(Tank);
if (!Phys) { if (CurrentPhysics) ReleaseSnapshot(); return; }
// 换车/重生:物理组件被重建 → 先还原旧的,再接管新的
if (Phys !== CurrentPhysics) {
if (CurrentPhysics) ReleaseSnapshot();
CurrentPhysics = Phys;
}
var Body = null;
try { Body = Phys.pbk(); } catch (e) { Body = null; }
var Shapes = Body && Body.y9i_1;
if (!Shapes || typeof Shapes.e1 !== 'function' || typeof Shapes.g1 !== 'function') return;
var Count = Shapes.e1() | 0;
var Touched = 0;
for (var i = 0; i < Count; i++) {
var Shape = null;
try { Shape = Shapes.g1(i); } catch (e) { Shape = null; }
if (!Shape || typeof Shape.z9j_1 !== 'number') continue;
// 首次见到该形状时记录原始 mask,保证 Stop 能精确还原
if (!MaskSnapshot.has(Shape)) MaskSnapshot.set(Shape, Shape.z9j_1);
Shape.z9j_1 = Shape.z9j_1 & ~TANK_CATEGORY_BIT;
Touched++;
}
Stat.shapes = Touched;
if (Touched > 0) {
Stat.applied++;
if (!LoggedPhysics.has(Phys)) {
LoggedPhysics.add(Phys);
// 原始 mask 一并打印:若这里没有任何 mask 含 bit0,说明坦克碰撞位变了,需要重新核对
var Masks = [];
MaskSnapshot.forEach(function (Mask) { Masks.push(Mask); });
var HasTankBit = false;
for (var k = 0; k < Masks.length; k++) { if (Masks[k] & TANK_CATEGORY_BIT) { HasTankBit = true; break; } }
console.log('[穿坦克] 已生效:本地坦克 ' + Touched + ' 块碰撞形状,原始 mask=[' + Masks.join(',') + '] ' +
(HasTankBit ? '(含坦克位 bit0,已清除)' : '⚠ 未发现坦克位 bit0,可能版本字段有变'));
}
}
}
function Loop() {
if (!Stat.running) return;
requestAnimationFrame(Loop);
try { ApplyMask(); } catch (e) {
// 引擎中途换场景/重建物理时可能抛错,忽略即可(下一帧会重新尝试)
}
}
function _start() {
if (Stat.running) return;
Stat.running = true;
Stat.applied = 0;
ApplyMask();
requestAnimationFrame(Loop);
console.log('[穿坦克] 已启动(只穿坦克,地形碰撞保留)');
}
function _stop() {
if (!Stat.running) return;
Stat.running = false;
ReleaseSnapshot();
console.log('[穿坦克] 已停止,碰撞已还原');
}
var Tool = {
Start: function () { _start(); },
Stop: function () { _stop(); },
GetRunning: function () { return Stat.running; },
Status: function () {
if (!Stat.running) return '已停止';
return Stat.shapes > 0 ? ('穿坦克 ' + Stat.shapes + ' 块') : '等待坦克';
}
};
window.__hornetTools__ = window.__hornetTools__ || {};
window.__hornetTools__.NoClip = Tool;
window.__hornetNoClip__ = {
IsTankPhysics: IsTankPhysics,
GetStat: function () {
return { running: Stat.running, shapes: Stat.shapes, applied: Stat.applied };
}
};
console.log('[3DTank] 穿坦克模块已加载');
})();
+4 -3
View File
@@ -149,14 +149,15 @@
{ key: 'AimWarning', label: '瞄准警告', obj: tools.AimWarning },
{ key: 'FreeLook', label: '自由视角', obj: tools.FreeLook },
{ key: 'WarSandbox', label: '战争沙盘', obj: tools.WarSandbox },
{ key: 'Mouse', label: '鼠标稳定', obj: tools.Mouse }
{ key: 'Mouse', label: '鼠标稳定', obj: tools.Mouse },
{ key: 'NoClip', label: '穿坦克', obj: tools.NoClip }
];
}
// ===== 工具启用状态(持久化 + 默认启用)=====
// 默认除「自由视角」外全部开启:自由视角会直接接管相机,默认开启过于突兀
var ToolKeys = ['Radar', 'Wireframe', 'Rearview', 'AimWarning', 'FreeLook', 'WarSandbox', 'Mouse'];
var ToolDefaults = { Radar: true, Wireframe: true, Rearview: true, AimWarning: true, FreeLook: false, WarSandbox: true, Mouse: true };
var ToolKeys = ['Radar', 'Wireframe', 'Rearview', 'AimWarning', 'FreeLook', 'WarSandbox', 'Mouse', 'NoClip'];
var ToolDefaults = { Radar: true, Wireframe: true, Rearview: true, AimWarning: true, FreeLook: false, WarSandbox: true, Mouse: true, NoClip: false };
var ToolEnabled = {};
try {
var __savedEnabled = JSON.parse(localStorage.getItem('hornet_tool_enabled') || '{}');