fix(tools): 工具链符号对齐新版 bundle(修复 HORNET 功能大面积失效)
- 根因:新版 main.7cfa4058 重拆后游戏符号整体平移,工具里写死的字段名(pyt_1/q100_1/r11l/oyz/jyw/cyw_1/dyw_1…)全部对不上 → 找不到组件/相机/场景 - 新增 tools/apply_symbol_map.js:按 map_symbols 映射批量替换,4 道安全规则:① 长名或含下划线 ② 排除 JS 关键字/内置名黑名单 ③ 游戏符号命名特征(含数字或 _数字 结尾) ④ 双向校验(旧名在新 bundle 仍存在→视为未变跳过;新名不存在→映射错误丢弃) - 单遍替换,避免链式污染(映射表新旧同名复用时会被二次替换) - 应用到 12 个 hornet_*.js 共 231 处(跳过 25764 个未变符号) - 重新生成 hornet_bundle.js(10 模块 / 249263 字节)并同步 unpacked
This commit is contained in:
1 parent
469f913619
commit
dcf5dfce5c
12 files changed
+638
-538
No files matched your search
@@ -0,0 +1,100 @@
|
||||
// 临时脚本:按 tmp_symbol_map.json 的映射,批量替换 tools/hornet_*.js 里的符号名
|
||||
// 用法:node tools/tmp_apply_symbol_map.js
|
||||
// 安全规则(上一版全量替换误伤了 function/return/window/length 等 JS 关键字与内置名):
|
||||
// ① 旧名必须是"长名或含下划线"(>=4 字符 或 含 '_')—— 游戏符号基本都长这样;
|
||||
// ② 旧名/新名都不能落在 JS 关键字/内置名黑名单里;
|
||||
// ③ 只替换独立标识符(带词边界)。
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const MapPath = path.resolve(__dirname, '..', '3DTank', 'tmp_symbol_map.json');
|
||||
const Map = (require(MapPath)._merged) || {};
|
||||
|
||||
// JS 关键字 + 内置名 + 常见通用词:绝不能参与替换
|
||||
const BLACK = new Set([
|
||||
'function', 'return', 'var', 'let', 'const', 'null', 'undefined', 'true', 'false', 'this',
|
||||
'typeof', 'instanceof', 'new', 'delete', 'void', 'in', 'of', 'if', 'else', 'for', 'while',
|
||||
'do', 'switch', 'case', 'break', 'continue', 'throw', 'try', 'catch', 'finally', 'class',
|
||||
'extends', 'super', 'yield', 'await', 'async', 'default', 'export', 'import',
|
||||
'window', 'document', 'console', 'Math', 'Object', 'Array', 'String', 'Number', 'Boolean',
|
||||
'JSON', 'Date', 'Error', 'Promise', 'Map', 'Set', 'Symbol', 'Proxy', 'Reflect',
|
||||
'length', 'name', 'value', 'text', 'call', 'apply', 'bind', 'prototype', 'constructor',
|
||||
'toString', 'valueOf', 'equals', 'hashCode', 'push', 'pop', 'slice', 'splice', 'indexOf',
|
||||
'forEach', 'filter', 'reduce', 'join', 'split', 'replace', 'match', 'test', 'keys', 'values',
|
||||
'entries', 'has', 'get', 'set', 'add', 'clear', 'size', 'run', 'self', 'globalThis',
|
||||
'requestAnimationFrame', 'setTimeout', 'setInterval', 'parseInt', 'parseFloat', 'isNaN',
|
||||
'NaN', 'Infinity', 'arguments', 'prototype_', 'undefined_',
|
||||
]);
|
||||
|
||||
// ===== 双向校验用的"新 bundle 符号表" =====
|
||||
// 背景:map_symbols 的投票在"通用字段名"上会算错(例如把未变的 f20_1 投成 w9p_1)。
|
||||
// 这里直接拿新 bundle 原文做事实校验:
|
||||
// ① 旧名在新 bundle 里**仍然存在** → 说明它根本没变,跳过(保守,避免误改);
|
||||
// ② 新名在新 bundle 里**不存在** → 说明这条映射是错的,丢弃。
|
||||
const BUNDLE_PATH = path.resolve(__dirname, '..', 'main.7cfa4058.js');
|
||||
const BUNDLE = fs.existsSync(BUNDLE_PATH) ? fs.readFileSync(BUNDLE_PATH, 'utf8') : '';
|
||||
const CountCache = {};
|
||||
function CountInBundle(Name) {
|
||||
if (!BUNDLE) return 1; // 拿不到 bundle 就不做校验(退回旧行为)
|
||||
if (CountCache[Name] !== undefined) return CountCache[Name];
|
||||
const Re = new RegExp('(?<![A-Za-z0-9_$])' + Name.replace(/\$/g, '\\$') + '(?![A-Za-z0-9_$])', 'g');
|
||||
const M = BUNDLE.match(Re);
|
||||
CountCache[Name] = M ? M.length : 0;
|
||||
return CountCache[Name];
|
||||
}
|
||||
|
||||
// 收集要替换的条目
|
||||
const Entries = [];
|
||||
const Skipped = { keeps: [], bad: [] };
|
||||
for (const K of Object.keys(Map)) {
|
||||
const V = Map[K];
|
||||
if (typeof V !== 'string' || !V || V === K) continue;
|
||||
if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(K)) continue;
|
||||
if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(V)) continue;
|
||||
if (BLACK.has(K) || BLACK.has(V)) continue;
|
||||
if (K.length < 4 && K.indexOf('_') < 0) continue; // 规则①:长名或含下划线
|
||||
// 规则④:游戏符号的命名特征 —— 必须含数字,或以 _数字 结尾(如 pyt_1 / q100_1 / r11l / cyw_1)。
|
||||
// 这样能排除工具自身的普通英文单词变量(placeholder / mode / length 之类)。
|
||||
if (!/[0-9]/.test(K) && !/_[0-9]+$/.test(K)) continue;
|
||||
// 规则⑤/⑥:双向校验
|
||||
if (CountInBundle(K) > 0) { Skipped.keeps.push(K); continue; }
|
||||
if (CountInBundle(V) === 0) { Skipped.bad.push(K + '→' + V); continue; }
|
||||
Entries.push([K, V]);
|
||||
}
|
||||
if (Skipped.keeps.length) console.log('跳过(旧名在新版仍存在,视为未变) ' + Skipped.keeps.length + ' 个: ' + Skipped.keeps.slice(0, 20).join(' ') + (Skipped.keeps.length > 20 ? ' …' : ''));
|
||||
if (Skipped.bad.length) console.log('丢弃(新名在新版不存在,映射有误) ' + Skipped.bad.length + ' 条: ' + Skipped.bad.slice(0, 20).join(' ') + (Skipped.bad.length > 20 ? ' …' : ''));
|
||||
// 长符号优先,避免短符号先吃掉长符号的一部分
|
||||
Entries.sort((A, B) => B[0].length - A[0].length);
|
||||
|
||||
const Dir = path.resolve(__dirname);
|
||||
const Files = fs.readdirSync(Dir).filter((F) => /^hornet_.*\.js$/.test(F));
|
||||
|
||||
// 单遍替换:一个正则同时匹配所有旧名,回调里查映射。
|
||||
// 这样能避免"上一轮的替换结果又被下一轮规则二次替换"(映射表里新旧符号可能同名复用,会造成链式污染)。
|
||||
const Alt = new RegExp(
|
||||
'(?<![A-Za-z0-9_$])(' + Entries.map((E) => E[0].replace(/\$/g, '\\$')).join('|') + ')(?![A-Za-z0-9_$])',
|
||||
'g'
|
||||
);
|
||||
let HitsOf = {}; // 注意:别用 new Map()——本文件里 Map 已被映射表变量占用
|
||||
|
||||
let TotalHits = 0;
|
||||
for (const F of Files) {
|
||||
const P = path.join(Dir, F);
|
||||
const Before = fs.readFileSync(P, 'utf8');
|
||||
HitsOf = {};
|
||||
const S = Before.replace(Alt, (M) => {
|
||||
const V = Map[M];
|
||||
if (!V || V === M) return M;
|
||||
HitsOf[M + '→' + V] = (HitsOf[M + '→' + V] || 0) + 1;
|
||||
return V;
|
||||
});
|
||||
if (S !== Before) {
|
||||
fs.writeFileSync(P, S, 'utf8');
|
||||
let Hits = 0;
|
||||
Object.keys(HitsOf).forEach((K) => (Hits += HitsOf[K]));
|
||||
TotalHits += Hits;
|
||||
const Detail = Object.keys(HitsOf).map((K) => K + '(' + HitsOf[K] + ')');
|
||||
console.log(F + ':' + Hits + ' 处 ' + Detail.slice(0, 12).join(' ') + (Detail.length > 12 ? ' …共' + Detail.length + '种' : ''));
|
||||
}
|
||||
}
|
||||
console.log('— 合计 ' + TotalHits + ' 处替换 —');
|
||||
@@ -178,12 +178,12 @@
|
||||
}
|
||||
|
||||
// ===== 获取 KMt / TDt 渲染组件 =====
|
||||
// 新 build:车身皮肤组件字段是 w14w_1(kDt(t)=t.w14w_1,缺失报 "skin";RDt.v16b 里赋值)
|
||||
// 旧 build:u14l_1 / t14l_1 / p14l_1(新版 t14l_1 已改成悬停车体状态 Qmt,务必别再当皮肤用)
|
||||
// 新 build:车身皮肤组件字段是 w14w_1(kDt(t)=t.w14w_1,缺失报 "skin";RDt.j16r 里赋值)
|
||||
// 旧 build:w14x_1 / t14l_1 / p14l_1(新版 t14l_1 已改成悬停车体状态 Qmt,务必别再当皮肤用)
|
||||
function GetKMtComponent(entity) {
|
||||
var comp = FindComponentByField(entity, 'w14w_1');
|
||||
if (comp) return comp;
|
||||
comp = FindComponentByField(entity, 'u14l_1');
|
||||
comp = FindComponentByField(entity, 'w14x_1');
|
||||
if (comp) return comp;
|
||||
comp = FindComponentByField(entity, 't14l_1');
|
||||
if (comp) return comp;
|
||||
@@ -191,13 +191,13 @@
|
||||
return comp;
|
||||
}
|
||||
|
||||
// 取车身皮肤对象(含 s17b_1 部件表),跨版本兼容;stats 用于诊断来源
|
||||
// 取车身皮肤对象(含 g17r_1 部件表),跨版本兼容;stats 用于诊断来源
|
||||
function GetHullSkin(tank, stats) {
|
||||
var comp = GetKMtComponent(tank);
|
||||
if (!comp) { if (stats) stats.hullSource = '无组件'; return null; }
|
||||
if (comp.w14w_1 && comp.w14w_1.s17b_1) { if (stats) stats.hullSource = 'w14w_1'; return comp.w14w_1; } // 新 build
|
||||
if (comp.t14l_1 && comp.t14l_1.s17b_1) { if (stats) stats.hullSource = 't14l_1'; return comp.t14l_1; } // 旧 build
|
||||
if (comp.p14l_1 && comp.p14l_1.s17b_1) { if (stats) stats.hullSource = 'p14l_1'; return comp.p14l_1; } // 旧 build
|
||||
if (comp.w14w_1 && comp.w14w_1.g17r_1) { if (stats) stats.hullSource = 'w14w_1'; return comp.w14w_1; } // 新 build
|
||||
if (comp.t14l_1 && comp.t14l_1.g17r_1) { if (stats) stats.hullSource = 't14l_1'; return comp.t14l_1; } // 旧 build
|
||||
if (comp.p14l_1 && comp.p14l_1.g17r_1) { if (stats) stats.hullSource = 'p14l_1'; return comp.p14l_1; } // 旧 build
|
||||
if (stats) stats.hullSource = '有组件但无皮肤';
|
||||
return null;
|
||||
}
|
||||
@@ -211,14 +211,14 @@
|
||||
|
||||
// ===== 从GPU读取顶点位置数据(缓冲区[0]) =====
|
||||
function ReadVertexPositionsFromTp(tp) {
|
||||
if (!tp || !tp.t4w_1) return null;
|
||||
var tl = tp.t4w_1.g1(0);
|
||||
if (!tp || !tp.x4w_1) return null;
|
||||
var tl = tp.x4w_1.g1(0);
|
||||
if (!tl || !tl.d3x_1) return null;
|
||||
var vq = tl.d3x_1;
|
||||
var bs = vq.s3h_1;
|
||||
if (!bs || !bs.b74_1) return null;
|
||||
var gl = bs.b74_1;
|
||||
var webglBuf = vq.r3x_1 && vq.r3x_1.z73_1;
|
||||
var webglBuf = vq.v3x_1 && vq.v3x_1.z73_1;
|
||||
if (!webglBuf) return null;
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, webglBuf);
|
||||
var bufSize = gl.getBufferParameter(gl.ARRAY_BUFFER, gl.BUFFER_SIZE);
|
||||
@@ -236,12 +236,12 @@
|
||||
var allIndices = [];
|
||||
for (var m = 0; m < meshCount; m++) {
|
||||
var il = tp.y4w_1.g1(m);
|
||||
if (!il || !il.q3p_1) continue;
|
||||
var fq = il.q3p_1;
|
||||
if (!il || !il.u3p_1) continue;
|
||||
var fq = il.u3p_1;
|
||||
var bs = fq.s3h_1;
|
||||
if (!bs || !bs.b74_1) continue;
|
||||
var gl = bs.b74_1;
|
||||
var webglBuf = fq.j3x_1 && fq.j3x_1.z73_1;
|
||||
var webglBuf = fq.n3x_1 && fq.n3x_1.z73_1;
|
||||
if (!webglBuf) continue;
|
||||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, webglBuf);
|
||||
var bufSize = gl.getBufferParameter(gl.ELEMENT_ARRAY_BUFFER, gl.BUFFER_SIZE);
|
||||
@@ -298,25 +298,25 @@
|
||||
function CollectMeshNodes(bpNode, nodes, depth, group, stats) {
|
||||
if (!bpNode || depth > 5) return;
|
||||
if (stats) stats.seen++;
|
||||
var name = bpNode.e35_1 || '';
|
||||
if (bpNode.t3m_1) {
|
||||
var name = bpNode.g35_1 || '';
|
||||
if (bpNode.v3m_1) {
|
||||
if (stats) stats.hasMesh++;
|
||||
var gpuData = GetGpuData(bpNode.t3m_1);
|
||||
var gpuData = GetGpuData(bpNode.v3m_1);
|
||||
if (gpuData) {
|
||||
if (stats) stats.gpuOk++;
|
||||
nodes.push({
|
||||
name: name, node: bpNode, tp: bpNode.t3m_1,
|
||||
name: name, node: bpNode, tp: bpNode.v3m_1,
|
||||
positions: gpuData.positions, indices: gpuData.indices, stride: gpuData.stride, group: group
|
||||
});
|
||||
} else if (stats) stats.gpuFail++;
|
||||
}
|
||||
if (bpNode.q4x_1 && typeof bpNode.q4x_1.e1 === 'function') {
|
||||
if (bpNode.u4x_1 && typeof bpNode.u4x_1.e1 === 'function') {
|
||||
try {
|
||||
var lodCount = bpNode.q4x_1.e1();
|
||||
var lodCount = bpNode.u4x_1.e1();
|
||||
for (var lod = 0; lod < lodCount && lod < 1; lod++) {
|
||||
var lodLevel = bpNode.q4x_1.g1(lod);
|
||||
var lodLevel = bpNode.u4x_1.g1(lod);
|
||||
if (!lodLevel) continue;
|
||||
var lodTp = lodLevel.t3m_1 || lodLevel.v3m_1;
|
||||
var lodTp = lodLevel.v3m_1 || lodLevel.v3m_1;
|
||||
if (!lodTp) continue;
|
||||
var lodGpuData = GetGpuData(lodTp);
|
||||
if (lodGpuData) {
|
||||
@@ -346,14 +346,14 @@
|
||||
var skin = GetHullSkin(tank, stats);
|
||||
if (skin) {
|
||||
try {
|
||||
var partCount = skin.s17b_1.e1();
|
||||
var partCount = skin.g17r_1.e1();
|
||||
if (stats) stats.hullParts = partCount;
|
||||
for (var p = 0; p < partCount; p++) {
|
||||
var part = skin.s17b_1.g1(p);
|
||||
if (!part || !part.v17b_1) continue;
|
||||
var nodeCount = part.v17b_1.e1();
|
||||
var part = skin.g17r_1.g1(p);
|
||||
if (!part || !part.j17r_1) continue;
|
||||
var nodeCount = part.j17r_1.e1();
|
||||
for (var n = 0; n < nodeCount; n++) {
|
||||
var bpNode = part.v17b_1.g1(n);
|
||||
var bpNode = part.j17r_1.g1(n);
|
||||
if (!bpNode) continue;
|
||||
CollectMeshNodes(bpNode, nodes, 0, 'hull', stats ? stats.hull : null);
|
||||
}
|
||||
@@ -920,7 +920,7 @@
|
||||
var physicsComp = FindComponentByField(tank, 'gzh_1');
|
||||
if (!physicsComp || !physicsComp.gzh_1) continue;
|
||||
var tankState = GetTankState(tank);
|
||||
// 可见坦克 = ACTIVE(3) 或 SEMI_ACTIVE(2)(引擎 y147() 判定),0/1/4 才跳过
|
||||
// 可见坦克 = ACTIVE(3) 或 SEMI_ACTIVE(2)(引擎 s14j() 判定),0/1/4 才跳过
|
||||
if (tankState !== 2 && tankState !== 3) continue;
|
||||
|
||||
var pos = {
|
||||
@@ -932,7 +932,7 @@
|
||||
// 本帧该坦克是否在移动(供「残留节点」检测:坦克在动而节点矩阵不动 = 旧位置遗留)
|
||||
// 用位移的指数滑动平均,而不是「单帧位移 > 10 单位」——后者对慢速移动几乎永远为假,
|
||||
// 会让残留检测形同虚设(残留漏掉的根因就在这里)
|
||||
var tankId = tank.hyt_1;
|
||||
var tankId = tank.iz1_1;
|
||||
var prevPos = TankPrevPos.get(tankId);
|
||||
var tankMove = 0;
|
||||
if (prevPos) {
|
||||
@@ -950,7 +950,7 @@
|
||||
if (distance > 50000) continue;
|
||||
|
||||
var friendly = IsFriendly(tank, playerTank);
|
||||
var tankName = GetTankName(tank) || ('ID:' + tank.hyt_1);
|
||||
var tankName = GetTankName(tank) || ('ID:' + tank.iz1_1);
|
||||
// 根据 HighlightMode 判断是否渲染该坦克
|
||||
if (!ShouldRender(tankName)) continue;
|
||||
// 颜色按队伍:友方=蓝,敌方=红
|
||||
|
||||
+13
-13
@@ -128,12 +128,12 @@
|
||||
if (UMtComponent) return UMtComponent;
|
||||
try {
|
||||
var gmEntity = gm.jyw();
|
||||
if (!gmEntity || !gmEntity.pyt_1 || !gmEntity.pyt_1.s2a_1) return null;
|
||||
gmEntity.pyt_1.s2a_1.forEach(function(valueList, key) {
|
||||
if (!gmEntity || !gmEntity.qz1_1 || !gmEntity.qz1_1.s2a_1) return null;
|
||||
gmEntity.qz1_1.s2a_1.forEach(function(valueList, key) {
|
||||
if (UMtComponent) return;
|
||||
try {
|
||||
var comp = (valueList && typeof valueList.g1 === 'function') ? valueList.g1(0) : valueList;
|
||||
if (comp && typeof comp.m179 === 'function') UMtComponent = comp;
|
||||
if (comp && typeof comp.a17p === 'function') UMtComponent = comp;
|
||||
} catch(e) {}
|
||||
});
|
||||
if (UMtComponent) console.log('[瞄准] uMt 碰撞检测器已获取');
|
||||
@@ -178,26 +178,26 @@
|
||||
y178_1: MkVec(0,0,0),
|
||||
z178_1: MkVec(0,0,0),
|
||||
a179_1: maxDist,
|
||||
c179_1: null,
|
||||
q17o_1: null,
|
||||
b179_1: null,
|
||||
d179: function(t, n, i, r, e) {
|
||||
r17o: function(t, n, i, r, e) {
|
||||
this.y178_1.k26(t);
|
||||
this.z178_1.k26(n);
|
||||
this.a179_1 = i;
|
||||
this.c179_1 = r;
|
||||
this.q17o_1 = r;
|
||||
this.b179_1 = e;
|
||||
},
|
||||
d3: function() { this.b179_1 = null; }
|
||||
};
|
||||
|
||||
var filter = {
|
||||
o1co_1: { h1: function() { return false; }, d3: function() {} },
|
||||
l1da_1: { h1: function() { return false; }, d3: function() {} },
|
||||
w179: function() { return true; },
|
||||
d3: function() { this.o1co_1.d3(); },
|
||||
p1co: function() {}
|
||||
d3: function() { this.l1da_1.d3(); },
|
||||
m1da: function() {}
|
||||
};
|
||||
|
||||
var hit = uMt.m179(startVec, dirVec, maxDist, result, filter);
|
||||
var hit = uMt.a17p(startVec, dirVec, maxDist, result, filter);
|
||||
blocked = !!hit && result.a179_1 < (maxDist - 50);
|
||||
RaycastFailCount = 0;
|
||||
} catch(e) {
|
||||
@@ -310,19 +310,19 @@
|
||||
if (aimInfo.angle >= aimThreshold) continue;
|
||||
|
||||
// 墙体阻隔判断
|
||||
var blocked = IsLineOfSightBlocked(pos, playerPos, tank.hyt_1, frameCount);
|
||||
var blocked = IsLineOfSightBlocked(pos, playerPos, tank.iz1_1, frameCount);
|
||||
if (blocked) continue;
|
||||
|
||||
// 记录威胁
|
||||
var proj = Project3D(pos, camInfo.pos, camInfo.yaw, camInfo.pitch, camInfo.fov, screenW, screenH);
|
||||
var tankName = GetTankName(tank) || ('ID:' + tank.hyt_1);
|
||||
var tankName = GetTankName(tank) || ('ID:' + tank.iz1_1);
|
||||
threats.push({
|
||||
pos: pos,
|
||||
distance: aimInfo.distance,
|
||||
angle: aimInfo.angle,
|
||||
turretDir: aimInfo.turretDir,
|
||||
screenProj: proj,
|
||||
tankId: tank.hyt_1,
|
||||
tankId: tank.iz1_1,
|
||||
tankName: tankName
|
||||
});
|
||||
}
|
||||
|
||||
+11
-11
@@ -33,14 +33,14 @@
|
||||
var initError = null;
|
||||
|
||||
try {
|
||||
scene = gm.jyw().iyt_1;
|
||||
mainCamera = scene.hyp_1;
|
||||
ff = scene.fyo_1.d75_1;
|
||||
if (!mainCamera || !mainCamera.s35_1 || !mainCamera.r35_1 || !mainCamera.o37) {
|
||||
scene = gm.jyw().jz1_1;
|
||||
mainCamera = scene.iyx_1;
|
||||
ff = scene.gyw_1.d75_1;
|
||||
if (!mainCamera || !mainCamera.u35_1 || !mainCamera.r35_1 || !mainCamera.o37) {
|
||||
throw new Error('相机字段缺失');
|
||||
}
|
||||
console.log('[自由视角] 主相机:', mainCamera.constructor.name,
|
||||
'l35=(' + mainCamera.s35_1.f20_1.toFixed(2) + ',' + mainCamera.s35_1.g20_1.toFixed(2) + ',' + mainCamera.s35_1.h20_1.toFixed(2) + ')',
|
||||
'l35=(' + mainCamera.u35_1.f20_1.toFixed(2) + ',' + mainCamera.u35_1.g20_1.toFixed(2) + ',' + mainCamera.u35_1.h20_1.toFixed(2) + ')',
|
||||
'k35=(' + mainCamera.r35_1.f20_1.toFixed(0) + ',' + mainCamera.r35_1.g20_1.toFixed(0) + ',' + mainCamera.r35_1.h20_1.toFixed(0) + ')');
|
||||
} catch(e) {
|
||||
initError = e;
|
||||
@@ -67,9 +67,9 @@
|
||||
var origH37 = mainCamera.o37;
|
||||
|
||||
function applyFreeLookParams() {
|
||||
mainCamera.s35_1.f20_1 = freePitch; // pitch (f)
|
||||
mainCamera.s35_1.g20_1 = freeRoll; // roll (g) 保持初始值
|
||||
mainCamera.s35_1.h20_1 = freeYaw; // yaw (h) 鼠标左右控制
|
||||
mainCamera.u35_1.f20_1 = freePitch; // pitch (f)
|
||||
mainCamera.u35_1.g20_1 = freeRoll; // roll (g) 保持初始值
|
||||
mainCamera.u35_1.h20_1 = freeYaw; // yaw (h) 鼠标左右控制
|
||||
mainCamera.r35_1.f20_1 = freePosX;
|
||||
mainCamera.r35_1.g20_1 = freePosY;
|
||||
mainCamera.r35_1.h20_1 = freePosZ;
|
||||
@@ -106,9 +106,9 @@
|
||||
function enableFreeLook() {
|
||||
// 从当前相机参数初始化(平滑过渡)
|
||||
// l35_1: f=pitch, g=roll, h=yaw
|
||||
freePitch = mainCamera.s35_1.f20_1;
|
||||
freeRoll = mainCamera.s35_1.g20_1; // roll 保持初始值
|
||||
freeYaw = mainCamera.s35_1.h20_1; // yaw 鼠标控制
|
||||
freePitch = mainCamera.u35_1.f20_1;
|
||||
freeRoll = mainCamera.u35_1.g20_1; // roll 保持初始值
|
||||
freeYaw = mainCamera.u35_1.h20_1; // yaw 鼠标控制
|
||||
freePosX = mainCamera.r35_1.f20_1;
|
||||
freePosY = mainCamera.r35_1.g20_1;
|
||||
freePosZ = mainCamera.r35_1.h20_1;
|
||||
|
||||
+33
-33
@@ -35,9 +35,9 @@
|
||||
// 遍历 entity.wyj_1.r2a_1(Map<组件标识, 组件列表>),value 是列表类(s_),g1(0) 获取实际组件
|
||||
// 注意:用 !== undefined 检查字段存在性,避免 falsy 值(0/""/null/false)漏判
|
||||
function FindComponentByField(entity, fieldName) {
|
||||
if (!entity || !entity.pyt_1 || !entity.pyt_1.s2a_1) return null;
|
||||
if (!entity || !entity.qz1_1 || !entity.qz1_1.s2a_1) return null;
|
||||
var result = null;
|
||||
entity.pyt_1.s2a_1.forEach(function(valueList, key) {
|
||||
entity.qz1_1.s2a_1.forEach(function(valueList, key) {
|
||||
if (result) return;
|
||||
try {
|
||||
var comp = (valueList && typeof valueList.g1 === 'function') ? valueList.g1(0) : valueList;
|
||||
@@ -47,7 +47,7 @@
|
||||
return result;
|
||||
}
|
||||
|
||||
// ===== 获取坦克位置(G$t 物理组件的 pz7_1 = O$向量) =====
|
||||
// ===== 获取坦克位置(G$t 物理组件的 rzf_1 = O$向量) =====
|
||||
// 返回 {x, y, z},x/y 水平面,z 高度
|
||||
function GetTankPosition(tank) {
|
||||
var physicsComp = FindComponentByField(tank, 'gzh_1');
|
||||
@@ -65,21 +65,21 @@
|
||||
// 状态枚举:0=DEAD, 1=DEAD_PHANTOM, 2=SEMI_ACTIVE, 3=ACTIVE, 4=DISABLED
|
||||
// Yjt 特征:有 azs_1(状态机)和 vzr_1(状态枚举)字段
|
||||
function GetTankState(tank) {
|
||||
if (!tank || !tank.pyt_1 || !tank.pyt_1.s2a_1) return -1;
|
||||
if (!tank || !tank.qz1_1 || !tank.qz1_1.s2a_1) return -1;
|
||||
var tankComp = null;
|
||||
tank.pyt_1.s2a_1.forEach(function(valueList, key) {
|
||||
tank.qz1_1.s2a_1.forEach(function(valueList, key) {
|
||||
if (tankComp) return;
|
||||
try {
|
||||
var comp = (valueList && typeof valueList.g1 === 'function') ? valueList.g1(0) : valueList;
|
||||
if (comp && comp.t101_1 && comp.o101_1 !== undefined) tankComp = comp;
|
||||
if (comp && comp.t101_1 && comp.k10c_1 !== undefined) tankComp = comp;
|
||||
} catch(e) {}
|
||||
});
|
||||
if (tankComp && tankComp.o101_1) return tankComp.o101_1.k3_1;
|
||||
if (tankComp && tankComp.k10c_1) return tankComp.k10c_1.k3_1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 可见/战斗中的坦克 = ACTIVE(3) 或 SEMI_ACTIVE(2)
|
||||
// 依据:引擎用 tankComponent.y147()(= SEMI_ACTIVE || ACTIVE)决定该坦克节点是否渲染,
|
||||
// 依据:引擎用 tankComponent.s14j()(= SEMI_ACTIVE || ACTIVE)决定该坦克节点是否渲染,
|
||||
// 只认 ACTIVE 会漏掉半活跃状态(出生/过渡)的坦克
|
||||
function IsTankAlive(tank) {
|
||||
var s = GetTankState(tank);
|
||||
@@ -89,20 +89,20 @@
|
||||
// ===== 获取坦克名称(yot 用户信息组件) =====
|
||||
// s120_1 = userName, u120_1 = clanTag(可为null), t120_1 = userRank
|
||||
function GetTankName(tank) {
|
||||
var userComp = FindComponentByField(tank, 'k12a_1');
|
||||
var userComp = FindComponentByField(tank, 'c12n_1');
|
||||
if (!userComp) return null;
|
||||
var name = userComp.k12a_1 || '';
|
||||
var clanTag = userComp.m12a_1 || '';
|
||||
var name = userComp.c12n_1 || '';
|
||||
var clanTag = userComp.e12n_1 || '';
|
||||
return clanTag ? (clanTag + ' ' + name) : name;
|
||||
}
|
||||
|
||||
// ===== 获取坦克血量(Vkt 血量组件) =====
|
||||
// f122_1 = 最大血量(max), g122_1 = 当前血量(current, 死亡时=0)
|
||||
function GetTankHealth(tank) {
|
||||
var healthComp = FindComponentByField(tank, 'y12b_1');
|
||||
var healthComp = FindComponentByField(tank, 'q12o_1');
|
||||
if (!healthComp) return null;
|
||||
var max = healthComp.x12b_1 || 0;
|
||||
var cur = healthComp.y12b_1 || 0;
|
||||
var max = healthComp.p12o_1 || 0;
|
||||
var cur = healthComp.q12o_1 || 0;
|
||||
if (max <= 0) return null;
|
||||
return { current: cur, max: max, percent: cur / max };
|
||||
}
|
||||
@@ -155,9 +155,9 @@
|
||||
// checkPosition: 是否检查位置有效性(默认true;测试脚本传false以在死亡状态控制)
|
||||
function GetPlayerTank(gm, checkPosition) {
|
||||
if (checkPosition === undefined) checkPosition = true;
|
||||
var t = gm.x100_1;
|
||||
var t = gm.t10b_1;
|
||||
if (t && (!checkPosition || GetTankPosition(t))) return t;
|
||||
t = gm.y100_1;
|
||||
t = gm.u10b_1;
|
||||
if (t && (!checkPosition || GetTankPosition(t))) return t;
|
||||
try {
|
||||
var tl = gm.q100_1.r11l();
|
||||
@@ -182,10 +182,10 @@
|
||||
} catch(e) {}
|
||||
}
|
||||
try {
|
||||
if (gm && gm.z100_1 && ZCt) {
|
||||
if (gm && gm.v10b_1 && ZCt) {
|
||||
var team = ZCt(tank);
|
||||
if (team && gm.z100_1) {
|
||||
return team.equals(gm.z100_1) && m2Fn ? m2Fn(team, gm.z100_1) : team.equals(gm.z100_1);
|
||||
if (team && gm.v10b_1) {
|
||||
return team.equals(gm.v10b_1) && m2Fn ? m2Fn(team, gm.v10b_1) : team.equals(gm.v10b_1);
|
||||
}
|
||||
}
|
||||
} catch(e) {}
|
||||
@@ -195,13 +195,13 @@
|
||||
// ===== 获取相机信息($r/gq 相机组件) =====
|
||||
// 遍历 GameMode entity 和所有坦克 entity 查找活跃相机组件
|
||||
// 策略1:查找 $r/gq 相机组件(有 kym_1 位置 和 lym_1 方向)
|
||||
// 策略2:查找 k7 相机控制器(有 qz9_1 和 gz9_1),从 gz9_1 获取相机组件
|
||||
// 策略2:查找 k7 相机控制器(有 qz9_1 和 jzh_1),从 jzh_1 获取相机组件
|
||||
// 返回 {yaw, pitch, fov, pos:{x,y,z}}
|
||||
// 注意:lym_1.h20_1 是 roll(绕Z),lym_1.f20_1 是 pitch(绕X)
|
||||
function GetCameraInfo(gm) {
|
||||
var entities = [];
|
||||
try {
|
||||
var gameScene = gm.jyw().iyt_1;
|
||||
var gameScene = gm.jyw().jz1_1;
|
||||
var gmEntity = gameScene.xyu("GameMode");
|
||||
if (gmEntity) entities.push(gmEntity);
|
||||
} catch(e) {}
|
||||
@@ -216,9 +216,9 @@
|
||||
|
||||
for (var i = 0; i < entities.length; i++) {
|
||||
var entity = entities[i];
|
||||
if (!entity || !entity.pyt_1 || !entity.pyt_1.s2a_1) continue;
|
||||
if (!entity || !entity.qz1_1 || !entity.qz1_1.s2a_1) continue;
|
||||
var found = null;
|
||||
entity.pyt_1.s2a_1.forEach(function(valueList, key) {
|
||||
entity.qz1_1.s2a_1.forEach(function(valueList, key) {
|
||||
if (found) return;
|
||||
try {
|
||||
var comp = (valueList && typeof valueList.g1 === 'function') ? valueList.g1(0) : valueList;
|
||||
@@ -263,13 +263,13 @@
|
||||
function GetUMtComponent(gm) {
|
||||
try {
|
||||
var gmEntity = gm.jyw();
|
||||
if (!gmEntity || !gmEntity.pyt_1 || !gmEntity.pyt_1.s2a_1) return null;
|
||||
if (!gmEntity || !gmEntity.qz1_1 || !gmEntity.qz1_1.s2a_1) return null;
|
||||
var uMt = null;
|
||||
gmEntity.pyt_1.s2a_1.forEach(function(valueList, key) {
|
||||
gmEntity.qz1_1.s2a_1.forEach(function(valueList, key) {
|
||||
if (uMt) return;
|
||||
try {
|
||||
var comp = (valueList && typeof valueList.g1 === 'function') ? valueList.g1(0) : valueList;
|
||||
if (comp && typeof comp.m179 === 'function') uMt = comp;
|
||||
if (comp && typeof comp.a17p === 'function') uMt = comp;
|
||||
} catch(e) {}
|
||||
});
|
||||
return uMt;
|
||||
@@ -299,24 +299,24 @@
|
||||
y178_1: MkVec(0, 0, 0),
|
||||
z178_1: MkVec(0, 0, 0),
|
||||
a179_1: maxDist,
|
||||
c179_1: null,
|
||||
q17o_1: null,
|
||||
b179_1: null,
|
||||
d179: function(t, n, i, r, e) {
|
||||
r17o: function(t, n, i, r, e) {
|
||||
this.y178_1.k26(t);
|
||||
this.z178_1.k26(n);
|
||||
this.a179_1 = i;
|
||||
this.c179_1 = r;
|
||||
this.q17o_1 = r;
|
||||
this.b179_1 = e;
|
||||
},
|
||||
d3: function() { this.b179_1 = null; }
|
||||
};
|
||||
var filter = {
|
||||
o1co_1: { h1: function() { return false; }, d3: function() {} },
|
||||
l1da_1: { h1: function() { return false; }, d3: function() {} },
|
||||
w179: function() { return true; },
|
||||
d3: function() { this.o1co_1.d3(); },
|
||||
p1co: function() {}
|
||||
d3: function() { this.l1da_1.d3(); },
|
||||
m1da: function() {}
|
||||
};
|
||||
var hit = uMt.m179(startVec, dirVec, maxDist, result, filter);
|
||||
var hit = uMt.a17p(startVec, dirVec, maxDist, result, filter);
|
||||
return (hit && result.a179_1 < maxDist) ? result.a179_1 : -1;
|
||||
} catch(e) {
|
||||
return -1;
|
||||
|
||||
@@ -538,7 +538,7 @@
|
||||
});
|
||||
|
||||
// ===== 电磁炮(RAILGUN / z4t)设置 =====
|
||||
// 锁定/蓄力时长实时覆盖 window.__hornetLockSec__(游戏内 z4t.w187() 优先读它),
|
||||
// 锁定/蓄力时长实时覆盖 window.__hornetLockSec__(游戏内 z4t.c18o() 优先读它),
|
||||
// 拖动滑块**立即生效**,不需要重打包或刷新页面。
|
||||
// 实测门槛:1.5s / 2.0s / 原版3~5s = 满伤;1.0s / 0.1s = 无伤害 ⇒ 门槛在 1.0~1.5s。
|
||||
var rgSec = MakeSettingsSection('电磁炮设置');
|
||||
|
||||
+16
-16
@@ -36,7 +36,7 @@
|
||||
|
||||
// ===== 获取坦克方向(Kkt 组件的 dzc_1) =====
|
||||
function getTankDirection(tank) {
|
||||
var hullComp = FindComponentByField(tank, 'vzl_1');
|
||||
var hullComp = FindComponentByField(tank, 'qzv_1');
|
||||
if (hullComp && hullComp.wzl_1) {
|
||||
return {
|
||||
x: hullComp.wzl_1.f20_1,
|
||||
@@ -50,13 +50,13 @@
|
||||
// ===== 获取相机方向(gq/$r 组件的 lym_1.h20_1 = 偏航角) =====
|
||||
// 相机组件可能在 GameMode entity、玩家坦克或任何坦克上
|
||||
// 策略1:查找 $r/gq 相机组件(有 kym_1 和 lym_1 字段)
|
||||
// 策略2:查找 k7 相机控制器(有 qz9_1 和 gz9_1 字段),从 gz9_1 获取相机组件
|
||||
// 策略2:查找 k7 相机控制器(有 qz9_1 和 jzh_1 字段),从 jzh_1 获取相机组件
|
||||
function getCameraHeading() {
|
||||
var entities = [];
|
||||
|
||||
// 1. GameMode entity
|
||||
try {
|
||||
var gameScene = gm.jyw().iyt_1;
|
||||
var gameScene = gm.jyw().jz1_1;
|
||||
var gmEntity = gameScene.xyu("GameMode");
|
||||
if (gmEntity) entities.push(gmEntity);
|
||||
} catch(e) {}
|
||||
@@ -74,10 +74,10 @@
|
||||
// 在每个 entity 上查找相机组件
|
||||
for (var i = 0; i < entities.length; i++) {
|
||||
var entity = entities[i];
|
||||
if (!entity || !entity.pyt_1 || !entity.pyt_1.s2a_1) continue;
|
||||
if (!entity || !entity.qz1_1 || !entity.qz1_1.s2a_1) continue;
|
||||
|
||||
var found = null;
|
||||
entity.pyt_1.s2a_1.forEach(function(valueList, key) {
|
||||
entity.qz1_1.s2a_1.forEach(function(valueList, key) {
|
||||
if (found) return;
|
||||
try {
|
||||
var comp = (valueList && typeof valueList.g1 === 'function') ? valueList.g1(0) : valueList;
|
||||
@@ -93,7 +93,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 策略2:查找 k7 相机控制器(有 qz9_1 和 gz9_1 字段)
|
||||
// 策略2:查找 k7 相机控制器(有 qz9_1 和 jzh_1 字段)
|
||||
if (comp.jzj_1 && comp.zzi_1) {
|
||||
var camComp = comp.zzi_1;
|
||||
if (camComp && camComp.cyw_1 && camComp.dyw_1 &&
|
||||
@@ -147,12 +147,12 @@
|
||||
if (UMtComponent) return UMtComponent;
|
||||
try {
|
||||
var gmEntity = gm.jyw();
|
||||
if (!gmEntity || !gmEntity.pyt_1 || !gmEntity.pyt_1.s2a_1) return null;
|
||||
gmEntity.pyt_1.s2a_1.forEach(function(valueList, key) {
|
||||
if (!gmEntity || !gmEntity.qz1_1 || !gmEntity.qz1_1.s2a_1) return null;
|
||||
gmEntity.qz1_1.s2a_1.forEach(function(valueList, key) {
|
||||
if (UMtComponent) return;
|
||||
try {
|
||||
var comp = (valueList && typeof valueList.g1 === 'function') ? valueList.g1(0) : valueList;
|
||||
if (comp && typeof comp.m179 === 'function') UMtComponent = comp;
|
||||
if (comp && typeof comp.a17p === 'function') UMtComponent = comp;
|
||||
} catch(e) {}
|
||||
});
|
||||
if (UMtComponent) console.log('[雷达] uMt 碰撞检测器已获取');
|
||||
@@ -175,19 +175,19 @@
|
||||
var dirVec = MkVec(dirX, dirY, dirZ);
|
||||
var result = {
|
||||
y178_1: MkVec(0,0,0), z178_1: MkVec(0,0,0),
|
||||
a179_1: maxDist, c179_1: null, b179_1: null,
|
||||
d179: function(t, n, i, r, e) {
|
||||
a179_1: maxDist, q17o_1: null, b179_1: null,
|
||||
r17o: function(t, n, i, r, e) {
|
||||
this.y178_1.k26(t); this.z178_1.k26(n);
|
||||
this.a179_1 = i; this.c179_1 = r; this.b179_1 = e;
|
||||
this.a179_1 = i; this.q17o_1 = r; this.b179_1 = e;
|
||||
},
|
||||
d3: function() { this.b179_1 = null; }
|
||||
};
|
||||
var filter = {
|
||||
o1co_1: { h1: function() { return false; }, d3: function() {} },
|
||||
l1da_1: { h1: function() { return false; }, d3: function() {} },
|
||||
w179: function() { return true; },
|
||||
d3: function() {}, p1co: function() {}
|
||||
d3: function() {}, m1da: function() {}
|
||||
};
|
||||
var hit = uMt.m179(startVec, dirVec, maxDist, result, filter);
|
||||
var hit = uMt.a17p(startVec, dirVec, maxDist, result, filter);
|
||||
return (hit && result.a179_1 < maxDist) ? result.a179_1 : -1;
|
||||
} catch(e) {
|
||||
return -1;
|
||||
@@ -746,7 +746,7 @@
|
||||
ctx.textAlign = 'left';
|
||||
ctx.fillText('Tanks: ' + count + ' (B:' + friendlyCount + ' R:' + enemyCount + ' X:' + deadCount + ' Bot:' + botCount + ')', 5, 12);
|
||||
if (radar.playerFound) {
|
||||
ctx.fillText('Player: ' + (playerTank ? playerTank.hyt_1 : '?'), 5, 24);
|
||||
ctx.fillText('Player: ' + (playerTank ? playerTank.iz1_1 : '?'), 5, 24);
|
||||
} else {
|
||||
ctx.fillText('Player: NOT FOUND', 5, 24);
|
||||
}
|
||||
|
||||
+31
-31
@@ -81,11 +81,11 @@
|
||||
var initError = null;
|
||||
|
||||
try {
|
||||
scene = gm.jyw().iyt_1;
|
||||
scene = gm.jyw().jz1_1;
|
||||
console.log('[后视] 场景对象:', scene);
|
||||
|
||||
// hC 渲染器实例(jr.lye_1)
|
||||
var renderer = scene.fyo_1;
|
||||
var renderer = scene.gyw_1;
|
||||
console.log('[后视] 渲染器:', renderer);
|
||||
|
||||
// Ff 绘制器实例(hC.r74_1)
|
||||
@@ -93,11 +93,11 @@
|
||||
console.log('[后视] Ff绘制器:', ff);
|
||||
|
||||
// 场景根节点(jr.kyf_1)
|
||||
rootNode = scene.eyp_1;
|
||||
rootNode = scene.fyx_1;
|
||||
console.log('[后视] 根节点:', rootNode);
|
||||
|
||||
// 主相机 Ha 实例(jr.nyf_1)
|
||||
mainCamera = scene.hyp_1;
|
||||
mainCamera = scene.iyx_1;
|
||||
console.log('[后视] 主相机:', mainCamera);
|
||||
|
||||
// 游戏 gl context(Ff.p39_1.p73_1 = BS.p73_1)
|
||||
@@ -109,11 +109,11 @@
|
||||
}
|
||||
|
||||
// 探测相机字段
|
||||
console.log('[后视] 相机s35_1:', mainCamera.s35_1);
|
||||
console.log('[后视] 相机u35_1:', mainCamera.u35_1);
|
||||
console.log('[后视] 相机r35_1:', mainCamera.r35_1);
|
||||
console.log('[后视] 相机b37_1:', mainCamera.b37_1);
|
||||
|
||||
if (!mainCamera.s35_1 || !mainCamera.r35_1) {
|
||||
if (!mainCamera.u35_1 || !mainCamera.r35_1) {
|
||||
throw new Error('相机字段缺失');
|
||||
}
|
||||
|
||||
@@ -309,14 +309,14 @@
|
||||
var camPosZ = playerPos.z + Config.cameraHeight;
|
||||
|
||||
// ===== 保存主相机参数 =====
|
||||
var savedPitch = mainCamera.s35_1.f20_1;
|
||||
var savedYaw = mainCamera.s35_1.g20_1;
|
||||
var savedRoll = mainCamera.s35_1.h20_1;
|
||||
var savedPitch = mainCamera.u35_1.f20_1;
|
||||
var savedYaw = mainCamera.u35_1.g20_1;
|
||||
var savedRoll = mainCamera.u35_1.h20_1;
|
||||
var savedX = mainCamera.r35_1.f20_1;
|
||||
var savedY = mainCamera.r35_1.g20_1;
|
||||
var savedZ = mainCamera.r35_1.h20_1;
|
||||
var savedL36 = mainCamera.s36_1;
|
||||
var savedM36 = mainCamera.t36_1;
|
||||
var savedL36 = mainCamera.u36_1;
|
||||
var savedM36 = mainCamera.v36_1;
|
||||
var savedS36 = mainCamera.z36_1;
|
||||
var savedQ36 = mainCamera.x36_1;
|
||||
var savedR36 = mainCamera.y36_1;
|
||||
@@ -330,14 +330,14 @@
|
||||
// g=π 使水平方向反向(朝后看),h=yaw+π 保持水平视角正确
|
||||
// f=-pitch 使垂直方向与主视一致(低头看后方地形)
|
||||
// up 朝下 → FBO 画面颠倒 → GL readback Y 翻转修正
|
||||
mainCamera.s35_1.f20_1 = rearPitch;
|
||||
mainCamera.s35_1.g20_1 = Math.PI;
|
||||
mainCamera.s35_1.h20_1 = camInfo.yaw + Math.PI;
|
||||
mainCamera.u35_1.f20_1 = rearPitch;
|
||||
mainCamera.u35_1.g20_1 = Math.PI;
|
||||
mainCamera.u35_1.h20_1 = camInfo.yaw + Math.PI;
|
||||
mainCamera.r35_1.f20_1 = camPosX;
|
||||
mainCamera.r35_1.g20_1 = camPosY;
|
||||
mainCamera.r35_1.h20_1 = camPosZ;
|
||||
mainCamera.s36_1 = rw;
|
||||
mainCamera.t36_1 = rh;
|
||||
mainCamera.u36_1 = rw;
|
||||
mainCamera.v36_1 = rh;
|
||||
mainCamera.z36_1 = Config.fov;
|
||||
mainCamera.x36_1 = Config.near;
|
||||
mainCamera.y36_1 = Config.far;
|
||||
@@ -349,14 +349,14 @@
|
||||
var rearViewMatrix = new Float32Array(mainCamera.b37_1);
|
||||
|
||||
// ===== 保存并禁用阴影/后处理(避免它们切换 FBO 覆盖 rearFbo)=====
|
||||
// p38_1.x3n_1 控制 z39_1(阴影),y3n_1 控制 c3a_1/d3a_1(后处理),z3n_1 控制 f3a_1
|
||||
// p38_1.b3o_1 控制 z39_1(阴影),c3o_1 控制 c3a_1/d3a_1(后处理),z3n_1 控制 f3a_1
|
||||
// 禁用后 y3s 只执行主场景渲染 z3k,跳过所有 FBO 切换的 pass
|
||||
var savedX3n = ff.w38_1.e3o_1;
|
||||
var savedY3n = ff.w38_1.f3o_1;
|
||||
var savedZ3n = ff.w38_1.g3o_1;
|
||||
ff.w38_1.e3o_1 = false;
|
||||
ff.w38_1.f3o_1 = false;
|
||||
ff.w38_1.g3o_1 = false;
|
||||
var savedX3n = ff.y38_1.e3o_1;
|
||||
var savedY3n = ff.y38_1.f3o_1;
|
||||
var savedZ3n = ff.y38_1.k3o_1;
|
||||
ff.y38_1.e3o_1 = false;
|
||||
ff.y38_1.f3o_1 = false;
|
||||
ff.y38_1.k3o_1 = false;
|
||||
|
||||
// ===== 禁用 UI 渲染层(防止技能条等 UI 混入后视 FBO)=====
|
||||
// 保存 UI 层队列,替换为空假对象,y3s 后恢复
|
||||
@@ -430,9 +430,9 @@
|
||||
// ===== 恢复阴影/后处理(异常时也要恢复)=====
|
||||
try {
|
||||
if (typeof savedX3n !== 'undefined') {
|
||||
ff.w38_1.e3o_1 = savedX3n;
|
||||
ff.w38_1.f3o_1 = savedY3n;
|
||||
ff.w38_1.g3o_1 = savedZ3n;
|
||||
ff.y38_1.e3o_1 = savedX3n;
|
||||
ff.y38_1.f3o_1 = savedY3n;
|
||||
ff.y38_1.k3o_1 = savedZ3n;
|
||||
}
|
||||
} catch(e) {
|
||||
console.error('[后视] 恢复阴影/后处理失败:', e.message);
|
||||
@@ -452,14 +452,14 @@
|
||||
|
||||
// ===== 恢复主相机参数 =====
|
||||
try {
|
||||
mainCamera.s35_1.f20_1 = savedPitch;
|
||||
mainCamera.s35_1.g20_1 = savedYaw;
|
||||
mainCamera.s35_1.h20_1 = savedRoll;
|
||||
mainCamera.u35_1.f20_1 = savedPitch;
|
||||
mainCamera.u35_1.g20_1 = savedYaw;
|
||||
mainCamera.u35_1.h20_1 = savedRoll;
|
||||
mainCamera.r35_1.f20_1 = savedX;
|
||||
mainCamera.r35_1.g20_1 = savedY;
|
||||
mainCamera.r35_1.h20_1 = savedZ;
|
||||
mainCamera.s36_1 = savedL36;
|
||||
mainCamera.t36_1 = savedM36;
|
||||
mainCamera.u36_1 = savedL36;
|
||||
mainCamera.v36_1 = savedM36;
|
||||
mainCamera.z36_1 = savedS36;
|
||||
mainCamera.x36_1 = savedQ36;
|
||||
mainCamera.y36_1 = savedR36;
|
||||
|
||||
+31
-31
@@ -2,10 +2,10 @@
|
||||
// 功能:通过修改 Dyt.j14b_1(履带控制状态 Uyt)触发 KC 回调 → ryp(Ds) → kyt 发送网络包
|
||||
// 原理:
|
||||
// 1. Dyt 构造函数中 KC 回调(z14a_1)注册到 gameScene 每帧更新
|
||||
// 2. KC 回调检测 j14b_1.equals(i14b_1) === false 时:
|
||||
// a) 复制 j14b_1 → i14b_1(l14a)
|
||||
// 2. KC 回调检测 j14b_1.equals(d14n_1) === false 时:
|
||||
// a) 复制 j14b_1 → d14n_1(l14a)
|
||||
// b) 触发 ryp(Ds)(_yt 事件全局单例)
|
||||
// 3. kyt 监听 _yt 事件 → 调用 w14b_1.b14c(time, j14b_1) 发送网络包
|
||||
// 3. kyt 监听 _yt 事件 → 调用 r14n_1.b14c(time, j14b_1) 发送网络包
|
||||
// 4. 这是游戏自然移动路径,网络包格式/时机完全正常,不触发服务器检测
|
||||
// 控制:W/S 前进/后退,A/D 左转/右转,松开停止
|
||||
// 在浏览器控制台运行(需先运行 hook_gamemode.js + hornet_lib.js)
|
||||
@@ -47,9 +47,9 @@
|
||||
|
||||
// ===== 获取玩家坦克 =====
|
||||
function GetPlayerTank() {
|
||||
var t = gm.y100_1;
|
||||
var t = gm.u10b_1;
|
||||
if (t) return t;
|
||||
t = gm.x100_1;
|
||||
t = gm.t10b_1;
|
||||
if (t) return t;
|
||||
try {
|
||||
var tl = gm.q100_1.r11l();
|
||||
@@ -77,9 +77,9 @@
|
||||
console.log(' n14m_1 (在战斗中):', dyt.n14m_1);
|
||||
try {
|
||||
console.log(' v14m_1:', dyt.v14m_1.toString());
|
||||
console.log(' i14b_1:', dyt.i14b_1.toString());
|
||||
console.log(' d14n_1:', dyt.d14n_1.toString());
|
||||
} catch(e) {
|
||||
console.log(' v14m_1/i14b_1 toString 失败:', e.message);
|
||||
console.log(' v14m_1/d14n_1 toString 失败:', e.message);
|
||||
}
|
||||
|
||||
// 检查 KC(z14a_1)的方法
|
||||
@@ -92,36 +92,36 @@
|
||||
}
|
||||
|
||||
// ===== 获取 maxSpeed 和 maxTurn =====
|
||||
// 尝试从 f14b_1(speedCharacteristics/dgt 组件)获取
|
||||
// 尝试从 a14n_1(speedCharacteristics/dgt 组件)获取
|
||||
try {
|
||||
var sc = dyt.r14m_1;
|
||||
var sc = dyt.q14y_1;
|
||||
if (sc) {
|
||||
if (typeof sc.na9 === 'function') {
|
||||
maxSpeed = sc.na9();
|
||||
console.log(' 从 r14m_1.na9() 获取 maxSpeed:', maxSpeed);
|
||||
console.log(' 从 q14y_1.na9() 获取 maxSpeed:', maxSpeed);
|
||||
} else if (sc.f14c_1 !== undefined) {
|
||||
maxSpeed = sc.f14c_1;
|
||||
console.log(' 从 r14m_1.f14c_1 获取 maxSpeed:', maxSpeed);
|
||||
console.log(' 从 q14y_1.f14c_1 获取 maxSpeed:', maxSpeed);
|
||||
}
|
||||
if (sc.j14c_1 !== undefined) {
|
||||
maxTurn = sc.j14c_1;
|
||||
console.log(' 从 r14m_1.j14c_1 获取 maxTurn:', maxTurn);
|
||||
console.log(' 从 q14y_1.j14c_1 获取 maxTurn:', maxTurn);
|
||||
}
|
||||
}
|
||||
} catch(e) {
|
||||
console.warn('[测试] 获取速度参数失败:', e.message);
|
||||
}
|
||||
|
||||
// 尝试从其他组件获取(a149_1 字段存储 na9() 的值)
|
||||
// 尝试从其他组件获取(u14k_1 字段存储 na9() 的值)
|
||||
if (maxSpeed === 100) {
|
||||
try {
|
||||
playerTank.pyt_1.s2a_1.forEach(function(valueList) {
|
||||
playerTank.qz1_1.s2a_1.forEach(function(valueList) {
|
||||
if (maxSpeed !== 100) return;
|
||||
try {
|
||||
var comp = valueList && typeof valueList.g1 === 'function' ? valueList.g1(0) : valueList;
|
||||
if (comp && comp.m14k_1 !== undefined && typeof comp.m14k_1 === 'number' && comp.m14k_1 > 0) {
|
||||
maxSpeed = comp.m14k_1;
|
||||
console.log(' 从组件 m14k_1 获取 maxSpeed:', maxSpeed);
|
||||
if (comp && comp.p14w_1 !== undefined && typeof comp.p14w_1 === 'number' && comp.p14w_1 > 0) {
|
||||
maxSpeed = comp.p14w_1;
|
||||
console.log(' 从组件 p14w_1 获取 maxSpeed:', maxSpeed);
|
||||
}
|
||||
} catch(e) {}
|
||||
});
|
||||
@@ -171,25 +171,25 @@
|
||||
var newS140 = forward * maxSpeed;
|
||||
var newT140 = turn * maxTurn;
|
||||
|
||||
// 只在值变化时设置(KC 回调检测 j14b_1 ≠ i14b_1 才触发 ryp(Ds))
|
||||
// 只在值变化时设置(KC 回调检测 j14b_1 ≠ d14n_1 才触发 ryp(Ds))
|
||||
if (newS140 !== lastS140 || newT140 !== lastT140) {
|
||||
try {
|
||||
dyt.v14m_1.m14b_1 = newS140;
|
||||
dyt.v14m_1.n14b_1 = newT140;
|
||||
// u140_1 (reverseBackTurn) 不修改
|
||||
// k14c_1 (reverseBackTurn) 不修改
|
||||
lastS140 = newS140;
|
||||
lastT140 = newT140;
|
||||
console.log('[移动] m14b_1=' + newS140 + ' n14b_1=' + newT140 +
|
||||
' (v14m_1 ≠ i14b_1 → KC 回调将触发 iyz(Ds) → kyt 发送网络包)');
|
||||
' (v14m_1 ≠ d14n_1 → KC 回调将触发 iyz(Ds) → kyt 发送网络包)');
|
||||
} catch(e) {
|
||||
console.error('[测试] 设置 v14m_1 失败:', e.message);
|
||||
}
|
||||
}
|
||||
}, 50);
|
||||
|
||||
// ===== KC 回调验证:修改 j14b_1 后检查 i14b_1 是否更新 =====
|
||||
// KC 回调会复制 j14b_1 → i14b_1(i14b_1.l14a(j14b_1))
|
||||
// 如果 i14b_1 更新了,说明 KC 回调每帧调用,方案有效
|
||||
// ===== KC 回调验证:修改 j14b_1 后检查 d14n_1 是否更新 =====
|
||||
// KC 回调会复制 j14b_1 → d14n_1(d14n_1.l14a(j14b_1))
|
||||
// 如果 d14n_1 更新了,说明 KC 回调每帧调用,方案有效
|
||||
kcVerified = false;
|
||||
var verifyCount = 0;
|
||||
verifyInterval = setInterval(function() {
|
||||
@@ -199,23 +199,23 @@
|
||||
clearInterval(verifyInterval);
|
||||
verifyInterval = null;
|
||||
if (!kcVerified) {
|
||||
console.warn('[验证] 3秒内未检测到 KC 回调工作(i14b_1 未跟随 v14m_1 更新)');
|
||||
console.warn('[验证] 3秒内未检测到 KC 回调工作(d14n_1 未跟随 v14m_1 更新)');
|
||||
console.warn('[验证] 可能原因:1) KC 回调不每帧调用 2) n14m_1=false 3) l14m_1 未注册');
|
||||
console.warn('[验证] 请尝试在游戏中移动(点击地面寻路),观察 i14b_1 是否变化');
|
||||
console.warn('[验证] 请尝试在游戏中移动(点击地面寻路),观察 d14n_1 是否变化');
|
||||
}
|
||||
return;
|
||||
}
|
||||
// 如果 j14b_1 ≠ i14b_1,说明 KC 回调还没复制(可能 KC 不每帧调用)
|
||||
// 如果 j14b_1 == i14b_1,说明 KC 回调已复制(KC 工作正常)
|
||||
// 如果 j14b_1 ≠ d14n_1,说明 KC 回调还没复制(可能 KC 不每帧调用)
|
||||
// 如果 j14b_1 == d14n_1,说明 KC 回调已复制(KC 工作正常)
|
||||
try {
|
||||
if (dyt.v14m_1.m14b_1 === dyt.i14b_1.m14b_1 &&
|
||||
dyt.v14m_1.n14b_1 === dyt.i14b_1.n14b_1) {
|
||||
if (dyt.v14m_1.m14b_1 === dyt.d14n_1.m14b_1 &&
|
||||
dyt.v14m_1.n14b_1 === dyt.d14n_1.n14b_1) {
|
||||
if (lastS140 !== 0 || lastT140 !== 0) {
|
||||
// j14b_1 有非零值且 i14b_1 已同步
|
||||
// j14b_1 有非零值且 d14n_1 已同步
|
||||
kcVerified = true;
|
||||
clearInterval(verifyInterval);
|
||||
verifyInterval = null;
|
||||
console.log('[验证] KC 回调工作正常!i14b_1 已跟随 v14m_1 更新');
|
||||
console.log('[验证] KC 回调工作正常!d14n_1 已跟随 v14m_1 更新');
|
||||
console.log('[验证] iyz(Ds) 应已触发,kyt 应已发送网络包');
|
||||
}
|
||||
}
|
||||
|
||||
+35
-35
@@ -39,21 +39,21 @@
|
||||
// 皮肤字段:新 build 是 w14w_1;旧 build 是 t14l_1(新版 t14l_1 已改成悬停车体状态 Qmt)
|
||||
var hullComp = FindComponentByField(tank, 'w14w_1') ||
|
||||
FindComponentByField(tank, 't14l_1') ||
|
||||
FindComponentByField(tank, 'u14l_1') ||
|
||||
FindComponentByField(tank, 'w14x_1') ||
|
||||
FindComponentByField(tank, 'p14l_1');
|
||||
if (!hullComp) return null;
|
||||
var skin = (hullComp.w14w_1 && hullComp.w14w_1.s17b_1) ? hullComp.w14w_1 :
|
||||
(hullComp.t14l_1 && hullComp.t14l_1.s17b_1) ? hullComp.t14l_1 :
|
||||
(hullComp.p14l_1 && hullComp.p14l_1.s17b_1) ? hullComp.p14l_1 : null;
|
||||
var skin = (hullComp.w14w_1 && hullComp.w14w_1.g17r_1) ? hullComp.w14w_1 :
|
||||
(hullComp.t14l_1 && hullComp.t14l_1.g17r_1) ? hullComp.t14l_1 :
|
||||
(hullComp.p14l_1 && hullComp.p14l_1.g17r_1) ? hullComp.p14l_1 : null;
|
||||
if (!skin) return null;
|
||||
try {
|
||||
var partCount = skin.s17b_1.e1();
|
||||
var partCount = skin.g17r_1.e1();
|
||||
for (var p = 0; p < partCount; p++) {
|
||||
var part = skin.s17b_1.g1(p);
|
||||
if (!part || !part.v17b_1) continue;
|
||||
var nodeCount = part.v17b_1.e1();
|
||||
var part = skin.g17r_1.g1(p);
|
||||
if (!part || !part.j17r_1) continue;
|
||||
var nodeCount = part.j17r_1.e1();
|
||||
for (var n = 0; n < nodeCount; n++) {
|
||||
var bpNode = part.v17b_1.g1(n);
|
||||
var bpNode = part.j17r_1.g1(n);
|
||||
if (!bpNode || !bpNode.n35_1) continue;
|
||||
// 不按 g35_1 过滤:它是引擎的细节/淡出权重(0~1),不是幽灵标志,
|
||||
// 过滤会漏掉低细节节点、导致取不到朝向
|
||||
@@ -87,10 +87,10 @@
|
||||
var scene = null, ff = null, rootNode = null, mainCamera = null, gl = null;
|
||||
var initError = null;
|
||||
try {
|
||||
scene = gm.jyw().iyt_1;
|
||||
ff = scene.fyo_1.d75_1;
|
||||
rootNode = scene.eyp_1;
|
||||
mainCamera = scene.hyp_1;
|
||||
scene = gm.jyw().jz1_1;
|
||||
ff = scene.gyw_1.d75_1;
|
||||
rootNode = scene.fyx_1;
|
||||
mainCamera = scene.iyx_1;
|
||||
gl = ff.w39_1.b74_1;
|
||||
if (!ff || !rootNode || !mainCamera || !gl) throw new Error('关键字段缺失');
|
||||
} catch(e) {
|
||||
@@ -661,37 +661,37 @@
|
||||
|
||||
try {
|
||||
// 保存主相机参数
|
||||
var sPitch = mainCamera.s35_1.f20_1;
|
||||
var sRoll = mainCamera.s35_1.g20_1;
|
||||
var sYaw = mainCamera.s35_1.h20_1;
|
||||
var sPitch = mainCamera.u35_1.f20_1;
|
||||
var sRoll = mainCamera.u35_1.g20_1;
|
||||
var sYaw = mainCamera.u35_1.h20_1;
|
||||
var sX = mainCamera.r35_1.f20_1;
|
||||
var sY = mainCamera.r35_1.g20_1;
|
||||
var sZ = mainCamera.r35_1.h20_1;
|
||||
var sL36 = mainCamera.s36_1, sM36 = mainCamera.t36_1;
|
||||
var sL36 = mainCamera.u36_1, sM36 = mainCamera.v36_1;
|
||||
var sS36 = mainCamera.z36_1, sQ36 = mainCamera.x36_1, sR36 = mainCamera.y36_1;
|
||||
|
||||
// 设置俯瞰相机参数
|
||||
// pitch=-π/2 朝下看,roll=0 不翻转,yaw=0 北朝上
|
||||
mainCamera.s35_1.f20_1 = -Math.PI / 2;
|
||||
mainCamera.s35_1.g20_1 = 0;
|
||||
mainCamera.s35_1.h20_1 = 0;
|
||||
mainCamera.u35_1.f20_1 = -Math.PI / 2;
|
||||
mainCamera.u35_1.g20_1 = 0;
|
||||
mainCamera.u35_1.h20_1 = 0;
|
||||
mainCamera.r35_1.f20_1 = viewCenterX;
|
||||
mainCamera.r35_1.g20_1 = viewCenterY;
|
||||
mainCamera.r35_1.h20_1 = camHeight;
|
||||
mainCamera.s36_1 = FBO_W;
|
||||
mainCamera.t36_1 = FBO_H;
|
||||
mainCamera.u36_1 = FBO_W;
|
||||
mainCamera.v36_1 = FBO_H;
|
||||
mainCamera.z36_1 = Config.fov;
|
||||
mainCamera.x36_1 = Config.near;
|
||||
mainCamera.y36_1 = Config.far;
|
||||
mainCamera.o37();
|
||||
|
||||
// 禁用阴影/后处理
|
||||
var savedX3n = ff.w38_1.e3o_1;
|
||||
var savedY3n = ff.w38_1.f3o_1;
|
||||
var savedZ3n = ff.w38_1.g3o_1;
|
||||
ff.w38_1.e3o_1 = false;
|
||||
ff.w38_1.f3o_1 = false;
|
||||
ff.w38_1.g3o_1 = false;
|
||||
var savedX3n = ff.y38_1.e3o_1;
|
||||
var savedY3n = ff.y38_1.f3o_1;
|
||||
var savedZ3n = ff.y38_1.k3o_1;
|
||||
ff.y38_1.e3o_1 = false;
|
||||
ff.y38_1.f3o_1 = false;
|
||||
ff.y38_1.k3o_1 = false;
|
||||
|
||||
// 禁用 UI 层
|
||||
var savedUiQueues = {};
|
||||
@@ -744,21 +744,21 @@
|
||||
origBindFramebuffer(gl.FRAMEBUFFER, mainFbo);
|
||||
|
||||
// 恢复渲染状态
|
||||
ff.w38_1.e3o_1 = savedX3n;
|
||||
ff.w38_1.f3o_1 = savedY3n;
|
||||
ff.w38_1.g3o_1 = savedZ3n;
|
||||
ff.y38_1.e3o_1 = savedX3n;
|
||||
ff.y38_1.f3o_1 = savedY3n;
|
||||
ff.y38_1.k3o_1 = savedZ3n;
|
||||
for (var ui2 = 0; ui2 < uiLayerIndices.length; ui2++) {
|
||||
ff.e39_1[uiLayerIndices[ui2]] = savedUiQueues[uiLayerIndices[ui2]];
|
||||
}
|
||||
|
||||
// 恢复主相机
|
||||
mainCamera.s35_1.f20_1 = sPitch;
|
||||
mainCamera.s35_1.g20_1 = sRoll;
|
||||
mainCamera.s35_1.h20_1 = sYaw;
|
||||
mainCamera.u35_1.f20_1 = sPitch;
|
||||
mainCamera.u35_1.g20_1 = sRoll;
|
||||
mainCamera.u35_1.h20_1 = sYaw;
|
||||
mainCamera.r35_1.f20_1 = sX;
|
||||
mainCamera.r35_1.g20_1 = sY;
|
||||
mainCamera.r35_1.h20_1 = sZ;
|
||||
mainCamera.s36_1 = sL36; mainCamera.t36_1 = sM36;
|
||||
mainCamera.u36_1 = sL36; mainCamera.v36_1 = sM36;
|
||||
mainCamera.z36_1 = sS36; mainCamera.x36_1 = sQ36; mainCamera.y36_1 = sR36;
|
||||
mainCamera.o37();
|
||||
|
||||
|
||||
Reference in new issue
Block a user