feat(noclip): 新增穿坦克工具并接入面板开关
清掉本地坦克碰撞形状 mask 的坦克位(bit0),可穿过其他玩家坦克;地形静态几何(category bit4)碰撞保持不变。引擎状态切换会重写 mask,运行期每帧补一次,停止时按原始快照精确还原。
This commit is contained in:
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
@@ -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
@@ -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 = """// ============================================================
|
||||
|
||||
@@ -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] 穿坦克模块已加载');
|
||||
})();
|
||||
@@ -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') || '{}');
|
||||
|
||||
Reference in new issue
Block a user