From 66a74df6e3431ad19ae25a66032c30d55bbf142b Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Tue, 20 Oct 2026 22:04:07 +0800 Subject: [PATCH] =?UTF-8?q?refactor(schema):=20=E5=85=A8=E9=83=A8=E5=B7=A5?= =?UTF-8?q?=E5=85=B7=E6=94=B9=E4=B8=BA=E7=BB=8F=E5=AD=97=E6=AE=B5=E6=8A=BD?= =?UTF-8?q?=E8=B1=A1=E5=B1=82=E5=8F=96=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hornet_lib / hook_gamemode / radar / 透视 / 后视镜 / 瞄准警告 / 自由视角 / 战争沙盘 全部改为 Fld/Mth 常量取值;schema 补齐渲染器、网格、射线、武器、皮肤槽位等 88 项字段与别名;SelfCheck 改为只校验核心字段,避免别名误报。 --- .../play/static/js/unpacked/hornet_bundle.js | 1028 +++++++++++------ 3dtank_bundle.js | 1028 +++++++++++------ tools/hook_gamemode.js | 19 +- tools/hornet_3d_wireframe.js | 123 +- tools/hornet_aim_warning.js | 76 +- tools/hornet_freelook.js | 63 +- tools/hornet_lib.js | 206 ++-- tools/hornet_radar.js | 59 +- tools/hornet_rearview.js | 170 +-- tools/hornet_schema.js | 142 ++- tools/hornet_warsandbox.js | 170 ++- 11 files changed, 2073 insertions(+), 1011 deletions(-) diff --git a/3dtank.com/play/static/js/unpacked/hornet_bundle.js b/3dtank.com/play/static/js/unpacked/hornet_bundle.js index f456422..441edc4 100644 --- a/3dtank.com/play/static/js/unpacked/hornet_bundle.js +++ b/3dtank.com/play/static/js/unpacked/hornet_bundle.js @@ -21,6 +21,15 @@ // 坦克列表 = dct(scene).m10b_1.j11y() (function() { + // ===== 字段抽象层(语义名 → 匿名字段,总表在 hornet_schema.js)===== + var Schema = window.__hornetSchema__ || null; + function Fld(Key, Fallback) { var N = Schema ? Schema.FieldName(Key) : null; return N || Fallback; } + function Mth(Key, Fallback) { var N = Schema ? Schema.MethodName(Key) : null; return N || Fallback; } + + // ===== 语义字段常量(取不到抽象层时用 fallback 字面名) ===== + var FGameModeTankManager = Fld('GameMode.TankManager', 'm10b_1'); // 坦克列表管理器 + var MTankList = Mth('GameMode.TankList', 'j11y'); // 坦克列表 + console.log('===== GameMode Hook v2 开始 ====='); // 1. 检查 webpack 内部对象 @@ -55,7 +64,7 @@ // 尝试获取坦克列表(失败也不影响 gameMode 设置) var tankCount = -1; try { - var list = gm.m10b_1.j11y(); + var list = gm[FGameModeTankManager][MTankList](); tankCount = list.e1(); window.__tankList__ = list; } catch(e) { @@ -111,7 +120,7 @@ if (window.__gameMode__) { // 验证当前引用是否有效 try { - var list = window.__gameMode__.m10b_1.j11y(); + var list = window.__gameMode__[FGameModeTankManager][MTankList](); window.__tankList__ = list; } catch(e) { // 引用失效(新战斗/退出战斗),重置等待重新捕获 @@ -150,7 +159,7 @@ console.log('ght 实例未捕获,请等待'); return; } - var list = window.__gameMode__.m10b_1.j11y(); + var list = window.__gameMode__[FGameModeTankManager][MTankList](); var count = list.e1(); console.log('=== 坦克列表 (' + count + " 个) ==="); for (var i = 0; i < count; i++) { @@ -165,7 +174,7 @@ console.log('ght 实例未捕获'); return; } - var list = window.__gameMode__.m10b_1.j11y(); + var list = window.__gameMode__[FGameModeTankManager][MTankList](); var tank = list.g1(idx || 0); console.log('坦克[' + (idx || 0) + ']:', tank); console.log('字段:', Object.keys(tank)); @@ -189,7 +198,7 @@ window.__refreshGameMode__ = function() { if (window.__gameMode__) { try { - var list = window.__gameMode__.m10b_1.j11y(); + var list = window.__gameMode__[FGameModeTankManager][MTankList](); console.log('%c[刷新] ght 引用有效,坦克数:' + list.e1(), 'color:#0f0;font-weight:bold'); return window.__gameMode__; } catch(e) { @@ -242,12 +251,20 @@ // ===== 字段映射总表 ===== // 命名规则:<对象类型>.<语义名> var Fields = { + // ---- 向量(O$)---- + 'Vec.X': 'f20_1', + 'Vec.Y': 'g20_1', + 'Vec.Z': 'h20_1', + // ---- 战斗实体基类(fe / BattleEntity)---- 'Entity.Id': 'iz1_1', // 实体 ID(坦克/战场唯一) + 'Entity.Root': 'qz1_1', // 组件宿主根对象 + 'Entity.ComponentMap': 's2a_1', // 宿主上的组件 Map 'Entity.Components': 'qz1_1.s2a_1', // Map<组件标识, 组件列表>,取值要 valueList.g1(0) // ---- GameMode(ght,window.__gameMode__)---- 'GameMode.Scene': 'lz4()', // 场景实体 + 'GameMode.TankManager': 'm10b_1', // 坦克管理器 'GameMode.TankList': 'm10b_1.j11y()', // 坦克列表(e1() 数量 / g1(i) 取) 'GameMode.OwnedTank': 'u10b_1', // 本地玩家拥有的坦克(onTankPossessed) 'GameMode.ObservedTank': 't10b_1', // 当前观察目标(死亡/观战时可能指向敌人) @@ -267,6 +284,8 @@ // ---- 坦克物理组件(ejt,组件名 tankPhysics)---- 'Physics.Position': 'bzr_1', // 世界位置向量(f20_1/g20_1/h20_1) + 'Physics.PositionOld1': 'gzh_1', // 旧版位置字段名(兜底候选) + 'Physics.PositionOld2': 'pz7_1', // 更旧位置字段名(兜底候选) 'Physics.Size': 'xzq_1', // 车体半尺寸(**不是位置**) 'Physics.Hull': 'zzq_1', // 车体主碰撞形状(x9j_1 === 'hull-main') 'Physics.Body': 'pbk()', // 物理刚体(jM) @@ -316,17 +335,107 @@ // ---- BP 场景节点(zl)---- 'Node.Name': 'g35_1', 'Node.Matrix': 'p35_1', // 世界矩阵(列主序,[12][13][14] = 平移) + 'Node.MatrixOld': 'n35_1', // 旧版矩阵字段(兜底候选) 'Node.Children': 'r35_1', 'Node.Mesh': 'v3m_1', 'Node.Lod': 'u4x_1', // ---- 相机(场景实体 .jz1_1.iyx_1)---- 'Camera.SceneRef': 'jz1_1', + 'Camera.Object': 'iyx_1', // 主相机对象(挂在场景实体的 jz1_1 上) + 'Camera.Viewport': 'w36_1', // 视口(h29() 宽 / g29() 高) 'Camera.Main': 'jz1_1.iyx_1', 'Camera.Position': 'jz1_1.iyx_1.t35_1', 'Camera.ViewMatrix': 'jz1_1.iyx_1.d37_1', 'Camera.ClipMatrix': 'jz1_1.iyx_1.g37_1', - 'Camera.Fov': 'jz1_1.iyx_1.b37_1' + 'Camera.Fov': 'jz1_1.iyx_1.b37_1', + + // ---- 相机:单段叶子字段(FieldName 只认单段,这里给同一批字段补叶子别名)---- + 'Camera.PositionLeaf': 't35_1', + 'Camera.ViewMatrixLeaf': 'd37_1', + 'Camera.ClipMatrixLeaf': 'g37_1', + 'Camera.FovLeaf': 'b37_1', + 'Camera.PositionNode': 'r35_1', // 旧版相机位置字段(与 Node.Children 同名但不通用) + 'Camera.Angles': 'u35_1', // 欧拉角(pitch/roll/yaw) + 'Camera.Euler': 'u35_1', // 同上(自由视角叫法) + 'Camera.Rotation': 'u35_1', // 同上(沙盘叫法) + 'Camera.Update': 'o37()', // 相机矩阵刷新 + 'Camera.ViewportWidth': 'u36_1', + 'Camera.ViewportHeight': 'v36_1', + 'Camera.RenderWidth': 'u36_1', // 别名 + 'Camera.RenderHeight': 'v36_1', // 别名 + 'Camera.FovValue': 'z36_1', + 'Camera.Near': 'x36_1', + 'Camera.Far': 'y36_1', + 'Camera.SceneFx': 'd75_1', + + // ---- 渲染器 / 绘制流程(module_92980)---- + 'Render.Renderer': 'gyw_1', // 场景渲染器 + 'Render.Drawer': 'd75_1', // Ff 绘制器 + 'Render.DrawBuffer': 'd75_1', // 别名(透视里按"绘制缓冲"称呼) + 'Renderer.Object': 'd75_1', // 别名(沙盘里按"渲染器对象"称呼) + 'Render.Gl': 'w39_1', // GL 包装器 + 'Renderer.Device': 'w39_1', // 别名 + 'Render.GlNative': 'b74_1', // 原生 WebGL2RenderingContext + 'Renderer.Gl': 'b74_1', // 别名 + 'Render.Device': 's3h_1', // 资源侧设备包装 + 'Render.DeviceNative': 'f75_1', // 设备里的原生 GL + 'Render.Settings': 'y38_1', // 渲染开关组 + 'Renderer.Flags': 'y38_1', // 别名 + 'Render.ShadowEnabled': 'e3o_1', + 'Renderer.Shadow': 'e3o_1', // 别名 + 'Render.PostProcessEnabled': 'f3o_1', + 'Renderer.PostFx': 'f3o_1', // 别名 + 'Render.ExtraPassEnabled': 'k3o_1', + 'Renderer.FlagAux': 'k3o_1', // 别名 + 'Render.LayerQueue': 'e39_1', // 渲染层队列(20=Hud 21-24=GUI) + 'Renderer.QueueList': 'e39_1', // 别名 + 'Render.Draw': 'f3t()', // y3s → f3t 主渲染 + 'Render.DrawingWidth': 'o3c()', + 'Render.DrawingHeight': 'n3c()', + 'Scene.RootNode': 'fyx_1', // 场景根节点 + + // ---- 网格资源(Tp / sm)---- + 'Mesh.VertexGroups': 'x4w_1', + 'Mesh.VertexBuffer': 'h3x_1', + 'Mesh.VertexCount': 'w3x_1', + 'Mesh.CpuVertexData': 'x3x_1', + 'Mesh.GpuBuffer': 'v3x_1', + 'Mesh.IndexGroups': 'y4w_1', + 'Mesh.IndexBuffer': 'q4w_1', + 'Mesh.IndexBufferOld': 'u3p_1', + 'Mesh.CpuIndexData': 'p3x_1', + 'Mesh.IndexGpuBuffer': 'n3x_1', + + // ---- 射线检测(碰撞世界的射线接口)---- + 'CollisionWorld.Raycast': 'a17p()', + 'Ray.Cast': 'a17p()', // 别名 + 'Ray.StartPoint': 'y178_1', + 'Ray.HitPoint': 'y178_1', // 别名 + 'Ray.EndPoint': 'z178_1', + 'Ray.HitNormal': 'z178_1', // 别名 + 'Ray.Distance': 'a179_1', + 'Ray.HitEntity': 'q17o_1', + 'Ray.HitShape': 'q17o_1', // 别名(雷达/瞄准把第 4 参数叫 HitShape) + 'Ray.HitBody': 'b179_1', // 第 5 参数 + 'RayFilter.Context': 'l1da_1', + 'RayFilter.Query': 'l1da_1', // 别名 + + // ---- 车体朝向 / 武器(雷达、瞄准警告)---- + 'Tank.HullComponent': 'qzv_1', // 车体朝向组件识别字段 + 'Tank.HullDirection': 'wzl_1', // 车体朝向向量 + 'Tank.StateId': 'k3_1', // 状态枚举数值(与 Team.Id 同名字段,按用途分开) + 'Weapon.Component': 'n11c_1', + 'Weapon.ComponentAlt': 'q112_1', + 'Weapon.TurretRoot': 'm17a_1', + + // ---- 炮塔 / 皮肤槽位 ---- + 'Turret.InnerWrapper': 'e11p_1', + 'Turret.RootNode': 'a17q_1', + 'Skin.SlotHull': 'w14w_1', + 'Skin.SlotHullLegacy': 't14l_1', + 'Skin.SlotHullAlt': 'w14x_1', + 'Skin.SlotHullOld': 'p14l_1' }; // ===== module_87072 的导出键($_.xx),每版都可能平移 ===== @@ -401,6 +510,21 @@ return Out; } + // 自检项(只查核心字段:别名/扩展项不在样本对象上,查了会误报) + var CoreKeys = { + Entity: ['Entity.Id', 'Entity.Root', 'Entity.ComponentMap', 'Entity.Components'], + GameMode: ['GameMode.Scene', 'GameMode.TankManager', 'GameMode.TankList', + 'GameMode.OwnedTank', 'GameMode.ObservedTank', 'GameMode.LocalTeam'], + Tank: ['Tank.Fsm', 'Tank.State', 'Tank.StateValue', 'Tank.IsAlive', 'Tank.TeamSource'], + Physics: ['Physics.Position', 'Physics.Size', 'Physics.Hull', 'Physics.Body', 'Physics.BodyShapes'], + Shape: ['Shape.Name', 'Shape.Category', 'Shape.Mask', 'Shape.Body', 'Body.Owner'], + Skin: ['Skin.HullSkin', 'Skin.HullRoot', 'Skin.Parts', 'Skin.PartNode'], + Turret: ['Turret.Wrapper', 'Turret.GetRoot'], + Node: ['Node.Name', 'Node.Matrix', 'Node.Children', 'Node.Mesh'], + Camera: ['Camera.SceneRef', 'Camera.Main', 'Camera.Position', 'Camera.ViewMatrix', + 'Camera.ClipMatrix', 'Camera.Fov'] + }; + // 用当前战局对象做一次全量自检(控制台调用 __hornetSchema__.SelfCheck()) function SelfCheck() { var Gm = window.__gameMode__; @@ -415,29 +539,36 @@ if (!Tank && TankList && typeof TankList.e1 === 'function' && TankList.e1() > 0) Tank = TankList.g1(0); } catch (e) {} + var Physics = Tank ? FindPhysics(Tank) : null; var Targets = { Entity: Tank || Scene, GameMode: Gm, Tank: Tank, - Physics: Tank ? FindPhysics(Tank) : null, - Shape: null, + Physics: Physics, + Shape: Physics ? Get(Physics, 'Physics.Hull') : null, Skin: Tank, Turret: Tank, - Node: null, + Node: Tank ? Get(Tank, 'Skin.HullRoot') : null, Camera: Scene }; - Object.keys(Targets).forEach(function (Prefix) { - if (!Targets[Prefix]) { Report.Groups[Prefix] = '无样本对象'; return; } - var R = Check(Targets[Prefix], Prefix); - Report.Groups[Prefix] = R; - R.Missing.forEach(function (M) { Report.Missing.push(Prefix + '.' + M); }); + Object.keys(CoreKeys).forEach(function (Prefix) { + var Target = Targets[Prefix]; + if (!Target) { Report.Groups[Prefix] = '无样本对象'; return; } + var Ok = [], Missing = []; + CoreKeys[Prefix].forEach(function (Key) { + var V = Get(Target, Key); + if (V === undefined) Missing.push(Key + ' → ' + PathOf(Key)); + else Ok.push(Key); + }); + Report.Groups[Prefix] = { Ok: Ok, Missing: Missing }; + Missing.forEach(function (M) { Report.Missing.push(Prefix + '.' + M); }); }); if (Report.Missing.length) { console.warn('[字段抽象] 失配 ' + Report.Missing.length + ' 项:', Report.Missing); } else { - console.log('[字段抽象] 全部语义字段解析正常'); + console.log('[字段抽象] 全部核心字段解析正常'); } return Report; } @@ -484,6 +615,11 @@ // 用法:各工具文件通过 var lib = window.__hornetLib__ 访问 (function() { + // ===== 字段抽象层(语义名 → 匿名字段,总表在 hornet_schema.js)===== + var Schema = window.__hornetSchema__ || null; + function Fld(Key, Fallback) { var N = Schema ? Schema.FieldName(Key) : null; return N || Fallback; } + function Mth(Key, Fallback) { var N = Schema ? Schema.MethodName(Key) : null; return N || Fallback; } + console.log('===== 3DTank公共库加载 ====='); if (!window.__gameMode__) { @@ -493,17 +629,58 @@ // ===== 缓存队伍判断函数(ZCt/m2Fn) ===== var ZCt = null, m2Fn = null; - // ===== 字段抽象层引用(字段名统一在 hornet_schema.js 里维护) ===== - // 取不到抽象层时退回本地常量,保证单独加载本文件也能工作 - var Schema = window.__hornetSchema__ || null; - function FieldOf(Key, Fallback) { - if (!Schema) return Fallback; - var P = Schema.PathOf(Key); - return (P && P.indexOf('.') < 0 && P.indexOf('(') < 0) ? P : Fallback; - } - var TEAM_FIELD = FieldOf('Team.Field', 'b135_1'); // 队伍组件上的队伍对象字段 - var TEAM_ID_FIELD = FieldOf('Team.Id', 'k3_1'); // 队伍编号:0/1/2(2=中立) + // ===== 语义字段常量(取不到抽象层时用 fallback 字面名,保证单独加载也能工作) ===== + // 实体 + var FEntityRoot = Fld('Entity.Root', 'qz1_1'); // 组件宿主根对象 + var FEntityComponentMap = Fld('Entity.ComponentMap', 's2a_1'); // 宿主上的组件 Map + var FEntityId = Fld('Entity.Id', 'iz1_1'); // 实体 ID + // 向量 + var FVecX = Fld('Vec.X', 'f20_1'); + var FVecY = Fld('Vec.Y', 'g20_1'); + var FVecZ = Fld('Vec.Z', 'h20_1'); + // 队伍 + var FTeamField = Fld('Team.Field', 'b135_1'); // 队伍组件上的队伍对象字段 + var FTeamId = Fld('Team.Id', 'k3_1'); // 队伍编号:0/1/2(2=中立) var NEUTRAL_TEAM_ID = 2; + // 物理 + var FPhysicsPosition = Fld('Physics.Position', 'bzr_1'); // 世界位置向量 + var FPhysicsPositionOld1 = Fld('Physics.PositionOld1', 'gzh_1'); // 旧版位置字段名(兜底候选) + var FPhysicsPositionOld2 = Fld('Physics.PositionOld2', 'pz7_1'); // 更旧位置字段名(兜底候选) + // 坦克状态 + var FTankFsm = Fld('Tank.Fsm', 'p10c_1'); // 状态机字段(组件识别用) + var FTankState = Fld('Tank.State', 'k10c_1'); // 状态枚举对象 + var FTankStateValue = Fld('Tank.StateValue', 'k3_1'); // 状态数值 + // 用户信息 / 血量 / 装备分 + var FUserName = Fld('User.Name', 'c12n_1'); + var FUserClan = Fld('User.Clan', 'e12n_1'); + var FHealthMax = Fld('Health.Max', 'p12o_1'); + var FHealthCurrent = Fld('Health.Current', 'q12o_1'); + var FGearScore = Fld('Gear.Score', 'cjf_1'); + // GameMode + var FGameModeTankManager = Fld('GameMode.TankManager', 'm10b_1'); + var FGameModeOwnedTank = Fld('GameMode.OwnedTank', 'u10b_1'); + var FGameModeObservedTank = Fld('GameMode.ObservedTank', 't10b_1'); + var FGameModeLocalTeam = Fld('GameMode.LocalTeam', 'v10b_1'); + // 相机 + var FCameraSceneRef = Fld('Camera.SceneRef', 'jz1_1'); + var FCameraObject = Fld('Camera.Object', 'iyx_1'); + var FCameraPosition = Fld('Camera.Position', 't35_1'); + var FCameraViewMatrix = Fld('Camera.ViewMatrix', 'd37_1'); + var FCameraClipMatrix = Fld('Camera.ClipMatrix', 'g37_1'); + var FCameraViewport = Fld('Camera.Viewport', 'w36_1'); + var FCameraFov = Fld('Camera.Fov', 'b37_1'); + // 射线检测结果 / 过滤器 + var FRayStartPoint = Fld('Ray.StartPoint', 'y178_1'); + var FRayEndPoint = Fld('Ray.EndPoint', 'z178_1'); + var FRayDistance = Fld('Ray.Distance', 'a179_1'); + var FRayHitEntity = Fld('Ray.HitEntity', 'q17o_1'); + var FRayHitShape = Fld('Ray.HitShape', 'b179_1'); + var FRayFilterContext = Fld('RayFilter.Context', 'l1da_1'); + // 方法 + var MGameModeScene = Mth('GameMode.Scene', 'lz4'); // 场景实体 + var MTankList = Mth('GameMode.TankList', 'j11y'); // 坦克列表 + var MIsTankAlive = Mth('Tank.IsAlive', 's14j'); // 引擎可见性判定 + var MRaycast = Mth('Ray.Cast', 'a17p'); // 全局射线检测 // 加载队伍判断函数(从 module_87072 导出) // 新旧差异(main.7cfa4058): @@ -537,9 +714,9 @@ // 遍历 entity.wyj_1.r2a_1(Map<组件标识, 组件列表>),value 是列表类(s_),g1(0) 获取实际组件 // 注意:用 !== undefined 检查字段存在性,避免 falsy 值(0/""/null/false)漏判 function FindComponentByField(entity, fieldName) { - if (!entity || !entity.qz1_1 || !entity.qz1_1.s2a_1) return null; + if (!entity || !entity[FEntityRoot] || !entity[FEntityRoot][FEntityComponentMap]) return null; var result = null; - entity.qz1_1.s2a_1.forEach(function(valueList, key) { + entity[FEntityRoot][FEntityComponentMap].forEach(function(valueList, key) { if (result) return; try { var comp = (valueList && typeof valueList.g1 === 'function') ? valueList.g1(0) : valueList; @@ -557,18 +734,18 @@ // njt() 里 `bzr_1.l27(上一物理态位置, 当前物理态位置, 插值)` —— bzr_1 就是世界位置。 // ⚠ xzq_1 是车体半尺寸(用于包围盒/出生点:`-xzq_1.h20_1/2 + 10`),绝不能当位置用。 // 返回 {x, y, z},x/y 水平面,z 高度 - var POS_FIELDS = ['bzr_1', 'gzh_1', 'pz7_1']; + var POS_FIELDS = [FPhysicsPosition, FPhysicsPositionOld1, FPhysicsPositionOld2]; function GetTankPosition(tank) { for (var i = 0; i < POS_FIELDS.length; i++) { var Key = POS_FIELDS[i]; var Comp = null; try { Comp = FindComponentByField(tank, Key); } catch (e) { Comp = null; } var V = Comp && Comp[Key]; - if (V && typeof V.f20_1 === 'number' && typeof V.g20_1 === 'number' && typeof V.h20_1 === 'number') { + if (V && typeof V[FVecX] === 'number' && typeof V[FVecY] === 'number' && typeof V[FVecZ] === 'number') { // 数值有效性校验:NaN/超大值一律视为读取失败(避免雷达画到画布外) - if (isFinite(V.f20_1) && isFinite(V.g20_1) && isFinite(V.h20_1) && - Math.abs(V.f20_1) < 1e7 && Math.abs(V.g20_1) < 1e7 && Math.abs(V.h20_1) < 1e7) { - return { x: V.f20_1, y: V.g20_1, z: V.h20_1 }; + if (isFinite(V[FVecX]) && isFinite(V[FVecY]) && isFinite(V[FVecZ]) && + Math.abs(V[FVecX]) < 1e7 && Math.abs(V[FVecY]) < 1e7 && Math.abs(V[FVecZ]) < 1e7) { + return { x: V[FVecX], y: V[FVecY], z: V[FVecZ] }; } } } @@ -578,13 +755,13 @@ // ===== 坦克组件(lxt / TankComponent)===== // 特征:有 k10c_1(状态枚举)与 p10c_1(fsm) function GetTankComponent(tank) { - if (!tank || !tank.qz1_1 || !tank.qz1_1.s2a_1) return null; + if (!tank || !tank[FEntityRoot] || !tank[FEntityRoot][FEntityComponentMap]) return null; var Found = null; - tank.qz1_1.s2a_1.forEach(function(valueList, key) { + tank[FEntityRoot][FEntityComponentMap].forEach(function(valueList, key) { if (Found) return; try { var comp = (valueList && typeof valueList.g1 === 'function') ? valueList.g1(0) : valueList; - if (comp && comp.p10c_1 && comp.k10c_1 !== undefined) Found = comp; + if (comp && comp[FTankFsm] && comp[FTankState] !== undefined) Found = comp; } catch(e) {} }); return Found; @@ -594,7 +771,7 @@ // 状态枚举:0=DEAD, 1=DEAD_PHANTOM, 2=SEMI_ACTIVE, 3=ACTIVE, 4=DISABLED, 5=TELEPORT function GetTankState(tank) { var C = GetTankComponent(tank); - return (C && C.k10c_1) ? C.k10c_1.k3_1 : -1; + return (C && C[FTankState]) ? C[FTankState][FTankStateValue] : -1; } // 可见/战斗中的坦克:**直接调用引擎自己的判定 s14j()** @@ -602,7 +779,7 @@ // 旧实现只认 2/3,会漏掉传送态;死亡态 0/1/4 会被正确排除 function IsTankAlive(tank) { var C = GetTankComponent(tank); - try { if (C && typeof C.s14j === 'function') return !!C.s14j(); } catch (e) {} + try { if (C && typeof C[MIsTankAlive] === 'function') return !!C[MIsTankAlive](); } catch (e) {} var s = GetTankState(tank); return s === 2 || s === 3 || s === 5; } @@ -610,20 +787,20 @@ // ===== 获取坦克名称(yot 用户信息组件) ===== // s120_1 = userName, u120_1 = clanTag(可为null), t120_1 = userRank function GetTankName(tank) { - var userComp = FindComponentByField(tank, 'c12n_1'); + var userComp = FindComponentByField(tank, FUserName); if (!userComp) return null; - var name = userComp.c12n_1 || ''; - var clanTag = userComp.e12n_1 || ''; + var name = userComp[FUserName] || ''; + var clanTag = userComp[FUserClan] || ''; return clanTag ? (clanTag + ' ' + name) : name; } // ===== 获取坦克血量(Vkt 血量组件) ===== // f122_1 = 最大血量(max), g122_1 = 当前血量(current, 死亡时=0) function GetTankHealth(tank) { - var healthComp = FindComponentByField(tank, 'q12o_1'); + var healthComp = FindComponentByField(tank, FHealthCurrent); if (!healthComp) return null; - var max = healthComp.p12o_1 || 0; - var cur = healthComp.q12o_1 || 0; + var max = healthComp[FHealthMax] || 0; + var cur = healthComp[FHealthCurrent] || 0; if (max <= 0) return null; return { current: cur, max: max, percent: cur / max }; } @@ -631,9 +808,9 @@ // ===== 装备分数(战斗 GS:BattleGearScoreCC.score 落在组件的 cjf_1 上) ===== // 来源:module_85183 的 BattleGearScoreCC(score = cjf_1);战斗内每辆坦克各有一份 function GetGearScore(tank) { - var c = FindComponentByField(tank, 'cjf_1'); - if (!c || typeof c.cjf_1 !== 'number') return null; - return c.cjf_1; + var c = FindComponentByField(tank, FGearScore); + if (!c || typeof c[FGearScore] !== 'number') return null; + return c[FGearScore]; } // ===== 人机识别 ===== @@ -646,10 +823,10 @@ var MaxScore = typeof opt.maxScore === 'number' ? opt.maxScore : 8000; BotSet = new Set(); var gm = window.__gameMode__; - if (!gm || !gm.m10b_1) return BotSet; + if (!gm || !gm[FGameModeTankManager]) return BotSet; var playerTank = GetPlayerTank(gm); var list, n; - try { list = gm.m10b_1.j11y(); n = list.e1(); } catch (e) { return BotSet; } + try { list = gm[FGameModeTankManager][MTankList](); n = list.e1(); } catch (e) { return BotSet; } var Groups = { ally: {}, enemy: {} }; // 组 → { 分数 → [坦克...] } for (var i = 0; i < n; i++) { var tank = list.g1(i); @@ -677,9 +854,9 @@ var g = window.__gameMode__; R.gm = !!g; if (!g) return R; - try { R.scene = !!g.lz4().jz1_1; } catch (e) { R.errors.push('scene: ' + e.message); } + try { R.scene = !!g[MGameModeScene]()[FCameraSceneRef]; } catch (e) { R.errors.push('scene: ' + e.message); } var list = null; - try { list = g.m10b_1.j11y(); R.tanks = list.e1(); } catch (e) { R.errors.push('tankList: ' + e.message); } + try { list = g[FGameModeTankManager][MTankList](); R.tanks = list.e1(); } catch (e) { R.errors.push('tankList: ' + e.message); } if (list) { var N = Math.min(list.e1(), 8); for (var i = 0; i < N; i++) { @@ -693,7 +870,7 @@ for (var j = 0; j < POS_FIELDS.length; j++) { var K = POS_FIELDS[j], C = null; try { C = FindComponentByField(T, K); } catch (e) { C = null; } - if (C && C[K] && typeof C[K].f20_1 === 'number') { Src = K; break; } + if (C && C[K] && typeof C[K][FVecX] === 'number') { Src = K; break; } } R.posHit[Src] = (R.posHit[Src] || 0) + 1; } else { @@ -701,13 +878,13 @@ // 失败时把该坦克组件的字段名与"候选向量"打印出来,便于定位 try { var Fields = []; - T.qz1_1.s2a_1.forEach(function (VL) { + T[FEntityRoot][FEntityComponentMap].forEach(function (VL) { var C = (VL && typeof VL.g1 === 'function') ? VL.g1(0) : VL; if (!C) return; for (var K2 in C) { var V2 = C[K2]; if (V2 && typeof V2 === 'object') { - var Kind = (typeof V2.f20_1 === 'number') ? 'O$' : (typeof V2.length === 'number' && V2.length >= 15 ? 'M' : ''); + var Kind = (typeof V2[FVecX] === 'number') ? 'O$' : (typeof V2.length === 'number' && V2.length >= 15 ? 'M' : ''); if (Kind) Fields.push(K2 + ':' + Kind); } } @@ -728,12 +905,12 @@ // checkPosition: 是否检查位置有效性(默认 true) function GetPlayerTank(gm, checkPosition) { if (checkPosition === undefined) checkPosition = true; - var t = gm.u10b_1; + var t = gm[FGameModeOwnedTank]; if (t && (!checkPosition || GetTankPosition(t))) return t; - t = gm.t10b_1; + t = gm[FGameModeObservedTank]; if (t && (!checkPosition || GetTankPosition(t))) return t; try { - var tl = gm.m10b_1.j11y(); + var tl = gm[FGameModeTankManager][MTankList](); if (tl.e1() > 0) { t = tl.g1(0); if (t && (!checkPosition || GetTankPosition(t))) return t; @@ -758,8 +935,8 @@ if (ZCt) { try { var T = ZCt(tank); if (T) return T; } catch (e) {} } - var Comp = FindComponentByField(tank, TEAM_FIELD); - return Comp ? Comp[TEAM_FIELD] : null; + var Comp = FindComponentByField(tank, FTeamField); + return Comp ? Comp[FTeamField] : null; } // 读队伍编号:0 / 1 = 两个阵营,2 = 中立(q$());读不到返回 -1 @@ -767,7 +944,7 @@ // (module_87072 里 P7()/$ct() 都按 k3_1 分派) function GetTeamId(Team) { if (!Team) return -1; - var Id = Team[TEAM_ID_FIELD]; + var Id = Team[FTeamId]; return (typeof Id === 'number') ? Id : -1; } @@ -776,9 +953,9 @@ function RememberLocalTeam(gm) { var t = null; - try { t = gm.u10b_1; } catch (e) { t = null; } + try { t = gm[FGameModeOwnedTank]; } catch (e) { t = null; } if (t) { - var Id = t.iz1_1; + var Id = t[FEntityId]; if (LocalTeam && LocalTeamTankId === Id) return; var Team = ReadTeamRaw(t); var TeamId = GetTeamId(Team); @@ -788,7 +965,7 @@ // u10b_1 为空(死亡/观战):保持已有缓存;若还没缓存过,再试 GameMode 上的本地队伍字段 if (LocalTeam) return; try { - var G = gm.v10b_1; + var G = gm[FGameModeLocalTeam]; var GId = GetTeamId(G); if (GId >= 0 && GId !== NEUTRAL_TEAM_ID) LocalTeam = G; } catch (e) {} @@ -814,7 +991,7 @@ // 可靠基准:本地拥有坦克的队伍(缓存)→ GameMode 上的本地队伍字段 // 观战目标(t10b_1)可能是敌人,绝不作为基准 var BaseTeam = LocalTeam; - if (!BaseTeam && gm) { try { BaseTeam = gm.v10b_1; } catch (e) {} } + if (!BaseTeam && gm) { try { BaseTeam = gm[FGameModeLocalTeam]; } catch (e) {} } var BaseId = GetTeamId(BaseTeam); if (BaseId < 0 || BaseId === NEUTRAL_TEAM_ID) { var Last2 = TankFriendCache.get(tank); // 基准不可靠(极早期):沿用上次 @@ -826,7 +1003,7 @@ if (!TeamJudgeLogged.has(tank)) { TeamJudgeLogged.add(tank); try { - console.log('[敌我] id=' + tank.iz1_1 + ' 队伍=' + TankId + ' 本地=' + BaseId + + console.log('[敌我] id=' + tank[FEntityId] + ' 队伍=' + TankId + ' 本地=' + BaseId + ' → ' + (Friendly ? '友方' : '敌方') + ' name=' + (GetTankName(tank) || '?')); } catch (e) {} } @@ -847,12 +1024,12 @@ // heading 供雷达使用(forward = (sin(heading), cos(heading)),水平面) function GetCameraInfo(gm) { try { - var scene = gm.lz4().jz1_1; - var cam = scene && scene.iyx_1; - if (!cam || !cam.t35_1) return null; - var P = cam.t35_1; - var pos = { x: P.f20_1, y: P.g20_1, z: P.h20_1 }; - var V = cam.d37_1; + var scene = gm[MGameModeScene]()[FCameraSceneRef]; + var cam = scene && scene[FCameraObject]; + if (!cam || !cam[FCameraPosition]) return null; + var P = cam[FCameraPosition]; + var pos = { x: P[FVecX], y: P[FVecY], z: P[FVecZ] }; + var V = cam[FCameraViewMatrix]; var fx = 0, fy = 1, fz = 0; if (V && typeof V.length === 'number' && V.length >= 11) { fx = -V[2]; fy = -V[6]; fz = -V[10]; @@ -860,14 +1037,14 @@ fx /= len; fy /= len; fz /= len; } var viewW = 0, viewH = 0; - try { viewW = cam.w36_1.h29(); viewH = cam.w36_1.g29(); } catch (e) {} + try { viewW = cam[FCameraViewport].h29(); viewH = cam[FCameraViewport].g29(); } catch (e) {} return { pos: pos, yaw: Math.atan2(-fx, fy), pitch: Math.asin(Math.max(-1, Math.min(1, fz))), heading: Math.atan2(fx, fy), - fov: cam.b37_1 || 1.0471976, - matrix: cam.g37_1 || null, + fov: cam[FCameraFov] || 1.0471976, + matrix: cam[FCameraClipMatrix] || null, viewW: viewW, viewH: viewH }; @@ -880,14 +1057,14 @@ // g16x(startVec, dirVec, maxDist, resultObj, filterObj) = 全局射线检测 function GetUMtComponent(gm) { try { - var gmEntity = gm.lz4(); - if (!gmEntity || !gmEntity.qz1_1 || !gmEntity.qz1_1.s2a_1) return null; + var gmEntity = gm[MGameModeScene](); + if (!gmEntity || !gmEntity[FEntityRoot] || !gmEntity[FEntityRoot][FEntityComponentMap]) return null; var uMt = null; - gmEntity.qz1_1.s2a_1.forEach(function(valueList, key) { + gmEntity[FEntityRoot][FEntityComponentMap].forEach(function(valueList, key) { if (uMt) return; try { var comp = (valueList && typeof valueList.g1 === 'function') ? valueList.g1(0) : valueList; - if (comp && typeof comp.a17p === 'function') uMt = comp; + if (comp && typeof comp[MRaycast] === 'function') uMt = comp; } catch(e) {} }); return uMt; @@ -899,9 +1076,9 @@ // ===== 构造 mock O$ 向量(用于射线检测接口) ===== function MkVec(x, y, z) { return { - f20_1: x || 0, g20_1: y || 0, h20_1: z || 0, - k26: function(t) { this.f20_1 = t.f20_1; this.g20_1 = t.g20_1; this.h20_1 = t.h20_1; }, - n23: function(x, y, z) { this.f20_1 = x; this.g20_1 = y; this.h20_1 = z; return this; } + [FVecX]: x || 0, [FVecY]: y || 0, [FVecZ]: z || 0, + k26: function(t) { this[FVecX] = t[FVecX]; this[FVecY] = t[FVecY]; this[FVecZ] = t[FVecZ]; }, + n23: function(x, y, z) { this[FVecX] = x; this[FVecY] = y; this[FVecZ] = z; return this; } }; } @@ -914,28 +1091,28 @@ var startVec = MkVec(fromPos.x, fromPos.y, fromPos.z); var dirVec = MkVec(dirX, dirY, dirZ); var result = { - y178_1: MkVec(0, 0, 0), - z178_1: MkVec(0, 0, 0), - a179_1: maxDist, - q17o_1: null, - b179_1: null, + [FRayStartPoint]: MkVec(0, 0, 0), + [FRayEndPoint]: MkVec(0, 0, 0), + [FRayDistance]: maxDist, + [FRayHitEntity]: null, + [FRayHitShape]: null, r17o: function(t, n, i, r, e) { - this.y178_1.k26(t); - this.z178_1.k26(n); - this.a179_1 = i; - this.q17o_1 = r; - this.b179_1 = e; + this[FRayStartPoint].k26(t); + this[FRayEndPoint].k26(n); + this[FRayDistance] = i; + this[FRayHitEntity] = r; + this[FRayHitShape] = e; }, - d3: function() { this.b179_1 = null; } + d3: function() { this[FRayHitShape] = null; } }; var filter = { - l1da_1: { h1: function() { return false; }, d3: function() {} }, + [FRayFilterContext]: { h1: function() { return false; }, d3: function() {} }, w179: function() { return true; }, - d3: function() { this.l1da_1.d3(); }, + d3: function() { this[FRayFilterContext].d3(); }, m1da: function() {} }; - var hit = uMt.a17p(startVec, dirVec, maxDist, result, filter); - return (hit && result.a179_1 < maxDist) ? result.a179_1 : -1; + var hit = uMt[MRaycast](startVec, dirVec, maxDist, result, filter); + return (hit && result[FRayDistance] < maxDist) ? result[FRayDistance] : -1; } catch(e) { return -1; } @@ -1144,6 +1321,29 @@ // 功能:在屏幕右上角显示2D雷达,显示所有坦克的相对位置 (function() { + // ===== 字段抽象层(语义名 → 匿名字段,总表在 hornet_schema.js)===== + var Schema = window.__hornetSchema__ || null; + function Fld(Key, Fallback) { var N = Schema ? Schema.FieldName(Key) : null; return N || Fallback; } + function Mth(Key, Fallback) { var N = Schema ? Schema.MethodName(Key) : null; return N || Fallback; } + + // ===== 匿名字段常量 ===== + var FVecX = Fld('Vec.X', 'f20_1'); + var FVecY = Fld('Vec.Y', 'g20_1'); + var FVecZ = Fld('Vec.Z', 'h20_1'); + var FEntityId = Fld('Entity.Id', 'iz1_1'); + var FEntityRoot = Fld('Entity.Root', 'qz1_1'); + var FEntityComponentMap = Fld('Entity.ComponentMap', 's2a_1'); + var FTankHullComponent = Fld('Tank.HullComponent', 'qzv_1'); + var FTankHullDirection = Fld('Tank.HullDirection', 'wzl_1'); + var MGameModeScene = Mth('GameMode.Scene', 'lz4'); + var MCollisionRaycast = Mth('CollisionWorld.Raycast', 'a17p'); + var FRayHitPoint = Fld('Ray.HitPoint', 'y178_1'); + var FRayHitNormal = Fld('Ray.HitNormal', 'z178_1'); + var FRayDistance = Fld('Ray.Distance', 'a179_1'); + var FRayHitShape = Fld('Ray.HitShape', 'q17o_1'); + var FRayHitBody = Fld('Ray.HitBody', 'b179_1'); + var FRayFilterQuery = Fld('RayFilter.Query', 'l1da_1'); + function _start() { console.log('===== 3DTank雷达启动 ====='); @@ -1179,12 +1379,12 @@ // ===== 获取坦克方向(Kkt 组件的 dzc_1) ===== function getTankDirection(tank) { - var hullComp = FindComponentByField(tank, 'qzv_1'); - if (hullComp && hullComp.wzl_1) { + var hullComp = FindComponentByField(tank, FTankHullComponent); + if (hullComp && hullComp[FTankHullDirection]) { return { - x: hullComp.wzl_1.f20_1, - y: hullComp.wzl_1.g20_1, - z: hullComp.wzl_1.h20_1 + x: hullComp[FTankHullDirection][FVecX], + y: hullComp[FTankHullDirection][FVecY], + z: hullComp[FTankHullDirection][FVecZ] }; } return null; @@ -1223,13 +1423,13 @@ function getUMtComponent() { if (UMtComponent) return UMtComponent; try { - var gmEntity = gm.lz4(); - if (!gmEntity || !gmEntity.qz1_1 || !gmEntity.qz1_1.s2a_1) return null; - gmEntity.qz1_1.s2a_1.forEach(function(valueList, key) { + var gmEntity = gm[MGameModeScene](); + if (!gmEntity || !gmEntity[FEntityRoot] || !gmEntity[FEntityRoot][FEntityComponentMap]) return null; + gmEntity[FEntityRoot][FEntityComponentMap].forEach(function(valueList, key) { if (UMtComponent) return; try { var comp = (valueList && typeof valueList.g1 === 'function') ? valueList.g1(0) : valueList; - if (comp && typeof comp.a17p === 'function') UMtComponent = comp; + if (comp && typeof comp[MCollisionRaycast] === 'function') UMtComponent = comp; } catch(e) {} }); if (UMtComponent) console.log('[雷达] uMt 碰撞检测器已获取'); @@ -1251,21 +1451,21 @@ var startVec = MkVec(fromPos.x, fromPos.y, fromPos.z); var dirVec = MkVec(dirX, dirY, dirZ); var result = { - y178_1: MkVec(0,0,0), z178_1: MkVec(0,0,0), - a179_1: maxDist, q17o_1: null, b179_1: null, + [FRayHitPoint]: MkVec(0,0,0), [FRayHitNormal]: MkVec(0,0,0), + [FRayDistance]: maxDist, [FRayHitShape]: null, [FRayHitBody]: null, r17o: function(t, n, i, r, e) { - this.y178_1.k26(t); this.z178_1.k26(n); - this.a179_1 = i; this.q17o_1 = r; this.b179_1 = e; + this[FRayHitPoint].k26(t); this[FRayHitNormal].k26(n); + this[FRayDistance] = i; this[FRayHitShape] = r; this[FRayHitBody] = e; }, - d3: function() { this.b179_1 = null; } + d3: function() { this[FRayHitBody] = null; } }; var filter = { - l1da_1: { h1: function() { return false; }, d3: function() {} }, + [FRayFilterQuery]: { h1: function() { return false; }, d3: function() {} }, w179: function() { return true; }, d3: function() {}, m1da: function() {} }; - var hit = uMt.a17p(startVec, dirVec, maxDist, result, filter); - return (hit && result.a179_1 < maxDist) ? result.a179_1 : -1; + var hit = uMt[MCollisionRaycast](startVec, dirVec, maxDist, result, filter); + return (hit && result[FRayDistance] < maxDist) ? result[FRayDistance] : -1; } catch(e) { return -1; } @@ -1830,7 +2030,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.iz1_1 : '?'), 5, 24); + ctx.fillText('Player: ' + (playerTank ? playerTank[FEntityId] : '?'), 5, 24); } else { ctx.fillText('Player: NOT FOUND', 5, 24); } @@ -1925,6 +2125,39 @@ var FSkinHullRoot = Fld('Skin.HullRoot', 'h159_1'); // 车身渲染树根 var FSkinParts = Fld('Skin.Parts', 'g17r_1'); // 部件表 var FSkinPartNode = Fld('Skin.PartNode', 'j17r_1'); // 部件 → BP 节点 + var FNodeMatrixOld = Fld('Node.MatrixOld', 'n35_1'); // 旧版世界矩阵(兜底) + + // 实体 / 向量 / 战斗实例 + var FEntityId = Fld('Entity.Id', 'iz1_1'); // 实体 ID + var FEntityRoot = Fld('Entity.Root', 'qz1_1'); // 组件宿主根对象 + var FEntityComponentMap = Fld('Entity.ComponentMap', 's2a_1'); // 宿主上的组件 Map + var FVecX = Fld('Vec.X', 'f20_1'); // 向量 X + var FVecY = Fld('Vec.Y', 'g20_1'); // 向量 Y + var FVecZ = Fld('Vec.Z', 'h20_1'); // 向量 Z + var FGameModeTankManager = Fld('GameMode.TankManager', 'm10b_1'); // 坦克管理器 + var FTeamId = Fld('Team.Id', 'k3_1'); // 队伍编号 + + // 炮塔皮肤(LUt / CUt 包装) + var FTurretWrapper = Fld('Turret.Wrapper', 'a11p_1'); // 炮塔组件包装对象 + var FTurretInnerWrapper = Fld('Turret.InnerWrapper', 'e11p_1'); // 包装内的 CUt 包装 + var FTurretRootNode = Fld('Turret.RootNode', 'a17q_1'); // 炮塔 BP 根节点 + + // mesh 资源(Tp / J$ / Y$) + var FMeshVertexGroups = Fld('Mesh.VertexGroups', 'x4w_1'); // 顶点组列表 + var FMeshVertexBuffer = Fld('Mesh.VertexBuffer', 'h3x_1'); // 顶点缓冲资源 + var FMeshGpuBuffer = Fld('Mesh.GpuBuffer', 'v3x_1'); // GPU 缓冲包装 + var FMeshVertexCount = Fld('Mesh.VertexCount', 'w3x_1'); // 顶点数 + var FMeshCpuVertexData = Fld('Mesh.CpuVertexData', 'x3x_1'); // CPU 端顶点原始数据 + var FMeshIndexGroups = Fld('Mesh.IndexGroups', 'y4w_1'); // 索引组列表 + var FMeshIndexBuffer = Fld('Mesh.IndexBuffer', 'q4w_1'); // 索引缓冲资源 + var FMeshIndexBufferOld = Fld('Mesh.IndexBufferOld', 'u3p_1'); // 旧版索引缓冲字段 + var FMeshCpuIndexData = Fld('Mesh.CpuIndexData', 'p3x_1'); // CPU 端索引原始数据 + var FMeshIndexGpuBuffer = Fld('Mesh.IndexGpuBuffer', 'n3x_1'); // 索引 GPU 缓冲包装 + + // 渲染设备 / 原生缓冲 + var FRenderDevice = Fld('Render.Device', 's3h_1'); // GL 包装器 + var FRenderDeviceNative = Fld('Render.DeviceNative', 'f75_1'); // 原生 WebGL2RenderingContext + var FRenderDrawBuffer = Fld('Render.DrawBuffer', 'd75_1'); // 原生 WebGLBuffer // ===== 当前模式 ===== var Mode = 'outline'; // 'wireframe' | 'outline' @@ -2137,11 +2370,11 @@ var Cached = SkinCompCache.get(entity); if (Cached) return Cached; var comp = FindComponentByField(entity, FSkinHullSkin); - if (!comp) comp = FindComponentByField(entity, 'a17q_1'); + if (!comp) comp = FindComponentByField(entity, FTurretRootNode); // 兜底:按结构找 —— 组件字段值是"皮肤对象"(含 g17r_1 部件表) if (!comp) { try { - entity.qz1_1.s2a_1.forEach(function (VL) { + entity[FEntityRoot][FEntityComponentMap].forEach(function (VL) { if (comp) return; var C = (VL && typeof VL.g1 === 'function') ? VL.g1(0) : VL; if (!C) return; @@ -2168,13 +2401,13 @@ // 炮塔 BP 根节点 // 新版:weaponSkin 组件(类 LUt,构造首字段 a11p_1)里 e11p_1 = CUt 包装,其 a17q_1 = 炮塔节点 function GetTurretNode(entity) { - var wp = FindComponentByField(entity, 'a11p_1'); + var wp = FindComponentByField(entity, FTurretWrapper); if (wp) { - if (wp.e11p_1 && wp.e11p_1.a17q_1) return wp.e11p_1.a17q_1; + if (wp[FTurretInnerWrapper] && wp[FTurretInnerWrapper][FTurretRootNode]) return wp[FTurretInnerWrapper][FTurretRootNode]; if (typeof wp.u11p === 'function') { try { return wp.u11p(); } catch (e) {} } } var comp = GetSkinComponent(entity); - if (comp && comp.a17q_1) return comp.a17q_1; + if (comp && comp[FTurretRootNode]) return comp[FTurretRootNode]; return null; } @@ -2201,8 +2434,8 @@ // ⚠ 旧字段 d3x_1 / s3h_1.b74_1 / v3x_1.z73_1 在新版分别是"渲染参数 / 雾色 uniform / uniform 槽位",全错 function GetGlFromResource(Res) { if (!Res) return null; - var Dev = Res.s3h_1; - return (Dev && Dev.f75_1) ? Dev.f75_1 : null; + var Dev = Res[FRenderDevice]; + return (Dev && Dev[FRenderDeviceNative]) ? Dev[FRenderDeviceNative] : null; } // 把引擎的 CPU 端缓冲区包装转成 TypedArray @@ -2235,17 +2468,17 @@ } function ReadVertexPositionsFromTp(tp) { - if (!tp || !tp.x4w_1 || typeof tp.x4w_1.e1 !== 'function' || tp.x4w_1.e1() === 0) return null; - var group = tp.x4w_1.g1(0); + if (!tp || !tp[FMeshVertexGroups] || typeof tp[FMeshVertexGroups].e1 !== 'function' || tp[FMeshVertexGroups].e1() === 0) return null; + var group = tp[FMeshVertexGroups].g1(0); if (!group) return null; - var Buf = group.h3x_1; // J$ + var Buf = group[FMeshVertexBuffer]; // J$ if (!Buf) return null; // ① 优先 CPU 端原始数据:未上传 GPU 的 mesh 也能读到(v3x_1.d75_1 此时为 null) - var Cpu = ToTypedArray(Buf.x3x_1, 'f32'); + var Cpu = ToTypedArray(Buf[FMeshCpuVertexData], 'f32'); if (Cpu && Cpu.length) return Cpu; // ② GPU 回读(引擎已上传并释放 CPU 数据时) var gl = GetGlFromResource(Buf); - var webglBuf = Buf.v3x_1 && Buf.v3x_1.d75_1; + var webglBuf = Buf[FMeshGpuBuffer] && Buf[FMeshGpuBuffer][FRenderDrawBuffer]; if (!gl || !webglBuf) return null; gl.bindBuffer(gl.ARRAY_BUFFER, webglBuf); var bufSize = gl.getBufferParameter(gl.ARRAY_BUFFER, gl.BUFFER_SIZE); @@ -2258,21 +2491,21 @@ // ===== 从GPU读取索引数据 ===== // y4w_1 = 索引列表,每项 { q4w_1: 索引缓冲资源 Y$ }(旧版是 il.u3p_1) function ReadIndicesFromTp(tp) { - if (!tp.y4w_1 || typeof tp.y4w_1.e1 !== 'function') return null; - var meshCount = tp.y4w_1.e1(); + if (!tp[FMeshIndexGroups] || typeof tp[FMeshIndexGroups].e1 !== 'function') return null; + var meshCount = tp[FMeshIndexGroups].e1(); if (meshCount === 0) return null; var allIndices = []; for (var m = 0; m < meshCount; m++) { - var il = tp.y4w_1.g1(m); + var il = tp[FMeshIndexGroups].g1(m); if (!il) continue; - var IdxBuf = il.q4w_1 || il.u3p_1; + var IdxBuf = il[FMeshIndexBuffer] || il[FMeshIndexBufferOld]; if (!IdxBuf) continue; // ① CPU 端索引数据(Y$.p3x_1),未上传时也能用 - var Cpu = ToTypedArray(IdxBuf.p3x_1, 'u16'); + var Cpu = ToTypedArray(IdxBuf[FMeshCpuIndexData], 'u16'); if (Cpu && Cpu.length) { allIndices.push(Cpu); continue; } // ② GPU 回读 var gl = GetGlFromResource(IdxBuf); - var webglBuf = IdxBuf.n3x_1 && IdxBuf.n3x_1.d75_1; + var webglBuf = IdxBuf[FMeshIndexGpuBuffer] && IdxBuf[FMeshIndexGpuBuffer][FRenderDrawBuffer]; if (!gl || !webglBuf) continue; gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, webglBuf); var bufSize = gl.getBufferParameter(gl.ELEMENT_ARRAY_BUFFER, gl.BUFFER_SIZE); @@ -2308,8 +2541,8 @@ var stride = 0; // 优先用引擎记录的顶点数反推 stride(J$.w3x_1 = 顶点数) try { - var G0 = tp.x4w_1.g1(0); - var Vc = G0 && G0.h3x_1 && G0.h3x_1.w3x_1; + var G0 = tp[FMeshVertexGroups].g1(0); + var Vc = G0 && G0[FMeshVertexBuffer] && G0[FMeshVertexBuffer][FMeshVertexCount]; if (Vc > 0 && positions.length % Vc === 0) stride = Math.round(positions.length / Vc); } catch (e) {} if (!stride || stride < 3 || stride > 20) { @@ -2337,23 +2570,23 @@ GpuFailLogged.add(tp); var D = {}; try { - var G = tp.x4w_1 && typeof tp.x4w_1.g1 === 'function' ? tp.x4w_1.g1(0) : null; - var B = G && G.h3x_1; - var Dev = B && B.s3h_1; - var I = tp.y4w_1 && typeof tp.y4w_1.e1 === 'function' && tp.y4w_1.e1() > 0 ? tp.y4w_1.g1(0) : null; - var IB = I && (I.q4w_1 || I.u3p_1); + var G = tp[FMeshVertexGroups] && typeof tp[FMeshVertexGroups].g1 === 'function' ? tp[FMeshVertexGroups].g1(0) : null; + var B = G && G[FMeshVertexBuffer]; + var Dev = B && B[FRenderDevice]; + var I = tp[FMeshIndexGroups] && typeof tp[FMeshIndexGroups].e1 === 'function' && tp[FMeshIndexGroups].e1() > 0 ? tp[FMeshIndexGroups].g1(0) : null; + var IB = I && (I[FMeshIndexBuffer] || I[FMeshIndexBufferOld]); D = { - 顶点组: tp.x4w_1 && tp.x4w_1.e1 ? tp.x4w_1.e1() : -1, + 顶点组: tp[FMeshVertexGroups] && tp[FMeshVertexGroups].e1 ? tp[FMeshVertexGroups].e1() : -1, 顶点缓冲资源: !!B, - 缓冲包装: !!(B && B.v3x_1), - 原生Buffer: !!(B && B.v3x_1 && B.v3x_1.d75_1), + 缓冲包装: !!(B && B[FMeshGpuBuffer]), + 原生Buffer: !!(B && B[FMeshGpuBuffer] && B[FMeshGpuBuffer][FRenderDrawBuffer]), 设备: !!Dev, - 原生GL: !!(Dev && Dev.f75_1), - 顶点数: B ? B.w3x_1 : -1, + 原生GL: !!(Dev && Dev[FRenderDeviceNative]), + 顶点数: B ? B[FMeshVertexCount] : -1, 位置长度: positions ? positions.length : -1, - 索引组: tp.y4w_1 && tp.y4w_1.e1 ? tp.y4w_1.e1() : -1, + 索引组: tp[FMeshIndexGroups] && tp[FMeshIndexGroups].e1 ? tp[FMeshIndexGroups].e1() : -1, 索引资源: !!IB, - 索引Buffer: !!(IB && IB.n3x_1 && IB.n3x_1.d75_1), + 索引Buffer: !!(IB && IB[FMeshIndexGpuBuffer] && IB[FMeshIndexGpuBuffer][FRenderDrawBuffer]), 索引个数: indices ? indices.length : -1 }; } catch (e) {} @@ -2442,7 +2675,7 @@ if (!Node) return null; var M = Node[FNodeMatrix]; if (!M || typeof M.length !== 'number' || M.length < 16) { - M = Node.n35_1; // 旧版矩阵字段 + M = Node[FNodeMatrixOld]; // 旧版矩阵字段 if (!M || typeof M.length !== 'number' || M.length < 16) return null; } return { x: M[12], y: M[13], z: M[14] }; @@ -2509,7 +2742,7 @@ Srcs.push({ Comp: Comp, Path: Path }); } try { - tank.qz1_1.s2a_1.forEach(function (VL) { + tank[FEntityRoot][FEntityComponentMap].forEach(function (VL) { var comp = (VL && typeof VL.g1 === 'function') ? VL.g1(0) : VL; if (!IsPlainObj(comp)) return; for (var K in comp) { @@ -2617,9 +2850,9 @@ function IsNodeValid(bpNode, pos, tankIsMoving, diagStats) { // 世界矩阵:新版是 p35_1(zl 基类构造里 = hl(),s3k/t3k/u3k 读 p35_1[12..14]) // 旧版是 n35_1;新版 n35_1 已变成布尔标志 - var M = bpNode.p35_1; + var M = bpNode[FNodeMatrix]; if (!M || typeof M.length !== 'number' || M.length < 16) { - var OldM = bpNode.n35_1; + var OldM = bpNode[FNodeMatrixOld]; M = (OldM && typeof OldM.length === 'number' && OldM.length >= 16) ? OldM : null; } if (!M) return null; @@ -2971,7 +3204,7 @@ for (var K in Obj) { try { var V = Obj[K]; - if (V && typeof V === 'object' && typeof V.k3_1 === 'number' && typeof V.equals === 'function') return V; + if (V && typeof V === 'object' && typeof V[FTeamId] === 'number' && typeof V.equals === 'function') return V; } catch (e) {} } return null; @@ -2981,7 +3214,7 @@ function IsGoldBoxEntity(Entity) { if (!Entity || !Entity.j10q_1) return false; var Type = FindEnumOf(Entity.j10q_1); - return !!(Type && Type.k3_1 === GOLD_BOX_TYPE); + return !!(Type && Type[FTeamId] === GOLD_BOX_TYPE); } function RegisterGoldBox(Entity) { @@ -3019,12 +3252,12 @@ try { var V = Bonus[K]; if (!V || typeof V !== 'object') continue; - if (V.n35_1 && V.n35_1.length >= 15) { - var M = V.n35_1; + if (V[FNodeMatrixOld] && V[FNodeMatrixOld].length >= 15) { + var M = V[FNodeMatrixOld]; return { x: M[12], y: M[13], z: M[14] }; } - if (typeof V.f20_1 === 'number' && typeof V.g20_1 === 'number' && typeof V.h20_1 === 'number') { - return { x: V.f20_1, y: V.g20_1, z: V.h20_1 }; + if (typeof V[FVecX] === 'number' && typeof V[FVecY] === 'number' && typeof V[FVecZ] === 'number') { + return { x: V[FVecX], y: V[FVecY], z: V[FVecZ] }; } } catch (e) {} } @@ -3139,7 +3372,7 @@ UpdateCam(camInfo, screenW, screenH); // 每帧只算一次相机基(需在相机位置兜底之后),供顶点变换/投影复用 - var tankList = gm.m10b_1.j11y(); + var tankList = gm[FGameModeTankManager].j11y(); var count = tankList.e1(); var drawnCount = 0, botDrawn = 0; @@ -3166,7 +3399,7 @@ // 本帧该坦克是否在移动(供「残留节点」检测:坦克在动而节点矩阵不动 = 旧位置遗留) // 用位移的指数滑动平均,而不是「单帧位移 > 10 单位」——后者对慢速移动几乎永远为假, // 会让残留检测形同虚设(残留漏掉的根因就在这里) - var tankId = tank.iz1_1; + var tankId = tank[FEntityId]; var prevPos = TankPrevPos.get(tankId); var tankMove = 0; if (prevPos) { @@ -3184,7 +3417,7 @@ if (distance > 50000) continue; var friendly = IsFriendly(tank, playerTank); - var tankName = GetTankName(tank) || ('ID:' + tank.iz1_1); + var tankName = GetTankName(tank) || ('ID:' + tank[FEntityId]); // 根据 HighlightMode 判断是否渲染该坦克 if (!ShouldRender(tankName)) continue; // 颜色按队伍:友方=蓝,敌方=红 @@ -3380,6 +3613,44 @@ // 原理:Hook gl.bindFramebuffer 重定向到自定义 FBO,用后视相机参数调用游戏渲染器,readback 显示 (function() { + // ===== 字段抽象层(语义名 → 匿名字段,总表在 hornet_schema.js)===== + var Schema = window.__hornetSchema__ || null; + function Fld(Key, Fallback) { var N = Schema ? Schema.FieldName(Key) : null; return N || Fallback; } + function Mth(Key, Fallback) { var N = Schema ? Schema.MethodName(Key) : null; return N || Fallback; } + + // ===== 场景 / 渲染器 ===== + var FSceneRef = Fld('Camera.SceneRef', 'jz1_1'); // 场景实体上的相机引用 + var FRenderer = Fld('Render.Renderer', 'gyw_1'); // 渲染器实例 + var FDrawer = Fld('Render.Drawer', 'd75_1'); // Ff 绘制器实例 + var FRootNode = Fld('Scene.RootNode', 'fyx_1'); // 场景根节点 + var FCameraObject = Fld('Camera.Object', 'iyx_1'); // 主相机对象 + var FGl = Fld('Render.Gl', 'w39_1'); // GL 包装对象(o3c/n3c) + var FGlNative = Fld('Render.GlNative', 'b74_1'); // 原生 WebGL 上下文 + + // ===== 相机 ===== + var FAngles = Fld('Camera.Angles', 'u35_1'); // 朝向角(pitch/roll/yaw) + var FPosition = Fld('Camera.Position', 'r35_1'); // 位置向量 + var FFov = Fld('Camera.Fov', 'b37_1'); // 视图矩阵数据 + var FViewportWidth = Fld('Camera.ViewportWidth', 'u36_1'); // 渲染宽 + var FViewportHeight = Fld('Camera.ViewportHeight', 'v36_1');// 渲染高 + var FFovValue = Fld('Camera.FovValue', 'z36_1'); // 视场角 + var FNear = Fld('Camera.Near', 'x36_1'); // 近裁剪面 + var FFar = Fld('Camera.Far', 'y36_1'); // 远裁剪面 + var MCameraUpdate = Mth('Camera.Update', 'o37'); // 重算相机矩阵 + var FX = Fld('Vec.X', 'f20_1'); + var FY = Fld('Vec.Y', 'g20_1'); + var FZ = Fld('Vec.Z', 'h20_1'); + + // ===== 渲染开关 / 队列 ===== + var FSettings = Fld('Render.Settings', 'y38_1'); // 渲染开关集合 + var FShadowEnabled = Fld('Render.ShadowEnabled', 'e3o_1'); // 阴影开关 + var FPostProcessEnabled = Fld('Render.PostProcessEnabled', 'f3o_1'); // 后处理开关 + var FExtraPassEnabled = Fld('Render.ExtraPassEnabled', 'k3o_1'); // 额外 pass 开关 + var FLayerQueue = Fld('Render.LayerQueue', 'e39_1'); // 渲染层队列 + var MDrawScene = Mth('Render.Draw', 'f3t'); // 渲染主场景 + var MGetDrawingWidth = Mth('Render.DrawingWidth', 'o3c'); // drawingBufferWidth + var MGetDrawingHeight = Mth('Render.DrawingHeight', 'n3c'); // drawingBufferHeight + function _start() { console.log('===== 3DTank后视镜(原生渲染版)启动 ====='); @@ -3457,27 +3728,27 @@ var initError = null; try { - scene = gm.lz4().jz1_1; + scene = gm.lz4()[FSceneRef]; console.log('[后视] 场景对象:', scene); // hC 渲染器实例(jr.lye_1) - var renderer = scene.gyw_1; + var renderer = scene[FRenderer]; console.log('[后视] 渲染器:', renderer); // Ff 绘制器实例(hC.r74_1) - ff = renderer.d75_1; + ff = renderer[FDrawer]; console.log('[后视] Ff绘制器:', ff); // 场景根节点(jr.kyf_1) - rootNode = scene.fyx_1; + rootNode = scene[FRootNode]; console.log('[后视] 根节点:', rootNode); // 主相机 Ha 实例(jr.nyf_1) - mainCamera = scene.iyx_1; + mainCamera = scene[FCameraObject]; console.log('[后视] 主相机:', mainCamera); // 游戏 gl context(Ff.p39_1.p73_1 = BS.p73_1) - gl = ff.w39_1.b74_1; + gl = ff[FGl][FGlNative]; console.log('[后视] GL上下文:', gl); if (!ff || !rootNode || !mainCamera || !gl) { @@ -3485,11 +3756,11 @@ } // 探测相机字段 - console.log('[后视] 相机u35_1:', mainCamera.u35_1); - console.log('[后视] 相机r35_1:', mainCamera.r35_1); - console.log('[后视] 相机b37_1:', mainCamera.b37_1); + console.log('[后视] 相机u35_1:', mainCamera[FAngles]); + console.log('[后视] 相机r35_1:', mainCamera[FPosition]); + console.log('[后视] 相机b37_1:', mainCamera[FFov]); - if (!mainCamera.u35_1 || !mainCamera.r35_1) { + if (!mainCamera[FAngles] || !mainCamera[FPosition]) { throw new Error('相机字段缺失'); } @@ -3621,10 +3892,10 @@ // Hook BS.h3c/g3c:后视渲染时返回 FBO 尺寸(修复 screenSize/aspect ratio) // y3s 会调用 BS.h3c()/g3c() 获取 drawingBufferWidth/Height 设置相机 l36_1/m36_1 // 不 Hook 的话 aspect ratio 会用游戏 canvas 尺寸(如1920x1080),导致画面比例错误 - origH3c = ff.w39_1.o3c.bind(ff.w39_1); - origG3c = ff.w39_1.n3c.bind(ff.w39_1); - ff.w39_1.o3c = function() { return rearviewRendering ? rw : origH3c(); }; - ff.w39_1.n3c = function() { return rearviewRendering ? rh : origG3c(); }; + origH3c = ff[FGl][MGetDrawingWidth].bind(ff[FGl]); + origG3c = ff[FGl][MGetDrawingHeight].bind(ff[FGl]); + ff[FGl][MGetDrawingWidth] = function() { return rearviewRendering ? rw : origH3c(); }; + ff[FGl][MGetDrawingHeight] = function() { return rearviewRendering ? rh : origG3c(); }; console.log('[后视] BS.o3c/n3c 已 Hook'); } @@ -3685,17 +3956,17 @@ var camPosZ = playerPos.z + Config.cameraHeight; // ===== 保存主相机参数 ===== - 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.u36_1; - var savedM36 = mainCamera.v36_1; - var savedS36 = mainCamera.z36_1; - var savedQ36 = mainCamera.x36_1; - var savedR36 = mainCamera.y36_1; + var savedPitch = mainCamera[FAngles][FX]; + var savedYaw = mainCamera[FAngles][FY]; + var savedRoll = mainCamera[FAngles][FZ]; + var savedX = mainCamera[FPosition][FX]; + var savedY = mainCamera[FPosition][FY]; + var savedZ = mainCamera[FPosition][FZ]; + var savedL36 = mainCamera[FViewportWidth]; + var savedM36 = mainCamera[FViewportHeight]; + var savedS36 = mainCamera[FFovValue]; + var savedQ36 = mainCamera[FNear]; + var savedR36 = mainCamera[FFar]; // 临时暂停自由视角的 h37 hook,避免覆盖后视镜相机参数 var fl = window.__hornetFreeLook__; @@ -3706,41 +3977,41 @@ // g=π 使水平方向反向(朝后看),h=yaw+π 保持水平视角正确 // f=-pitch 使垂直方向与主视一致(低头看后方地形) // up 朝下 → FBO 画面颠倒 → GL readback Y 翻转修正 - 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.u36_1 = rw; - mainCamera.v36_1 = rh; - mainCamera.z36_1 = Config.fov; - mainCamera.x36_1 = Config.near; - mainCamera.y36_1 = Config.far; + mainCamera[FAngles][FX] = rearPitch; + mainCamera[FAngles][FY] = Math.PI; + mainCamera[FAngles][FZ] = camInfo.yaw + Math.PI; + mainCamera[FPosition][FX] = camPosX; + mainCamera[FPosition][FY] = camPosY; + mainCamera[FPosition][FZ] = camPosZ; + mainCamera[FViewportWidth] = rw; + mainCamera[FViewportHeight] = rh; + mainCamera[FFovValue] = Config.fov; + mainCamera[FNear] = Config.near; + mainCamera[FFar] = Config.far; // 更新相机矩阵(viewMatrix, projectionMatrix, viewProjectionMatrix) - mainCamera.o37(); + mainCamera[MCameraUpdate](); // 保存后视视图矩阵副本(用于 UI 标记投影,finally 会恢复相机所以需提前保存) - var rearViewMatrix = new Float32Array(mainCamera.b37_1); + var rearViewMatrix = new Float32Array(mainCamera[FFov]); // ===== 保存并禁用阴影/后处理(避免它们切换 FBO 覆盖 rearFbo)===== // p38_1.b3o_1 控制 z39_1(阴影),c3o_1 控制 c3a_1/d3a_1(后处理),z3n_1 控制 f3a_1 // 禁用后 y3s 只执行主场景渲染 z3k,跳过所有 FBO 切换的 pass - 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; + var savedX3n = ff[FSettings][FShadowEnabled]; + var savedY3n = ff[FSettings][FPostProcessEnabled]; + var savedZ3n = ff[FSettings][FExtraPassEnabled]; + ff[FSettings][FShadowEnabled] = false; + ff[FSettings][FPostProcessEnabled] = false; + ff[FSettings][FExtraPassEnabled] = false; // ===== 禁用 UI 渲染层(防止技能条等 UI 混入后视 FBO)===== // 保存 UI 层队列,替换为空假对象,y3s 后恢复 var savedUiQueues = {}; for (var ui = 0; ui < uiLayerIndices.length; ui++) { var li = uiLayerIndices[ui]; - savedUiQueues[li] = ff.e39_1[li]; - ff.e39_1[li] = fakeQueue; + savedUiQueues[li] = ff[FLayerQueue][li]; + ff[FLayerQueue][li] = fakeQueue; } // ===== 渲染到 FBO ===== @@ -3753,7 +4024,7 @@ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); // 调用游戏原生渲染器(已禁用阴影/后处理/UI层,只渲染 3D 场景) - ff.f3t(rootNode, mainCamera); + ff[MDrawScene](rootNode, mainCamera); rearviewRendering = false; @@ -3806,9 +4077,9 @@ // ===== 恢复阴影/后处理(异常时也要恢复)===== try { if (typeof savedX3n !== 'undefined') { - ff.y38_1.e3o_1 = savedX3n; - ff.y38_1.f3o_1 = savedY3n; - ff.y38_1.k3o_1 = savedZ3n; + ff[FSettings][FShadowEnabled] = savedX3n; + ff[FSettings][FPostProcessEnabled] = savedY3n; + ff[FSettings][FExtraPassEnabled] = savedZ3n; } } catch(e) { console.error('[后视] 恢复阴影/后处理失败:', e.message); @@ -3819,7 +4090,7 @@ if (savedUiQueues) { for (var ui2 = 0; ui2 < uiLayerIndices.length; ui2++) { var li2 = uiLayerIndices[ui2]; - ff.e39_1[li2] = savedUiQueues[li2]; + ff[FLayerQueue][li2] = savedUiQueues[li2]; } } } catch(e) { @@ -3828,18 +4099,18 @@ // ===== 恢复主相机参数 ===== try { - 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.u36_1 = savedL36; - mainCamera.v36_1 = savedM36; - mainCamera.z36_1 = savedS36; - mainCamera.x36_1 = savedQ36; - mainCamera.y36_1 = savedR36; - mainCamera.o37(); + mainCamera[FAngles][FX] = savedPitch; + mainCamera[FAngles][FY] = savedYaw; + mainCamera[FAngles][FZ] = savedRoll; + mainCamera[FPosition][FX] = savedX; + mainCamera[FPosition][FY] = savedY; + mainCamera[FPosition][FZ] = savedZ; + mainCamera[FViewportWidth] = savedL36; + mainCamera[FViewportHeight] = savedM36; + mainCamera[FFovValue] = savedS36; + mainCamera[FNear] = savedQ36; + mainCamera[FFar] = savedR36; + mainCamera[MCameraUpdate](); } catch(e) { console.error('[后视] 恢复相机失败:', e.message); } @@ -3967,9 +4238,9 @@ } // 恢复 BS.h3c/g3c - if (origH3c && ff && ff.w39_1) { - ff.w39_1.o3c = origH3c; - ff.w39_1.n3c = origG3c; + if (origH3c && ff && ff[FGl]) { + ff[FGl][MGetDrawingWidth] = origH3c; + ff[FGl][MGetDrawingHeight] = origG3c; console.log('[后视] BS.o3c/n3c 已恢复'); } @@ -4063,6 +4334,28 @@ // 独立轻量级脚本,不依赖3D透视渲染 (function() { + // ===== 字段抽象层(语义名 → 匿名字段,总表在 hornet_schema.js)===== + var Schema = window.__hornetSchema__ || null; + function Fld(Key, Fallback) { var N = Schema ? Schema.FieldName(Key) : null; return N || Fallback; } + function Mth(Key, Fallback) { var N = Schema ? Schema.MethodName(Key) : null; return N || Fallback; } + + // ===== 匿名字段常量 ===== + var FEntityId = Fld('Entity.Id', 'iz1_1'); + var FEntityRoot = Fld('Entity.Root', 'qz1_1'); + var FEntityComponentMap = Fld('Entity.ComponentMap', 's2a_1'); + var MGameModeScene = Mth('GameMode.Scene', 'lz4'); + var MCollisionRaycast = Mth('CollisionWorld.Raycast', 'a17p'); + var FWeaponComponent = Fld('Weapon.Component', 'n11c_1'); + var FWeaponComponentAlt = Fld('Weapon.ComponentAlt', 'q112_1'); + var FWeaponTurretRoot = Fld('Weapon.TurretRoot', 'm17a_1'); + var FNodeMatrix = Fld('Node.MatrixOld', 'n35_1'); + var FRayHitPoint = Fld('Ray.HitPoint', 'y178_1'); + var FRayHitNormal = Fld('Ray.HitNormal', 'z178_1'); + var FRayDistance = Fld('Ray.Distance', 'a179_1'); + var FRayHitShape = Fld('Ray.HitShape', 'q17o_1'); + var FRayHitBody = Fld('Ray.HitBody', 'b179_1'); + var FRayFilterQuery = Fld('RayFilter.Query', 'l1da_1'); + function _start() { console.log('===== 3DTank瞄准警告启动 ====='); @@ -4129,9 +4422,9 @@ // ===== 获取 TDt 武器皮肤组件(炮塔) ===== function GetTDtComponent(entity) { - var comp = FindComponentByField(entity, 'n11c_1'); + var comp = FindComponentByField(entity, FWeaponComponent); if (comp) return comp; - comp = FindComponentByField(entity, 'q112_1'); + comp = FindComponentByField(entity, FWeaponComponentAlt); return comp; } @@ -4144,9 +4437,9 @@ // 返回 { angle: 炮塔朝向与"敌方→玩家"方向的夹角, distance: 距离, turretAngle: 炮塔朝向角度, turretDir: {x,y} } function GetTurretAimAngle(tank, enemyPos, playerPos) { var tdt = GetTDtComponent(tank); - if (!tdt || !tdt.n11c_1 || !tdt.n11c_1.m17a_1) return null; - var bpNode = tdt.n11c_1.m17a_1; - var m = bpNode.n35_1; + if (!tdt || !tdt[FWeaponComponent] || !tdt[FWeaponComponent][FWeaponTurretRoot]) return null; + var bpNode = tdt[FWeaponComponent][FWeaponTurretRoot]; + var m = bpNode[FNodeMatrix]; if (!m) return null; // 敌方→玩家方向(x,y 水平面) @@ -4186,13 +4479,13 @@ function GetUMtComponent() { if (UMtComponent) return UMtComponent; try { - var gmEntity = gm.lz4(); - if (!gmEntity || !gmEntity.qz1_1 || !gmEntity.qz1_1.s2a_1) return null; - gmEntity.qz1_1.s2a_1.forEach(function(valueList, key) { + var gmEntity = gm[MGameModeScene](); + if (!gmEntity || !gmEntity[FEntityRoot] || !gmEntity[FEntityRoot][FEntityComponentMap]) return null; + gmEntity[FEntityRoot][FEntityComponentMap].forEach(function(valueList, key) { if (UMtComponent) return; try { var comp = (valueList && typeof valueList.g1 === 'function') ? valueList.g1(0) : valueList; - if (comp && typeof comp.a17p === 'function') UMtComponent = comp; + if (comp && typeof comp[MCollisionRaycast] === 'function') UMtComponent = comp; } catch(e) {} }); if (UMtComponent) console.log('[瞄准] uMt 碰撞检测器已获取'); @@ -4234,30 +4527,30 @@ var maxDist = dist - 300; var result = { - y178_1: MkVec(0,0,0), - z178_1: MkVec(0,0,0), - a179_1: maxDist, - q17o_1: null, - b179_1: null, + [FRayHitPoint]: MkVec(0,0,0), + [FRayHitNormal]: MkVec(0,0,0), + [FRayDistance]: maxDist, + [FRayHitShape]: null, + [FRayHitBody]: null, r17o: function(t, n, i, r, e) { - this.y178_1.k26(t); - this.z178_1.k26(n); - this.a179_1 = i; - this.q17o_1 = r; - this.b179_1 = e; + this[FRayHitPoint].k26(t); + this[FRayHitNormal].k26(n); + this[FRayDistance] = i; + this[FRayHitShape] = r; + this[FRayHitBody] = e; }, - d3: function() { this.b179_1 = null; } + d3: function() { this[FRayHitBody] = null; } }; var filter = { - l1da_1: { h1: function() { return false; }, d3: function() {} }, + [FRayFilterQuery]: { h1: function() { return false; }, d3: function() {} }, w179: function() { return true; }, - d3: function() { this.l1da_1.d3(); }, + d3: function() { this[FRayFilterQuery].d3(); }, m1da: function() {} }; - var hit = uMt.a17p(startVec, dirVec, maxDist, result, filter); - blocked = !!hit && result.a179_1 < (maxDist - 50); + var hit = uMt[MCollisionRaycast](startVec, dirVec, maxDist, result, filter); + blocked = !!hit && result[FRayDistance] < (maxDist - 50); RaycastFailCount = 0; } catch(e) { RaycastFailCount++; @@ -4369,19 +4662,19 @@ if (aimInfo.angle >= aimThreshold) continue; // 墙体阻隔判断 - var blocked = IsLineOfSightBlocked(pos, playerPos, tank.iz1_1, frameCount); + var blocked = IsLineOfSightBlocked(pos, playerPos, tank[FEntityId], frameCount); if (blocked) continue; // 记录威胁 var proj = Project3D(pos, camInfo.pos, camInfo.yaw, camInfo.pitch, camInfo.fov, screenW, screenH); - var tankName = GetTankName(tank) || ('ID:' + tank.iz1_1); + var tankName = GetTankName(tank) || ('ID:' + tank[FEntityId]); threats.push({ pos: pos, distance: aimInfo.distance, angle: aimInfo.angle, turretDir: aimInfo.turretDir, screenProj: proj, - tankId: tank.iz1_1, + tankId: tank[FEntityId], tankName: tankName }); } @@ -4711,6 +5004,23 @@ // 原理:Hook mainCamera.h37,在相机矩阵更新前覆盖l35_1/k35_1参数 (function() { + // ===== 字段抽象层(语义名 → 匿名字段,总表在 hornet_schema.js)===== + var Schema = window.__hornetSchema__ || null; + function Fld(Key, Fallback) { var N = Schema ? Schema.FieldName(Key) : null; return N || Fallback; } + function Mth(Key, Fallback) { var N = Schema ? Schema.MethodName(Key) : null; return N || Fallback; } + + // ===== 匿名字段常量 ===== + var FVecX = Fld('Vec.X', 'f20_1'); + var FVecY = Fld('Vec.Y', 'g20_1'); + var FVecZ = Fld('Vec.Z', 'h20_1'); + var FCameraSceneRef = Fld('Camera.SceneRef', 'jz1_1'); + var FCameraObject = Fld('Camera.Object', 'iyx_1'); + var FCameraEuler = Fld('Camera.Euler', 'u35_1'); + var FCameraPosition = Fld('Node.Children', 'r35_1'); + var FViewMatrix = Fld('Camera.Fov', 'b37_1'); + var FCameraSceneFx = Fld('Camera.SceneFx', 'd75_1'); + var MGameModeScene = Mth('GameMode.Scene', 'lz4'); + function _start() { console.log('===== 3DTank自由视角启动 ====='); @@ -4740,15 +5050,15 @@ var initError = null; try { - scene = gm.lz4().jz1_1; - mainCamera = scene.iyx_1; - ff = scene.gyw_1.d75_1; - if (!mainCamera || !mainCamera.u35_1 || !mainCamera.r35_1 || !mainCamera.o37) { + scene = gm[MGameModeScene]()[FCameraSceneRef]; + mainCamera = scene[FCameraObject]; + ff = scene.gyw_1[FCameraSceneFx]; + if (!mainCamera || !mainCamera[FCameraEuler] || !mainCamera[FCameraPosition] || !mainCamera.o37) { throw new Error('相机字段缺失'); } console.log('[自由视角] 主相机:', mainCamera.constructor.name, - '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) + ')'); + 'l35=(' + mainCamera[FCameraEuler][FVecX].toFixed(2) + ',' + mainCamera[FCameraEuler][FVecY].toFixed(2) + ',' + mainCamera[FCameraEuler][FVecZ].toFixed(2) + ')', + 'k35=(' + mainCamera[FCameraPosition][FVecX].toFixed(0) + ',' + mainCamera[FCameraPosition][FVecY].toFixed(0) + ',' + mainCamera[FCameraPosition][FVecZ].toFixed(0) + ')'); } catch(e) { initError = e; console.error('[自由视角] 初始化失败:', e.message); @@ -4774,12 +5084,12 @@ var origH37 = mainCamera.o37; function applyFreeLookParams() { - 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; + mainCamera[FCameraEuler][FVecX] = freePitch; // pitch (f) + mainCamera[FCameraEuler][FVecY] = freeRoll; // roll (g) 保持初始值 + mainCamera[FCameraEuler][FVecZ] = freeYaw; // yaw (h) 鼠标左右控制 + mainCamera[FCameraPosition][FVecX] = freePosX; + mainCamera[FCameraPosition][FVecY] = freePosY; + mainCamera[FCameraPosition][FVecZ] = freePosZ; } // 缓存从 viewMatrix 提取的前方向/右方向(h37 更新后设置) @@ -4796,11 +5106,11 @@ applyFreeLookParams(); origH37.call(this); // 从更新后的 viewMatrix (u36_1) 提取前方向和右方向(水平分量 x,y) - if (this.b37_1) { - cachedFwdX = -this.b37_1[2]; // forward.x - cachedFwdY = -this.b37_1[6]; // forward.y - cachedRightX = this.b37_1[0]; // right.x - cachedRightY = this.b37_1[4]; // right.y + if (this[FViewMatrix]) { + cachedFwdX = -this[FViewMatrix][2]; // forward.x + cachedFwdY = -this[FViewMatrix][6]; // forward.y + cachedRightX = this[FViewMatrix][0]; // right.x + cachedRightY = this[FViewMatrix][4]; // right.y cachedInitialized = true; } } else { @@ -4813,12 +5123,12 @@ function enableFreeLook() { // 从当前相机参数初始化(平滑过渡) // l35_1: f=pitch, g=roll, h=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; + freePitch = mainCamera[FCameraEuler][FVecX]; + freeRoll = mainCamera[FCameraEuler][FVecY]; // roll 保持初始值 + freeYaw = mainCamera[FCameraEuler][FVecZ]; // yaw 鼠标控制 + freePosX = mainCamera[FCameraPosition][FVecX]; + freePosY = mainCamera[FCameraPosition][FVecY]; + freePosZ = mainCamera[FCameraPosition][FVecZ]; freeLookActive = true; console.log('[自由视角] 已开启 — WASD移动 | 鼠标旋转 | 空格上升/Shift下降 | 滚轮调速 | Alt+F关闭'); updateHud(); @@ -5067,6 +5377,50 @@ // 操作:拖拽标题移动窗口 | 拖拽地图平移 | 滚轮缩放 | 双击居中 | Alt+M 开关 (function() { + // ===== 字段抽象层(语义名 → 匿名字段,总表在 hornet_schema.js)===== + var Schema = window.__hornetSchema__ || null; + function Fld(Key, Fallback) { var N = Schema ? Schema.FieldName(Key) : null; return N || Fallback; } + function Mth(Key, Fallback) { var N = Schema ? Schema.MethodName(Key) : null; return N || Fallback; } + + // 向量 + var FVecX = Fld('Vec.X', 'f20_1'); + var FVecY = Fld('Vec.Y', 'g20_1'); + var FVecZ = Fld('Vec.Z', 'h20_1'); + + // 战局 / 场景 + var MScene = Mth('GameMode.Scene', 'lz4'); + var FCameraSceneRef = Fld('Camera.SceneRef', 'jz1_1'); + var FTankManager = Fld('GameMode.TankManager', 'm10b_1'); + var MTankList = Mth('GameMode.TankList', 'j11y'); + + // 渲染器 + var FRenderer = Fld('Renderer.Object', 'd75_1'); + var FRenderDevice = Fld('Renderer.Device', 'w39_1'); + var FRenderGl = Fld('Renderer.Gl', 'b74_1'); + var FRenderFlags = Fld('Renderer.Flags', 'y38_1'); + var FRenderShadow = Fld('Renderer.Shadow', 'e3o_1'); + var FRenderPostFx = Fld('Renderer.PostFx', 'f3o_1'); + var FRenderFlagAux = Fld('Renderer.FlagAux', 'k3o_1'); + var FRenderQueueList = Fld('Renderer.QueueList', 'e39_1'); + + // 相机 + var FCameraRotation = Fld('Camera.Rotation', 'u35_1'); + var FNodeChildren = Fld('Node.Children', 'r35_1'); // 本文件里该字段是相机位置向量(与节点子列表同名复用) + var FCameraRenderWidth = Fld('Camera.RenderWidth', 'u36_1'); + var FCameraRenderHeight = Fld('Camera.RenderHeight', 'v36_1'); + var FCameraFov = Fld('Camera.Fov', 'z36_1'); + var FCameraNear = Fld('Camera.Near', 'x36_1'); + var FCameraFar = Fld('Camera.Far', 'y36_1'); + + // 皮肤 / 节点 + var FSkinSlotHull = Fld('Skin.SlotHull', 'w14w_1'); + var FSkinSlotHullLegacy = Fld('Skin.SlotHullLegacy', 't14l_1'); + var FSkinSlotHullAlt = Fld('Skin.SlotHullAlt', 'w14x_1'); + var FSkinSlotHullOld = Fld('Skin.SlotHullOld', 'p14l_1'); + var FSkinParts = Fld('Skin.Parts', 'g17r_1'); + var FSkinPartNode = Fld('Skin.PartNode', 'j17r_1'); + var FNodeMatrixOld = Fld('Node.MatrixOld', 'n35_1'); + function _start() { console.log('===== 3DTank 战争沙盘(3D俯瞰版)启动 ====='); @@ -5099,27 +5453,27 @@ // 从车体皮肤组件(KMt)的Bp节点世界矩阵提取前向方向 // dzc_1 是局部空间向量且可能未设置,不可靠 // 皮肤字段:新 build 是 w14w_1;旧 build 是 t14l_1(新版 t14l_1 已改成悬停车体状态 Qmt) - var hullComp = FindComponentByField(tank, 'w14w_1') || - FindComponentByField(tank, 't14l_1') || - FindComponentByField(tank, 'w14x_1') || - FindComponentByField(tank, 'p14l_1'); + var hullComp = FindComponentByField(tank, FSkinSlotHull) || + FindComponentByField(tank, FSkinSlotHullLegacy) || + FindComponentByField(tank, FSkinSlotHullAlt) || + FindComponentByField(tank, FSkinSlotHullOld); if (!hullComp) return 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; + var skin = (hullComp[FSkinSlotHull] && hullComp[FSkinSlotHull][FSkinParts]) ? hullComp[FSkinSlotHull] : + (hullComp[FSkinSlotHullLegacy] && hullComp[FSkinSlotHullLegacy][FSkinParts]) ? hullComp[FSkinSlotHullLegacy] : + (hullComp[FSkinSlotHullOld] && hullComp[FSkinSlotHullOld][FSkinParts]) ? hullComp[FSkinSlotHullOld] : null; if (!skin) return null; try { - var partCount = skin.g17r_1.e1(); + var partCount = skin[FSkinParts].e1(); for (var p = 0; p < partCount; p++) { - var part = skin.g17r_1.g1(p); - if (!part || !part.j17r_1) continue; - var nodeCount = part.j17r_1.e1(); + var part = skin[FSkinParts].g1(p); + if (!part || !part[FSkinPartNode]) continue; + var nodeCount = part[FSkinPartNode].e1(); for (var n = 0; n < nodeCount; n++) { - var bpNode = part.j17r_1.g1(n); - if (!bpNode || !bpNode.n35_1) continue; + var bpNode = part[FSkinPartNode].g1(n); + if (!bpNode || !bpNode[FNodeMatrixOld]) continue; // 不按 g35_1 过滤:它是引擎的细节/淡出权重(0~1),不是幽灵标志, // 过滤会漏掉低细节节点、导致取不到朝向 - var m = bpNode.n35_1; + var m = bpNode[FNodeMatrixOld]; // 列主序世界矩阵:Col0=(m[0],m[1],m[2]) Col1=(m[4],m[5],m[6]) Col2=(m[8],m[9],m[10]) // 游戏坐标:x,y 水平面; z(h20_1)=高度。找"上"列(|z|最大),其余为水平方向 var az0 = Math.abs(m[2]), az1 = Math.abs(m[6]), az2 = Math.abs(m[10]); @@ -5149,11 +5503,11 @@ var scene = null, ff = null, rootNode = null, mainCamera = null, gl = null; var initError = null; try { - scene = gm.lz4().jz1_1; - ff = scene.gyw_1.d75_1; + scene = gm[MScene]()[FCameraSceneRef]; + ff = scene.gyw_1[FRenderer]; rootNode = scene.fyx_1; mainCamera = scene.iyx_1; - gl = ff.w39_1.b74_1; + gl = ff[FRenderDevice][FRenderGl]; if (!ff || !rootNode || !mainCamera || !gl) throw new Error('关键字段缺失'); } catch(e) { initError = e; @@ -5340,10 +5694,10 @@ } return origBindFramebuffer(target, fbo); }; - var origH3c = ff.w39_1.o3c.bind(ff.w39_1); - var origG3c = ff.w39_1.n3c.bind(ff.w39_1); - ff.w39_1.o3c = function() { return sandboxRendering ? FBO_W : origH3c(); }; - ff.w39_1.n3c = function() { return sandboxRendering ? FBO_H : origG3c(); }; + var origH3c = ff[FRenderDevice].o3c.bind(ff[FRenderDevice]); + var origG3c = ff[FRenderDevice].n3c.bind(ff[FRenderDevice]); + ff[FRenderDevice].o3c = function() { return sandboxRendering ? FBO_W : origH3c(); }; + ff[FRenderDevice].n3c = function() { return sandboxRendering ? FBO_H : origG3c(); }; // UI 层禁用 var uiLayerIndices = [20, 21, 22, 23, 24]; @@ -5543,7 +5897,7 @@ // 坦克标记 var tanks = []; try { - var list = gm.m10b_1.j11y(); + var list = gm[FTankManager][MTankList](); var n = list.e1(); for (var i = 0; i < n; i++) { var t = list.g1(i); if (t) tanks.push(t); } } catch(e) {} @@ -5723,44 +6077,44 @@ try { // 保存主相机参数 - 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.u36_1, sM36 = mainCamera.v36_1; - var sS36 = mainCamera.z36_1, sQ36 = mainCamera.x36_1, sR36 = mainCamera.y36_1; + var sPitch = mainCamera[FCameraRotation][FVecX]; + var sRoll = mainCamera[FCameraRotation][FVecY]; + var sYaw = mainCamera[FCameraRotation][FVecZ]; + var sX = mainCamera[FNodeChildren][FVecX]; + var sY = mainCamera[FNodeChildren][FVecY]; + var sZ = mainCamera[FNodeChildren][FVecZ]; + var sL36 = mainCamera[FCameraRenderWidth], sM36 = mainCamera[FCameraRenderHeight]; + var sS36 = mainCamera[FCameraFov], sQ36 = mainCamera[FCameraNear], sR36 = mainCamera[FCameraFar]; // 设置俯瞰相机参数 // pitch=-π/2 朝下看,roll=0 不翻转,yaw=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.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[FCameraRotation][FVecX] = -Math.PI / 2; + mainCamera[FCameraRotation][FVecY] = 0; + mainCamera[FCameraRotation][FVecZ] = 0; + mainCamera[FNodeChildren][FVecX] = viewCenterX; + mainCamera[FNodeChildren][FVecY] = viewCenterY; + mainCamera[FNodeChildren][FVecZ] = camHeight; + mainCamera[FCameraRenderWidth] = FBO_W; + mainCamera[FCameraRenderHeight] = FBO_H; + mainCamera[FCameraFov] = Config.fov; + mainCamera[FCameraNear] = Config.near; + mainCamera[FCameraFar] = Config.far; mainCamera.o37(); // 禁用阴影/后处理 - 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; + var savedX3n = ff[FRenderFlags][FRenderShadow]; + var savedY3n = ff[FRenderFlags][FRenderPostFx]; + var savedZ3n = ff[FRenderFlags][FRenderFlagAux]; + ff[FRenderFlags][FRenderShadow] = false; + ff[FRenderFlags][FRenderPostFx] = false; + ff[FRenderFlags][FRenderFlagAux] = false; // 禁用 UI 层 var savedUiQueues = {}; for (var ui = 0; ui < uiLayerIndices.length; ui++) { var li = uiLayerIndices[ui]; - savedUiQueues[li] = ff.e39_1[li]; - ff.e39_1[li] = fakeQueue; + savedUiQueues[li] = ff[FRenderQueueList][li]; + ff[FRenderQueueList][li] = fakeQueue; } // 渲染到 FBO @@ -5806,22 +6160,22 @@ origBindFramebuffer(gl.FRAMEBUFFER, mainFbo); // 恢复渲染状态 - ff.y38_1.e3o_1 = savedX3n; - ff.y38_1.f3o_1 = savedY3n; - ff.y38_1.k3o_1 = savedZ3n; + ff[FRenderFlags][FRenderShadow] = savedX3n; + ff[FRenderFlags][FRenderPostFx] = savedY3n; + ff[FRenderFlags][FRenderFlagAux] = savedZ3n; for (var ui2 = 0; ui2 < uiLayerIndices.length; ui2++) { - ff.e39_1[uiLayerIndices[ui2]] = savedUiQueues[uiLayerIndices[ui2]]; + ff[FRenderQueueList][uiLayerIndices[ui2]] = savedUiQueues[uiLayerIndices[ui2]]; } // 恢复主相机 - 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.u36_1 = sL36; mainCamera.v36_1 = sM36; - mainCamera.z36_1 = sS36; mainCamera.x36_1 = sQ36; mainCamera.y36_1 = sR36; + mainCamera[FCameraRotation][FVecX] = sPitch; + mainCamera[FCameraRotation][FVecY] = sRoll; + mainCamera[FCameraRotation][FVecZ] = sYaw; + mainCamera[FNodeChildren][FVecX] = sX; + mainCamera[FNodeChildren][FVecY] = sY; + mainCamera[FNodeChildren][FVecZ] = sZ; + mainCamera[FCameraRenderWidth] = sL36; mainCamera[FCameraRenderHeight] = sM36; + mainCamera[FCameraFov] = sS36; mainCamera[FCameraNear] = sQ36; mainCamera[FCameraFar] = sR36; mainCamera.o37(); // 绘制 2D 叠加层(坦克标记/网格/罗盘) @@ -5857,8 +6211,8 @@ if (rafId) cancelAnimationFrame(rafId); // 恢复 hook gl.bindFramebuffer = origBindFramebuffer; - ff.w39_1.o3c = origH3c; - ff.w39_1.n3c = origG3c; + ff[FRenderDevice].o3c = origH3c; + ff[FRenderDevice].n3c = origG3c; window.removeEventListener('resize', resizeCanvas); if (container.parentNode) container.parentNode.removeChild(container); window.__hornetWarSandbox__ = null; diff --git a/3dtank_bundle.js b/3dtank_bundle.js index f456422..441edc4 100644 --- a/3dtank_bundle.js +++ b/3dtank_bundle.js @@ -21,6 +21,15 @@ // 坦克列表 = dct(scene).m10b_1.j11y() (function() { + // ===== 字段抽象层(语义名 → 匿名字段,总表在 hornet_schema.js)===== + var Schema = window.__hornetSchema__ || null; + function Fld(Key, Fallback) { var N = Schema ? Schema.FieldName(Key) : null; return N || Fallback; } + function Mth(Key, Fallback) { var N = Schema ? Schema.MethodName(Key) : null; return N || Fallback; } + + // ===== 语义字段常量(取不到抽象层时用 fallback 字面名) ===== + var FGameModeTankManager = Fld('GameMode.TankManager', 'm10b_1'); // 坦克列表管理器 + var MTankList = Mth('GameMode.TankList', 'j11y'); // 坦克列表 + console.log('===== GameMode Hook v2 开始 ====='); // 1. 检查 webpack 内部对象 @@ -55,7 +64,7 @@ // 尝试获取坦克列表(失败也不影响 gameMode 设置) var tankCount = -1; try { - var list = gm.m10b_1.j11y(); + var list = gm[FGameModeTankManager][MTankList](); tankCount = list.e1(); window.__tankList__ = list; } catch(e) { @@ -111,7 +120,7 @@ if (window.__gameMode__) { // 验证当前引用是否有效 try { - var list = window.__gameMode__.m10b_1.j11y(); + var list = window.__gameMode__[FGameModeTankManager][MTankList](); window.__tankList__ = list; } catch(e) { // 引用失效(新战斗/退出战斗),重置等待重新捕获 @@ -150,7 +159,7 @@ console.log('ght 实例未捕获,请等待'); return; } - var list = window.__gameMode__.m10b_1.j11y(); + var list = window.__gameMode__[FGameModeTankManager][MTankList](); var count = list.e1(); console.log('=== 坦克列表 (' + count + " 个) ==="); for (var i = 0; i < count; i++) { @@ -165,7 +174,7 @@ console.log('ght 实例未捕获'); return; } - var list = window.__gameMode__.m10b_1.j11y(); + var list = window.__gameMode__[FGameModeTankManager][MTankList](); var tank = list.g1(idx || 0); console.log('坦克[' + (idx || 0) + ']:', tank); console.log('字段:', Object.keys(tank)); @@ -189,7 +198,7 @@ window.__refreshGameMode__ = function() { if (window.__gameMode__) { try { - var list = window.__gameMode__.m10b_1.j11y(); + var list = window.__gameMode__[FGameModeTankManager][MTankList](); console.log('%c[刷新] ght 引用有效,坦克数:' + list.e1(), 'color:#0f0;font-weight:bold'); return window.__gameMode__; } catch(e) { @@ -242,12 +251,20 @@ // ===== 字段映射总表 ===== // 命名规则:<对象类型>.<语义名> var Fields = { + // ---- 向量(O$)---- + 'Vec.X': 'f20_1', + 'Vec.Y': 'g20_1', + 'Vec.Z': 'h20_1', + // ---- 战斗实体基类(fe / BattleEntity)---- 'Entity.Id': 'iz1_1', // 实体 ID(坦克/战场唯一) + 'Entity.Root': 'qz1_1', // 组件宿主根对象 + 'Entity.ComponentMap': 's2a_1', // 宿主上的组件 Map 'Entity.Components': 'qz1_1.s2a_1', // Map<组件标识, 组件列表>,取值要 valueList.g1(0) // ---- GameMode(ght,window.__gameMode__)---- 'GameMode.Scene': 'lz4()', // 场景实体 + 'GameMode.TankManager': 'm10b_1', // 坦克管理器 'GameMode.TankList': 'm10b_1.j11y()', // 坦克列表(e1() 数量 / g1(i) 取) 'GameMode.OwnedTank': 'u10b_1', // 本地玩家拥有的坦克(onTankPossessed) 'GameMode.ObservedTank': 't10b_1', // 当前观察目标(死亡/观战时可能指向敌人) @@ -267,6 +284,8 @@ // ---- 坦克物理组件(ejt,组件名 tankPhysics)---- 'Physics.Position': 'bzr_1', // 世界位置向量(f20_1/g20_1/h20_1) + 'Physics.PositionOld1': 'gzh_1', // 旧版位置字段名(兜底候选) + 'Physics.PositionOld2': 'pz7_1', // 更旧位置字段名(兜底候选) 'Physics.Size': 'xzq_1', // 车体半尺寸(**不是位置**) 'Physics.Hull': 'zzq_1', // 车体主碰撞形状(x9j_1 === 'hull-main') 'Physics.Body': 'pbk()', // 物理刚体(jM) @@ -316,17 +335,107 @@ // ---- BP 场景节点(zl)---- 'Node.Name': 'g35_1', 'Node.Matrix': 'p35_1', // 世界矩阵(列主序,[12][13][14] = 平移) + 'Node.MatrixOld': 'n35_1', // 旧版矩阵字段(兜底候选) 'Node.Children': 'r35_1', 'Node.Mesh': 'v3m_1', 'Node.Lod': 'u4x_1', // ---- 相机(场景实体 .jz1_1.iyx_1)---- 'Camera.SceneRef': 'jz1_1', + 'Camera.Object': 'iyx_1', // 主相机对象(挂在场景实体的 jz1_1 上) + 'Camera.Viewport': 'w36_1', // 视口(h29() 宽 / g29() 高) 'Camera.Main': 'jz1_1.iyx_1', 'Camera.Position': 'jz1_1.iyx_1.t35_1', 'Camera.ViewMatrix': 'jz1_1.iyx_1.d37_1', 'Camera.ClipMatrix': 'jz1_1.iyx_1.g37_1', - 'Camera.Fov': 'jz1_1.iyx_1.b37_1' + 'Camera.Fov': 'jz1_1.iyx_1.b37_1', + + // ---- 相机:单段叶子字段(FieldName 只认单段,这里给同一批字段补叶子别名)---- + 'Camera.PositionLeaf': 't35_1', + 'Camera.ViewMatrixLeaf': 'd37_1', + 'Camera.ClipMatrixLeaf': 'g37_1', + 'Camera.FovLeaf': 'b37_1', + 'Camera.PositionNode': 'r35_1', // 旧版相机位置字段(与 Node.Children 同名但不通用) + 'Camera.Angles': 'u35_1', // 欧拉角(pitch/roll/yaw) + 'Camera.Euler': 'u35_1', // 同上(自由视角叫法) + 'Camera.Rotation': 'u35_1', // 同上(沙盘叫法) + 'Camera.Update': 'o37()', // 相机矩阵刷新 + 'Camera.ViewportWidth': 'u36_1', + 'Camera.ViewportHeight': 'v36_1', + 'Camera.RenderWidth': 'u36_1', // 别名 + 'Camera.RenderHeight': 'v36_1', // 别名 + 'Camera.FovValue': 'z36_1', + 'Camera.Near': 'x36_1', + 'Camera.Far': 'y36_1', + 'Camera.SceneFx': 'd75_1', + + // ---- 渲染器 / 绘制流程(module_92980)---- + 'Render.Renderer': 'gyw_1', // 场景渲染器 + 'Render.Drawer': 'd75_1', // Ff 绘制器 + 'Render.DrawBuffer': 'd75_1', // 别名(透视里按"绘制缓冲"称呼) + 'Renderer.Object': 'd75_1', // 别名(沙盘里按"渲染器对象"称呼) + 'Render.Gl': 'w39_1', // GL 包装器 + 'Renderer.Device': 'w39_1', // 别名 + 'Render.GlNative': 'b74_1', // 原生 WebGL2RenderingContext + 'Renderer.Gl': 'b74_1', // 别名 + 'Render.Device': 's3h_1', // 资源侧设备包装 + 'Render.DeviceNative': 'f75_1', // 设备里的原生 GL + 'Render.Settings': 'y38_1', // 渲染开关组 + 'Renderer.Flags': 'y38_1', // 别名 + 'Render.ShadowEnabled': 'e3o_1', + 'Renderer.Shadow': 'e3o_1', // 别名 + 'Render.PostProcessEnabled': 'f3o_1', + 'Renderer.PostFx': 'f3o_1', // 别名 + 'Render.ExtraPassEnabled': 'k3o_1', + 'Renderer.FlagAux': 'k3o_1', // 别名 + 'Render.LayerQueue': 'e39_1', // 渲染层队列(20=Hud 21-24=GUI) + 'Renderer.QueueList': 'e39_1', // 别名 + 'Render.Draw': 'f3t()', // y3s → f3t 主渲染 + 'Render.DrawingWidth': 'o3c()', + 'Render.DrawingHeight': 'n3c()', + 'Scene.RootNode': 'fyx_1', // 场景根节点 + + // ---- 网格资源(Tp / sm)---- + 'Mesh.VertexGroups': 'x4w_1', + 'Mesh.VertexBuffer': 'h3x_1', + 'Mesh.VertexCount': 'w3x_1', + 'Mesh.CpuVertexData': 'x3x_1', + 'Mesh.GpuBuffer': 'v3x_1', + 'Mesh.IndexGroups': 'y4w_1', + 'Mesh.IndexBuffer': 'q4w_1', + 'Mesh.IndexBufferOld': 'u3p_1', + 'Mesh.CpuIndexData': 'p3x_1', + 'Mesh.IndexGpuBuffer': 'n3x_1', + + // ---- 射线检测(碰撞世界的射线接口)---- + 'CollisionWorld.Raycast': 'a17p()', + 'Ray.Cast': 'a17p()', // 别名 + 'Ray.StartPoint': 'y178_1', + 'Ray.HitPoint': 'y178_1', // 别名 + 'Ray.EndPoint': 'z178_1', + 'Ray.HitNormal': 'z178_1', // 别名 + 'Ray.Distance': 'a179_1', + 'Ray.HitEntity': 'q17o_1', + 'Ray.HitShape': 'q17o_1', // 别名(雷达/瞄准把第 4 参数叫 HitShape) + 'Ray.HitBody': 'b179_1', // 第 5 参数 + 'RayFilter.Context': 'l1da_1', + 'RayFilter.Query': 'l1da_1', // 别名 + + // ---- 车体朝向 / 武器(雷达、瞄准警告)---- + 'Tank.HullComponent': 'qzv_1', // 车体朝向组件识别字段 + 'Tank.HullDirection': 'wzl_1', // 车体朝向向量 + 'Tank.StateId': 'k3_1', // 状态枚举数值(与 Team.Id 同名字段,按用途分开) + 'Weapon.Component': 'n11c_1', + 'Weapon.ComponentAlt': 'q112_1', + 'Weapon.TurretRoot': 'm17a_1', + + // ---- 炮塔 / 皮肤槽位 ---- + 'Turret.InnerWrapper': 'e11p_1', + 'Turret.RootNode': 'a17q_1', + 'Skin.SlotHull': 'w14w_1', + 'Skin.SlotHullLegacy': 't14l_1', + 'Skin.SlotHullAlt': 'w14x_1', + 'Skin.SlotHullOld': 'p14l_1' }; // ===== module_87072 的导出键($_.xx),每版都可能平移 ===== @@ -401,6 +510,21 @@ return Out; } + // 自检项(只查核心字段:别名/扩展项不在样本对象上,查了会误报) + var CoreKeys = { + Entity: ['Entity.Id', 'Entity.Root', 'Entity.ComponentMap', 'Entity.Components'], + GameMode: ['GameMode.Scene', 'GameMode.TankManager', 'GameMode.TankList', + 'GameMode.OwnedTank', 'GameMode.ObservedTank', 'GameMode.LocalTeam'], + Tank: ['Tank.Fsm', 'Tank.State', 'Tank.StateValue', 'Tank.IsAlive', 'Tank.TeamSource'], + Physics: ['Physics.Position', 'Physics.Size', 'Physics.Hull', 'Physics.Body', 'Physics.BodyShapes'], + Shape: ['Shape.Name', 'Shape.Category', 'Shape.Mask', 'Shape.Body', 'Body.Owner'], + Skin: ['Skin.HullSkin', 'Skin.HullRoot', 'Skin.Parts', 'Skin.PartNode'], + Turret: ['Turret.Wrapper', 'Turret.GetRoot'], + Node: ['Node.Name', 'Node.Matrix', 'Node.Children', 'Node.Mesh'], + Camera: ['Camera.SceneRef', 'Camera.Main', 'Camera.Position', 'Camera.ViewMatrix', + 'Camera.ClipMatrix', 'Camera.Fov'] + }; + // 用当前战局对象做一次全量自检(控制台调用 __hornetSchema__.SelfCheck()) function SelfCheck() { var Gm = window.__gameMode__; @@ -415,29 +539,36 @@ if (!Tank && TankList && typeof TankList.e1 === 'function' && TankList.e1() > 0) Tank = TankList.g1(0); } catch (e) {} + var Physics = Tank ? FindPhysics(Tank) : null; var Targets = { Entity: Tank || Scene, GameMode: Gm, Tank: Tank, - Physics: Tank ? FindPhysics(Tank) : null, - Shape: null, + Physics: Physics, + Shape: Physics ? Get(Physics, 'Physics.Hull') : null, Skin: Tank, Turret: Tank, - Node: null, + Node: Tank ? Get(Tank, 'Skin.HullRoot') : null, Camera: Scene }; - Object.keys(Targets).forEach(function (Prefix) { - if (!Targets[Prefix]) { Report.Groups[Prefix] = '无样本对象'; return; } - var R = Check(Targets[Prefix], Prefix); - Report.Groups[Prefix] = R; - R.Missing.forEach(function (M) { Report.Missing.push(Prefix + '.' + M); }); + Object.keys(CoreKeys).forEach(function (Prefix) { + var Target = Targets[Prefix]; + if (!Target) { Report.Groups[Prefix] = '无样本对象'; return; } + var Ok = [], Missing = []; + CoreKeys[Prefix].forEach(function (Key) { + var V = Get(Target, Key); + if (V === undefined) Missing.push(Key + ' → ' + PathOf(Key)); + else Ok.push(Key); + }); + Report.Groups[Prefix] = { Ok: Ok, Missing: Missing }; + Missing.forEach(function (M) { Report.Missing.push(Prefix + '.' + M); }); }); if (Report.Missing.length) { console.warn('[字段抽象] 失配 ' + Report.Missing.length + ' 项:', Report.Missing); } else { - console.log('[字段抽象] 全部语义字段解析正常'); + console.log('[字段抽象] 全部核心字段解析正常'); } return Report; } @@ -484,6 +615,11 @@ // 用法:各工具文件通过 var lib = window.__hornetLib__ 访问 (function() { + // ===== 字段抽象层(语义名 → 匿名字段,总表在 hornet_schema.js)===== + var Schema = window.__hornetSchema__ || null; + function Fld(Key, Fallback) { var N = Schema ? Schema.FieldName(Key) : null; return N || Fallback; } + function Mth(Key, Fallback) { var N = Schema ? Schema.MethodName(Key) : null; return N || Fallback; } + console.log('===== 3DTank公共库加载 ====='); if (!window.__gameMode__) { @@ -493,17 +629,58 @@ // ===== 缓存队伍判断函数(ZCt/m2Fn) ===== var ZCt = null, m2Fn = null; - // ===== 字段抽象层引用(字段名统一在 hornet_schema.js 里维护) ===== - // 取不到抽象层时退回本地常量,保证单独加载本文件也能工作 - var Schema = window.__hornetSchema__ || null; - function FieldOf(Key, Fallback) { - if (!Schema) return Fallback; - var P = Schema.PathOf(Key); - return (P && P.indexOf('.') < 0 && P.indexOf('(') < 0) ? P : Fallback; - } - var TEAM_FIELD = FieldOf('Team.Field', 'b135_1'); // 队伍组件上的队伍对象字段 - var TEAM_ID_FIELD = FieldOf('Team.Id', 'k3_1'); // 队伍编号:0/1/2(2=中立) + // ===== 语义字段常量(取不到抽象层时用 fallback 字面名,保证单独加载也能工作) ===== + // 实体 + var FEntityRoot = Fld('Entity.Root', 'qz1_1'); // 组件宿主根对象 + var FEntityComponentMap = Fld('Entity.ComponentMap', 's2a_1'); // 宿主上的组件 Map + var FEntityId = Fld('Entity.Id', 'iz1_1'); // 实体 ID + // 向量 + var FVecX = Fld('Vec.X', 'f20_1'); + var FVecY = Fld('Vec.Y', 'g20_1'); + var FVecZ = Fld('Vec.Z', 'h20_1'); + // 队伍 + var FTeamField = Fld('Team.Field', 'b135_1'); // 队伍组件上的队伍对象字段 + var FTeamId = Fld('Team.Id', 'k3_1'); // 队伍编号:0/1/2(2=中立) var NEUTRAL_TEAM_ID = 2; + // 物理 + var FPhysicsPosition = Fld('Physics.Position', 'bzr_1'); // 世界位置向量 + var FPhysicsPositionOld1 = Fld('Physics.PositionOld1', 'gzh_1'); // 旧版位置字段名(兜底候选) + var FPhysicsPositionOld2 = Fld('Physics.PositionOld2', 'pz7_1'); // 更旧位置字段名(兜底候选) + // 坦克状态 + var FTankFsm = Fld('Tank.Fsm', 'p10c_1'); // 状态机字段(组件识别用) + var FTankState = Fld('Tank.State', 'k10c_1'); // 状态枚举对象 + var FTankStateValue = Fld('Tank.StateValue', 'k3_1'); // 状态数值 + // 用户信息 / 血量 / 装备分 + var FUserName = Fld('User.Name', 'c12n_1'); + var FUserClan = Fld('User.Clan', 'e12n_1'); + var FHealthMax = Fld('Health.Max', 'p12o_1'); + var FHealthCurrent = Fld('Health.Current', 'q12o_1'); + var FGearScore = Fld('Gear.Score', 'cjf_1'); + // GameMode + var FGameModeTankManager = Fld('GameMode.TankManager', 'm10b_1'); + var FGameModeOwnedTank = Fld('GameMode.OwnedTank', 'u10b_1'); + var FGameModeObservedTank = Fld('GameMode.ObservedTank', 't10b_1'); + var FGameModeLocalTeam = Fld('GameMode.LocalTeam', 'v10b_1'); + // 相机 + var FCameraSceneRef = Fld('Camera.SceneRef', 'jz1_1'); + var FCameraObject = Fld('Camera.Object', 'iyx_1'); + var FCameraPosition = Fld('Camera.Position', 't35_1'); + var FCameraViewMatrix = Fld('Camera.ViewMatrix', 'd37_1'); + var FCameraClipMatrix = Fld('Camera.ClipMatrix', 'g37_1'); + var FCameraViewport = Fld('Camera.Viewport', 'w36_1'); + var FCameraFov = Fld('Camera.Fov', 'b37_1'); + // 射线检测结果 / 过滤器 + var FRayStartPoint = Fld('Ray.StartPoint', 'y178_1'); + var FRayEndPoint = Fld('Ray.EndPoint', 'z178_1'); + var FRayDistance = Fld('Ray.Distance', 'a179_1'); + var FRayHitEntity = Fld('Ray.HitEntity', 'q17o_1'); + var FRayHitShape = Fld('Ray.HitShape', 'b179_1'); + var FRayFilterContext = Fld('RayFilter.Context', 'l1da_1'); + // 方法 + var MGameModeScene = Mth('GameMode.Scene', 'lz4'); // 场景实体 + var MTankList = Mth('GameMode.TankList', 'j11y'); // 坦克列表 + var MIsTankAlive = Mth('Tank.IsAlive', 's14j'); // 引擎可见性判定 + var MRaycast = Mth('Ray.Cast', 'a17p'); // 全局射线检测 // 加载队伍判断函数(从 module_87072 导出) // 新旧差异(main.7cfa4058): @@ -537,9 +714,9 @@ // 遍历 entity.wyj_1.r2a_1(Map<组件标识, 组件列表>),value 是列表类(s_),g1(0) 获取实际组件 // 注意:用 !== undefined 检查字段存在性,避免 falsy 值(0/""/null/false)漏判 function FindComponentByField(entity, fieldName) { - if (!entity || !entity.qz1_1 || !entity.qz1_1.s2a_1) return null; + if (!entity || !entity[FEntityRoot] || !entity[FEntityRoot][FEntityComponentMap]) return null; var result = null; - entity.qz1_1.s2a_1.forEach(function(valueList, key) { + entity[FEntityRoot][FEntityComponentMap].forEach(function(valueList, key) { if (result) return; try { var comp = (valueList && typeof valueList.g1 === 'function') ? valueList.g1(0) : valueList; @@ -557,18 +734,18 @@ // njt() 里 `bzr_1.l27(上一物理态位置, 当前物理态位置, 插值)` —— bzr_1 就是世界位置。 // ⚠ xzq_1 是车体半尺寸(用于包围盒/出生点:`-xzq_1.h20_1/2 + 10`),绝不能当位置用。 // 返回 {x, y, z},x/y 水平面,z 高度 - var POS_FIELDS = ['bzr_1', 'gzh_1', 'pz7_1']; + var POS_FIELDS = [FPhysicsPosition, FPhysicsPositionOld1, FPhysicsPositionOld2]; function GetTankPosition(tank) { for (var i = 0; i < POS_FIELDS.length; i++) { var Key = POS_FIELDS[i]; var Comp = null; try { Comp = FindComponentByField(tank, Key); } catch (e) { Comp = null; } var V = Comp && Comp[Key]; - if (V && typeof V.f20_1 === 'number' && typeof V.g20_1 === 'number' && typeof V.h20_1 === 'number') { + if (V && typeof V[FVecX] === 'number' && typeof V[FVecY] === 'number' && typeof V[FVecZ] === 'number') { // 数值有效性校验:NaN/超大值一律视为读取失败(避免雷达画到画布外) - if (isFinite(V.f20_1) && isFinite(V.g20_1) && isFinite(V.h20_1) && - Math.abs(V.f20_1) < 1e7 && Math.abs(V.g20_1) < 1e7 && Math.abs(V.h20_1) < 1e7) { - return { x: V.f20_1, y: V.g20_1, z: V.h20_1 }; + if (isFinite(V[FVecX]) && isFinite(V[FVecY]) && isFinite(V[FVecZ]) && + Math.abs(V[FVecX]) < 1e7 && Math.abs(V[FVecY]) < 1e7 && Math.abs(V[FVecZ]) < 1e7) { + return { x: V[FVecX], y: V[FVecY], z: V[FVecZ] }; } } } @@ -578,13 +755,13 @@ // ===== 坦克组件(lxt / TankComponent)===== // 特征:有 k10c_1(状态枚举)与 p10c_1(fsm) function GetTankComponent(tank) { - if (!tank || !tank.qz1_1 || !tank.qz1_1.s2a_1) return null; + if (!tank || !tank[FEntityRoot] || !tank[FEntityRoot][FEntityComponentMap]) return null; var Found = null; - tank.qz1_1.s2a_1.forEach(function(valueList, key) { + tank[FEntityRoot][FEntityComponentMap].forEach(function(valueList, key) { if (Found) return; try { var comp = (valueList && typeof valueList.g1 === 'function') ? valueList.g1(0) : valueList; - if (comp && comp.p10c_1 && comp.k10c_1 !== undefined) Found = comp; + if (comp && comp[FTankFsm] && comp[FTankState] !== undefined) Found = comp; } catch(e) {} }); return Found; @@ -594,7 +771,7 @@ // 状态枚举:0=DEAD, 1=DEAD_PHANTOM, 2=SEMI_ACTIVE, 3=ACTIVE, 4=DISABLED, 5=TELEPORT function GetTankState(tank) { var C = GetTankComponent(tank); - return (C && C.k10c_1) ? C.k10c_1.k3_1 : -1; + return (C && C[FTankState]) ? C[FTankState][FTankStateValue] : -1; } // 可见/战斗中的坦克:**直接调用引擎自己的判定 s14j()** @@ -602,7 +779,7 @@ // 旧实现只认 2/3,会漏掉传送态;死亡态 0/1/4 会被正确排除 function IsTankAlive(tank) { var C = GetTankComponent(tank); - try { if (C && typeof C.s14j === 'function') return !!C.s14j(); } catch (e) {} + try { if (C && typeof C[MIsTankAlive] === 'function') return !!C[MIsTankAlive](); } catch (e) {} var s = GetTankState(tank); return s === 2 || s === 3 || s === 5; } @@ -610,20 +787,20 @@ // ===== 获取坦克名称(yot 用户信息组件) ===== // s120_1 = userName, u120_1 = clanTag(可为null), t120_1 = userRank function GetTankName(tank) { - var userComp = FindComponentByField(tank, 'c12n_1'); + var userComp = FindComponentByField(tank, FUserName); if (!userComp) return null; - var name = userComp.c12n_1 || ''; - var clanTag = userComp.e12n_1 || ''; + var name = userComp[FUserName] || ''; + var clanTag = userComp[FUserClan] || ''; return clanTag ? (clanTag + ' ' + name) : name; } // ===== 获取坦克血量(Vkt 血量组件) ===== // f122_1 = 最大血量(max), g122_1 = 当前血量(current, 死亡时=0) function GetTankHealth(tank) { - var healthComp = FindComponentByField(tank, 'q12o_1'); + var healthComp = FindComponentByField(tank, FHealthCurrent); if (!healthComp) return null; - var max = healthComp.p12o_1 || 0; - var cur = healthComp.q12o_1 || 0; + var max = healthComp[FHealthMax] || 0; + var cur = healthComp[FHealthCurrent] || 0; if (max <= 0) return null; return { current: cur, max: max, percent: cur / max }; } @@ -631,9 +808,9 @@ // ===== 装备分数(战斗 GS:BattleGearScoreCC.score 落在组件的 cjf_1 上) ===== // 来源:module_85183 的 BattleGearScoreCC(score = cjf_1);战斗内每辆坦克各有一份 function GetGearScore(tank) { - var c = FindComponentByField(tank, 'cjf_1'); - if (!c || typeof c.cjf_1 !== 'number') return null; - return c.cjf_1; + var c = FindComponentByField(tank, FGearScore); + if (!c || typeof c[FGearScore] !== 'number') return null; + return c[FGearScore]; } // ===== 人机识别 ===== @@ -646,10 +823,10 @@ var MaxScore = typeof opt.maxScore === 'number' ? opt.maxScore : 8000; BotSet = new Set(); var gm = window.__gameMode__; - if (!gm || !gm.m10b_1) return BotSet; + if (!gm || !gm[FGameModeTankManager]) return BotSet; var playerTank = GetPlayerTank(gm); var list, n; - try { list = gm.m10b_1.j11y(); n = list.e1(); } catch (e) { return BotSet; } + try { list = gm[FGameModeTankManager][MTankList](); n = list.e1(); } catch (e) { return BotSet; } var Groups = { ally: {}, enemy: {} }; // 组 → { 分数 → [坦克...] } for (var i = 0; i < n; i++) { var tank = list.g1(i); @@ -677,9 +854,9 @@ var g = window.__gameMode__; R.gm = !!g; if (!g) return R; - try { R.scene = !!g.lz4().jz1_1; } catch (e) { R.errors.push('scene: ' + e.message); } + try { R.scene = !!g[MGameModeScene]()[FCameraSceneRef]; } catch (e) { R.errors.push('scene: ' + e.message); } var list = null; - try { list = g.m10b_1.j11y(); R.tanks = list.e1(); } catch (e) { R.errors.push('tankList: ' + e.message); } + try { list = g[FGameModeTankManager][MTankList](); R.tanks = list.e1(); } catch (e) { R.errors.push('tankList: ' + e.message); } if (list) { var N = Math.min(list.e1(), 8); for (var i = 0; i < N; i++) { @@ -693,7 +870,7 @@ for (var j = 0; j < POS_FIELDS.length; j++) { var K = POS_FIELDS[j], C = null; try { C = FindComponentByField(T, K); } catch (e) { C = null; } - if (C && C[K] && typeof C[K].f20_1 === 'number') { Src = K; break; } + if (C && C[K] && typeof C[K][FVecX] === 'number') { Src = K; break; } } R.posHit[Src] = (R.posHit[Src] || 0) + 1; } else { @@ -701,13 +878,13 @@ // 失败时把该坦克组件的字段名与"候选向量"打印出来,便于定位 try { var Fields = []; - T.qz1_1.s2a_1.forEach(function (VL) { + T[FEntityRoot][FEntityComponentMap].forEach(function (VL) { var C = (VL && typeof VL.g1 === 'function') ? VL.g1(0) : VL; if (!C) return; for (var K2 in C) { var V2 = C[K2]; if (V2 && typeof V2 === 'object') { - var Kind = (typeof V2.f20_1 === 'number') ? 'O$' : (typeof V2.length === 'number' && V2.length >= 15 ? 'M' : ''); + var Kind = (typeof V2[FVecX] === 'number') ? 'O$' : (typeof V2.length === 'number' && V2.length >= 15 ? 'M' : ''); if (Kind) Fields.push(K2 + ':' + Kind); } } @@ -728,12 +905,12 @@ // checkPosition: 是否检查位置有效性(默认 true) function GetPlayerTank(gm, checkPosition) { if (checkPosition === undefined) checkPosition = true; - var t = gm.u10b_1; + var t = gm[FGameModeOwnedTank]; if (t && (!checkPosition || GetTankPosition(t))) return t; - t = gm.t10b_1; + t = gm[FGameModeObservedTank]; if (t && (!checkPosition || GetTankPosition(t))) return t; try { - var tl = gm.m10b_1.j11y(); + var tl = gm[FGameModeTankManager][MTankList](); if (tl.e1() > 0) { t = tl.g1(0); if (t && (!checkPosition || GetTankPosition(t))) return t; @@ -758,8 +935,8 @@ if (ZCt) { try { var T = ZCt(tank); if (T) return T; } catch (e) {} } - var Comp = FindComponentByField(tank, TEAM_FIELD); - return Comp ? Comp[TEAM_FIELD] : null; + var Comp = FindComponentByField(tank, FTeamField); + return Comp ? Comp[FTeamField] : null; } // 读队伍编号:0 / 1 = 两个阵营,2 = 中立(q$());读不到返回 -1 @@ -767,7 +944,7 @@ // (module_87072 里 P7()/$ct() 都按 k3_1 分派) function GetTeamId(Team) { if (!Team) return -1; - var Id = Team[TEAM_ID_FIELD]; + var Id = Team[FTeamId]; return (typeof Id === 'number') ? Id : -1; } @@ -776,9 +953,9 @@ function RememberLocalTeam(gm) { var t = null; - try { t = gm.u10b_1; } catch (e) { t = null; } + try { t = gm[FGameModeOwnedTank]; } catch (e) { t = null; } if (t) { - var Id = t.iz1_1; + var Id = t[FEntityId]; if (LocalTeam && LocalTeamTankId === Id) return; var Team = ReadTeamRaw(t); var TeamId = GetTeamId(Team); @@ -788,7 +965,7 @@ // u10b_1 为空(死亡/观战):保持已有缓存;若还没缓存过,再试 GameMode 上的本地队伍字段 if (LocalTeam) return; try { - var G = gm.v10b_1; + var G = gm[FGameModeLocalTeam]; var GId = GetTeamId(G); if (GId >= 0 && GId !== NEUTRAL_TEAM_ID) LocalTeam = G; } catch (e) {} @@ -814,7 +991,7 @@ // 可靠基准:本地拥有坦克的队伍(缓存)→ GameMode 上的本地队伍字段 // 观战目标(t10b_1)可能是敌人,绝不作为基准 var BaseTeam = LocalTeam; - if (!BaseTeam && gm) { try { BaseTeam = gm.v10b_1; } catch (e) {} } + if (!BaseTeam && gm) { try { BaseTeam = gm[FGameModeLocalTeam]; } catch (e) {} } var BaseId = GetTeamId(BaseTeam); if (BaseId < 0 || BaseId === NEUTRAL_TEAM_ID) { var Last2 = TankFriendCache.get(tank); // 基准不可靠(极早期):沿用上次 @@ -826,7 +1003,7 @@ if (!TeamJudgeLogged.has(tank)) { TeamJudgeLogged.add(tank); try { - console.log('[敌我] id=' + tank.iz1_1 + ' 队伍=' + TankId + ' 本地=' + BaseId + + console.log('[敌我] id=' + tank[FEntityId] + ' 队伍=' + TankId + ' 本地=' + BaseId + ' → ' + (Friendly ? '友方' : '敌方') + ' name=' + (GetTankName(tank) || '?')); } catch (e) {} } @@ -847,12 +1024,12 @@ // heading 供雷达使用(forward = (sin(heading), cos(heading)),水平面) function GetCameraInfo(gm) { try { - var scene = gm.lz4().jz1_1; - var cam = scene && scene.iyx_1; - if (!cam || !cam.t35_1) return null; - var P = cam.t35_1; - var pos = { x: P.f20_1, y: P.g20_1, z: P.h20_1 }; - var V = cam.d37_1; + var scene = gm[MGameModeScene]()[FCameraSceneRef]; + var cam = scene && scene[FCameraObject]; + if (!cam || !cam[FCameraPosition]) return null; + var P = cam[FCameraPosition]; + var pos = { x: P[FVecX], y: P[FVecY], z: P[FVecZ] }; + var V = cam[FCameraViewMatrix]; var fx = 0, fy = 1, fz = 0; if (V && typeof V.length === 'number' && V.length >= 11) { fx = -V[2]; fy = -V[6]; fz = -V[10]; @@ -860,14 +1037,14 @@ fx /= len; fy /= len; fz /= len; } var viewW = 0, viewH = 0; - try { viewW = cam.w36_1.h29(); viewH = cam.w36_1.g29(); } catch (e) {} + try { viewW = cam[FCameraViewport].h29(); viewH = cam[FCameraViewport].g29(); } catch (e) {} return { pos: pos, yaw: Math.atan2(-fx, fy), pitch: Math.asin(Math.max(-1, Math.min(1, fz))), heading: Math.atan2(fx, fy), - fov: cam.b37_1 || 1.0471976, - matrix: cam.g37_1 || null, + fov: cam[FCameraFov] || 1.0471976, + matrix: cam[FCameraClipMatrix] || null, viewW: viewW, viewH: viewH }; @@ -880,14 +1057,14 @@ // g16x(startVec, dirVec, maxDist, resultObj, filterObj) = 全局射线检测 function GetUMtComponent(gm) { try { - var gmEntity = gm.lz4(); - if (!gmEntity || !gmEntity.qz1_1 || !gmEntity.qz1_1.s2a_1) return null; + var gmEntity = gm[MGameModeScene](); + if (!gmEntity || !gmEntity[FEntityRoot] || !gmEntity[FEntityRoot][FEntityComponentMap]) return null; var uMt = null; - gmEntity.qz1_1.s2a_1.forEach(function(valueList, key) { + gmEntity[FEntityRoot][FEntityComponentMap].forEach(function(valueList, key) { if (uMt) return; try { var comp = (valueList && typeof valueList.g1 === 'function') ? valueList.g1(0) : valueList; - if (comp && typeof comp.a17p === 'function') uMt = comp; + if (comp && typeof comp[MRaycast] === 'function') uMt = comp; } catch(e) {} }); return uMt; @@ -899,9 +1076,9 @@ // ===== 构造 mock O$ 向量(用于射线检测接口) ===== function MkVec(x, y, z) { return { - f20_1: x || 0, g20_1: y || 0, h20_1: z || 0, - k26: function(t) { this.f20_1 = t.f20_1; this.g20_1 = t.g20_1; this.h20_1 = t.h20_1; }, - n23: function(x, y, z) { this.f20_1 = x; this.g20_1 = y; this.h20_1 = z; return this; } + [FVecX]: x || 0, [FVecY]: y || 0, [FVecZ]: z || 0, + k26: function(t) { this[FVecX] = t[FVecX]; this[FVecY] = t[FVecY]; this[FVecZ] = t[FVecZ]; }, + n23: function(x, y, z) { this[FVecX] = x; this[FVecY] = y; this[FVecZ] = z; return this; } }; } @@ -914,28 +1091,28 @@ var startVec = MkVec(fromPos.x, fromPos.y, fromPos.z); var dirVec = MkVec(dirX, dirY, dirZ); var result = { - y178_1: MkVec(0, 0, 0), - z178_1: MkVec(0, 0, 0), - a179_1: maxDist, - q17o_1: null, - b179_1: null, + [FRayStartPoint]: MkVec(0, 0, 0), + [FRayEndPoint]: MkVec(0, 0, 0), + [FRayDistance]: maxDist, + [FRayHitEntity]: null, + [FRayHitShape]: null, r17o: function(t, n, i, r, e) { - this.y178_1.k26(t); - this.z178_1.k26(n); - this.a179_1 = i; - this.q17o_1 = r; - this.b179_1 = e; + this[FRayStartPoint].k26(t); + this[FRayEndPoint].k26(n); + this[FRayDistance] = i; + this[FRayHitEntity] = r; + this[FRayHitShape] = e; }, - d3: function() { this.b179_1 = null; } + d3: function() { this[FRayHitShape] = null; } }; var filter = { - l1da_1: { h1: function() { return false; }, d3: function() {} }, + [FRayFilterContext]: { h1: function() { return false; }, d3: function() {} }, w179: function() { return true; }, - d3: function() { this.l1da_1.d3(); }, + d3: function() { this[FRayFilterContext].d3(); }, m1da: function() {} }; - var hit = uMt.a17p(startVec, dirVec, maxDist, result, filter); - return (hit && result.a179_1 < maxDist) ? result.a179_1 : -1; + var hit = uMt[MRaycast](startVec, dirVec, maxDist, result, filter); + return (hit && result[FRayDistance] < maxDist) ? result[FRayDistance] : -1; } catch(e) { return -1; } @@ -1144,6 +1321,29 @@ // 功能:在屏幕右上角显示2D雷达,显示所有坦克的相对位置 (function() { + // ===== 字段抽象层(语义名 → 匿名字段,总表在 hornet_schema.js)===== + var Schema = window.__hornetSchema__ || null; + function Fld(Key, Fallback) { var N = Schema ? Schema.FieldName(Key) : null; return N || Fallback; } + function Mth(Key, Fallback) { var N = Schema ? Schema.MethodName(Key) : null; return N || Fallback; } + + // ===== 匿名字段常量 ===== + var FVecX = Fld('Vec.X', 'f20_1'); + var FVecY = Fld('Vec.Y', 'g20_1'); + var FVecZ = Fld('Vec.Z', 'h20_1'); + var FEntityId = Fld('Entity.Id', 'iz1_1'); + var FEntityRoot = Fld('Entity.Root', 'qz1_1'); + var FEntityComponentMap = Fld('Entity.ComponentMap', 's2a_1'); + var FTankHullComponent = Fld('Tank.HullComponent', 'qzv_1'); + var FTankHullDirection = Fld('Tank.HullDirection', 'wzl_1'); + var MGameModeScene = Mth('GameMode.Scene', 'lz4'); + var MCollisionRaycast = Mth('CollisionWorld.Raycast', 'a17p'); + var FRayHitPoint = Fld('Ray.HitPoint', 'y178_1'); + var FRayHitNormal = Fld('Ray.HitNormal', 'z178_1'); + var FRayDistance = Fld('Ray.Distance', 'a179_1'); + var FRayHitShape = Fld('Ray.HitShape', 'q17o_1'); + var FRayHitBody = Fld('Ray.HitBody', 'b179_1'); + var FRayFilterQuery = Fld('RayFilter.Query', 'l1da_1'); + function _start() { console.log('===== 3DTank雷达启动 ====='); @@ -1179,12 +1379,12 @@ // ===== 获取坦克方向(Kkt 组件的 dzc_1) ===== function getTankDirection(tank) { - var hullComp = FindComponentByField(tank, 'qzv_1'); - if (hullComp && hullComp.wzl_1) { + var hullComp = FindComponentByField(tank, FTankHullComponent); + if (hullComp && hullComp[FTankHullDirection]) { return { - x: hullComp.wzl_1.f20_1, - y: hullComp.wzl_1.g20_1, - z: hullComp.wzl_1.h20_1 + x: hullComp[FTankHullDirection][FVecX], + y: hullComp[FTankHullDirection][FVecY], + z: hullComp[FTankHullDirection][FVecZ] }; } return null; @@ -1223,13 +1423,13 @@ function getUMtComponent() { if (UMtComponent) return UMtComponent; try { - var gmEntity = gm.lz4(); - if (!gmEntity || !gmEntity.qz1_1 || !gmEntity.qz1_1.s2a_1) return null; - gmEntity.qz1_1.s2a_1.forEach(function(valueList, key) { + var gmEntity = gm[MGameModeScene](); + if (!gmEntity || !gmEntity[FEntityRoot] || !gmEntity[FEntityRoot][FEntityComponentMap]) return null; + gmEntity[FEntityRoot][FEntityComponentMap].forEach(function(valueList, key) { if (UMtComponent) return; try { var comp = (valueList && typeof valueList.g1 === 'function') ? valueList.g1(0) : valueList; - if (comp && typeof comp.a17p === 'function') UMtComponent = comp; + if (comp && typeof comp[MCollisionRaycast] === 'function') UMtComponent = comp; } catch(e) {} }); if (UMtComponent) console.log('[雷达] uMt 碰撞检测器已获取'); @@ -1251,21 +1451,21 @@ var startVec = MkVec(fromPos.x, fromPos.y, fromPos.z); var dirVec = MkVec(dirX, dirY, dirZ); var result = { - y178_1: MkVec(0,0,0), z178_1: MkVec(0,0,0), - a179_1: maxDist, q17o_1: null, b179_1: null, + [FRayHitPoint]: MkVec(0,0,0), [FRayHitNormal]: MkVec(0,0,0), + [FRayDistance]: maxDist, [FRayHitShape]: null, [FRayHitBody]: null, r17o: function(t, n, i, r, e) { - this.y178_1.k26(t); this.z178_1.k26(n); - this.a179_1 = i; this.q17o_1 = r; this.b179_1 = e; + this[FRayHitPoint].k26(t); this[FRayHitNormal].k26(n); + this[FRayDistance] = i; this[FRayHitShape] = r; this[FRayHitBody] = e; }, - d3: function() { this.b179_1 = null; } + d3: function() { this[FRayHitBody] = null; } }; var filter = { - l1da_1: { h1: function() { return false; }, d3: function() {} }, + [FRayFilterQuery]: { h1: function() { return false; }, d3: function() {} }, w179: function() { return true; }, d3: function() {}, m1da: function() {} }; - var hit = uMt.a17p(startVec, dirVec, maxDist, result, filter); - return (hit && result.a179_1 < maxDist) ? result.a179_1 : -1; + var hit = uMt[MCollisionRaycast](startVec, dirVec, maxDist, result, filter); + return (hit && result[FRayDistance] < maxDist) ? result[FRayDistance] : -1; } catch(e) { return -1; } @@ -1830,7 +2030,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.iz1_1 : '?'), 5, 24); + ctx.fillText('Player: ' + (playerTank ? playerTank[FEntityId] : '?'), 5, 24); } else { ctx.fillText('Player: NOT FOUND', 5, 24); } @@ -1925,6 +2125,39 @@ var FSkinHullRoot = Fld('Skin.HullRoot', 'h159_1'); // 车身渲染树根 var FSkinParts = Fld('Skin.Parts', 'g17r_1'); // 部件表 var FSkinPartNode = Fld('Skin.PartNode', 'j17r_1'); // 部件 → BP 节点 + var FNodeMatrixOld = Fld('Node.MatrixOld', 'n35_1'); // 旧版世界矩阵(兜底) + + // 实体 / 向量 / 战斗实例 + var FEntityId = Fld('Entity.Id', 'iz1_1'); // 实体 ID + var FEntityRoot = Fld('Entity.Root', 'qz1_1'); // 组件宿主根对象 + var FEntityComponentMap = Fld('Entity.ComponentMap', 's2a_1'); // 宿主上的组件 Map + var FVecX = Fld('Vec.X', 'f20_1'); // 向量 X + var FVecY = Fld('Vec.Y', 'g20_1'); // 向量 Y + var FVecZ = Fld('Vec.Z', 'h20_1'); // 向量 Z + var FGameModeTankManager = Fld('GameMode.TankManager', 'm10b_1'); // 坦克管理器 + var FTeamId = Fld('Team.Id', 'k3_1'); // 队伍编号 + + // 炮塔皮肤(LUt / CUt 包装) + var FTurretWrapper = Fld('Turret.Wrapper', 'a11p_1'); // 炮塔组件包装对象 + var FTurretInnerWrapper = Fld('Turret.InnerWrapper', 'e11p_1'); // 包装内的 CUt 包装 + var FTurretRootNode = Fld('Turret.RootNode', 'a17q_1'); // 炮塔 BP 根节点 + + // mesh 资源(Tp / J$ / Y$) + var FMeshVertexGroups = Fld('Mesh.VertexGroups', 'x4w_1'); // 顶点组列表 + var FMeshVertexBuffer = Fld('Mesh.VertexBuffer', 'h3x_1'); // 顶点缓冲资源 + var FMeshGpuBuffer = Fld('Mesh.GpuBuffer', 'v3x_1'); // GPU 缓冲包装 + var FMeshVertexCount = Fld('Mesh.VertexCount', 'w3x_1'); // 顶点数 + var FMeshCpuVertexData = Fld('Mesh.CpuVertexData', 'x3x_1'); // CPU 端顶点原始数据 + var FMeshIndexGroups = Fld('Mesh.IndexGroups', 'y4w_1'); // 索引组列表 + var FMeshIndexBuffer = Fld('Mesh.IndexBuffer', 'q4w_1'); // 索引缓冲资源 + var FMeshIndexBufferOld = Fld('Mesh.IndexBufferOld', 'u3p_1'); // 旧版索引缓冲字段 + var FMeshCpuIndexData = Fld('Mesh.CpuIndexData', 'p3x_1'); // CPU 端索引原始数据 + var FMeshIndexGpuBuffer = Fld('Mesh.IndexGpuBuffer', 'n3x_1'); // 索引 GPU 缓冲包装 + + // 渲染设备 / 原生缓冲 + var FRenderDevice = Fld('Render.Device', 's3h_1'); // GL 包装器 + var FRenderDeviceNative = Fld('Render.DeviceNative', 'f75_1'); // 原生 WebGL2RenderingContext + var FRenderDrawBuffer = Fld('Render.DrawBuffer', 'd75_1'); // 原生 WebGLBuffer // ===== 当前模式 ===== var Mode = 'outline'; // 'wireframe' | 'outline' @@ -2137,11 +2370,11 @@ var Cached = SkinCompCache.get(entity); if (Cached) return Cached; var comp = FindComponentByField(entity, FSkinHullSkin); - if (!comp) comp = FindComponentByField(entity, 'a17q_1'); + if (!comp) comp = FindComponentByField(entity, FTurretRootNode); // 兜底:按结构找 —— 组件字段值是"皮肤对象"(含 g17r_1 部件表) if (!comp) { try { - entity.qz1_1.s2a_1.forEach(function (VL) { + entity[FEntityRoot][FEntityComponentMap].forEach(function (VL) { if (comp) return; var C = (VL && typeof VL.g1 === 'function') ? VL.g1(0) : VL; if (!C) return; @@ -2168,13 +2401,13 @@ // 炮塔 BP 根节点 // 新版:weaponSkin 组件(类 LUt,构造首字段 a11p_1)里 e11p_1 = CUt 包装,其 a17q_1 = 炮塔节点 function GetTurretNode(entity) { - var wp = FindComponentByField(entity, 'a11p_1'); + var wp = FindComponentByField(entity, FTurretWrapper); if (wp) { - if (wp.e11p_1 && wp.e11p_1.a17q_1) return wp.e11p_1.a17q_1; + if (wp[FTurretInnerWrapper] && wp[FTurretInnerWrapper][FTurretRootNode]) return wp[FTurretInnerWrapper][FTurretRootNode]; if (typeof wp.u11p === 'function') { try { return wp.u11p(); } catch (e) {} } } var comp = GetSkinComponent(entity); - if (comp && comp.a17q_1) return comp.a17q_1; + if (comp && comp[FTurretRootNode]) return comp[FTurretRootNode]; return null; } @@ -2201,8 +2434,8 @@ // ⚠ 旧字段 d3x_1 / s3h_1.b74_1 / v3x_1.z73_1 在新版分别是"渲染参数 / 雾色 uniform / uniform 槽位",全错 function GetGlFromResource(Res) { if (!Res) return null; - var Dev = Res.s3h_1; - return (Dev && Dev.f75_1) ? Dev.f75_1 : null; + var Dev = Res[FRenderDevice]; + return (Dev && Dev[FRenderDeviceNative]) ? Dev[FRenderDeviceNative] : null; } // 把引擎的 CPU 端缓冲区包装转成 TypedArray @@ -2235,17 +2468,17 @@ } function ReadVertexPositionsFromTp(tp) { - if (!tp || !tp.x4w_1 || typeof tp.x4w_1.e1 !== 'function' || tp.x4w_1.e1() === 0) return null; - var group = tp.x4w_1.g1(0); + if (!tp || !tp[FMeshVertexGroups] || typeof tp[FMeshVertexGroups].e1 !== 'function' || tp[FMeshVertexGroups].e1() === 0) return null; + var group = tp[FMeshVertexGroups].g1(0); if (!group) return null; - var Buf = group.h3x_1; // J$ + var Buf = group[FMeshVertexBuffer]; // J$ if (!Buf) return null; // ① 优先 CPU 端原始数据:未上传 GPU 的 mesh 也能读到(v3x_1.d75_1 此时为 null) - var Cpu = ToTypedArray(Buf.x3x_1, 'f32'); + var Cpu = ToTypedArray(Buf[FMeshCpuVertexData], 'f32'); if (Cpu && Cpu.length) return Cpu; // ② GPU 回读(引擎已上传并释放 CPU 数据时) var gl = GetGlFromResource(Buf); - var webglBuf = Buf.v3x_1 && Buf.v3x_1.d75_1; + var webglBuf = Buf[FMeshGpuBuffer] && Buf[FMeshGpuBuffer][FRenderDrawBuffer]; if (!gl || !webglBuf) return null; gl.bindBuffer(gl.ARRAY_BUFFER, webglBuf); var bufSize = gl.getBufferParameter(gl.ARRAY_BUFFER, gl.BUFFER_SIZE); @@ -2258,21 +2491,21 @@ // ===== 从GPU读取索引数据 ===== // y4w_1 = 索引列表,每项 { q4w_1: 索引缓冲资源 Y$ }(旧版是 il.u3p_1) function ReadIndicesFromTp(tp) { - if (!tp.y4w_1 || typeof tp.y4w_1.e1 !== 'function') return null; - var meshCount = tp.y4w_1.e1(); + if (!tp[FMeshIndexGroups] || typeof tp[FMeshIndexGroups].e1 !== 'function') return null; + var meshCount = tp[FMeshIndexGroups].e1(); if (meshCount === 0) return null; var allIndices = []; for (var m = 0; m < meshCount; m++) { - var il = tp.y4w_1.g1(m); + var il = tp[FMeshIndexGroups].g1(m); if (!il) continue; - var IdxBuf = il.q4w_1 || il.u3p_1; + var IdxBuf = il[FMeshIndexBuffer] || il[FMeshIndexBufferOld]; if (!IdxBuf) continue; // ① CPU 端索引数据(Y$.p3x_1),未上传时也能用 - var Cpu = ToTypedArray(IdxBuf.p3x_1, 'u16'); + var Cpu = ToTypedArray(IdxBuf[FMeshCpuIndexData], 'u16'); if (Cpu && Cpu.length) { allIndices.push(Cpu); continue; } // ② GPU 回读 var gl = GetGlFromResource(IdxBuf); - var webglBuf = IdxBuf.n3x_1 && IdxBuf.n3x_1.d75_1; + var webglBuf = IdxBuf[FMeshIndexGpuBuffer] && IdxBuf[FMeshIndexGpuBuffer][FRenderDrawBuffer]; if (!gl || !webglBuf) continue; gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, webglBuf); var bufSize = gl.getBufferParameter(gl.ELEMENT_ARRAY_BUFFER, gl.BUFFER_SIZE); @@ -2308,8 +2541,8 @@ var stride = 0; // 优先用引擎记录的顶点数反推 stride(J$.w3x_1 = 顶点数) try { - var G0 = tp.x4w_1.g1(0); - var Vc = G0 && G0.h3x_1 && G0.h3x_1.w3x_1; + var G0 = tp[FMeshVertexGroups].g1(0); + var Vc = G0 && G0[FMeshVertexBuffer] && G0[FMeshVertexBuffer][FMeshVertexCount]; if (Vc > 0 && positions.length % Vc === 0) stride = Math.round(positions.length / Vc); } catch (e) {} if (!stride || stride < 3 || stride > 20) { @@ -2337,23 +2570,23 @@ GpuFailLogged.add(tp); var D = {}; try { - var G = tp.x4w_1 && typeof tp.x4w_1.g1 === 'function' ? tp.x4w_1.g1(0) : null; - var B = G && G.h3x_1; - var Dev = B && B.s3h_1; - var I = tp.y4w_1 && typeof tp.y4w_1.e1 === 'function' && tp.y4w_1.e1() > 0 ? tp.y4w_1.g1(0) : null; - var IB = I && (I.q4w_1 || I.u3p_1); + var G = tp[FMeshVertexGroups] && typeof tp[FMeshVertexGroups].g1 === 'function' ? tp[FMeshVertexGroups].g1(0) : null; + var B = G && G[FMeshVertexBuffer]; + var Dev = B && B[FRenderDevice]; + var I = tp[FMeshIndexGroups] && typeof tp[FMeshIndexGroups].e1 === 'function' && tp[FMeshIndexGroups].e1() > 0 ? tp[FMeshIndexGroups].g1(0) : null; + var IB = I && (I[FMeshIndexBuffer] || I[FMeshIndexBufferOld]); D = { - 顶点组: tp.x4w_1 && tp.x4w_1.e1 ? tp.x4w_1.e1() : -1, + 顶点组: tp[FMeshVertexGroups] && tp[FMeshVertexGroups].e1 ? tp[FMeshVertexGroups].e1() : -1, 顶点缓冲资源: !!B, - 缓冲包装: !!(B && B.v3x_1), - 原生Buffer: !!(B && B.v3x_1 && B.v3x_1.d75_1), + 缓冲包装: !!(B && B[FMeshGpuBuffer]), + 原生Buffer: !!(B && B[FMeshGpuBuffer] && B[FMeshGpuBuffer][FRenderDrawBuffer]), 设备: !!Dev, - 原生GL: !!(Dev && Dev.f75_1), - 顶点数: B ? B.w3x_1 : -1, + 原生GL: !!(Dev && Dev[FRenderDeviceNative]), + 顶点数: B ? B[FMeshVertexCount] : -1, 位置长度: positions ? positions.length : -1, - 索引组: tp.y4w_1 && tp.y4w_1.e1 ? tp.y4w_1.e1() : -1, + 索引组: tp[FMeshIndexGroups] && tp[FMeshIndexGroups].e1 ? tp[FMeshIndexGroups].e1() : -1, 索引资源: !!IB, - 索引Buffer: !!(IB && IB.n3x_1 && IB.n3x_1.d75_1), + 索引Buffer: !!(IB && IB[FMeshIndexGpuBuffer] && IB[FMeshIndexGpuBuffer][FRenderDrawBuffer]), 索引个数: indices ? indices.length : -1 }; } catch (e) {} @@ -2442,7 +2675,7 @@ if (!Node) return null; var M = Node[FNodeMatrix]; if (!M || typeof M.length !== 'number' || M.length < 16) { - M = Node.n35_1; // 旧版矩阵字段 + M = Node[FNodeMatrixOld]; // 旧版矩阵字段 if (!M || typeof M.length !== 'number' || M.length < 16) return null; } return { x: M[12], y: M[13], z: M[14] }; @@ -2509,7 +2742,7 @@ Srcs.push({ Comp: Comp, Path: Path }); } try { - tank.qz1_1.s2a_1.forEach(function (VL) { + tank[FEntityRoot][FEntityComponentMap].forEach(function (VL) { var comp = (VL && typeof VL.g1 === 'function') ? VL.g1(0) : VL; if (!IsPlainObj(comp)) return; for (var K in comp) { @@ -2617,9 +2850,9 @@ function IsNodeValid(bpNode, pos, tankIsMoving, diagStats) { // 世界矩阵:新版是 p35_1(zl 基类构造里 = hl(),s3k/t3k/u3k 读 p35_1[12..14]) // 旧版是 n35_1;新版 n35_1 已变成布尔标志 - var M = bpNode.p35_1; + var M = bpNode[FNodeMatrix]; if (!M || typeof M.length !== 'number' || M.length < 16) { - var OldM = bpNode.n35_1; + var OldM = bpNode[FNodeMatrixOld]; M = (OldM && typeof OldM.length === 'number' && OldM.length >= 16) ? OldM : null; } if (!M) return null; @@ -2971,7 +3204,7 @@ for (var K in Obj) { try { var V = Obj[K]; - if (V && typeof V === 'object' && typeof V.k3_1 === 'number' && typeof V.equals === 'function') return V; + if (V && typeof V === 'object' && typeof V[FTeamId] === 'number' && typeof V.equals === 'function') return V; } catch (e) {} } return null; @@ -2981,7 +3214,7 @@ function IsGoldBoxEntity(Entity) { if (!Entity || !Entity.j10q_1) return false; var Type = FindEnumOf(Entity.j10q_1); - return !!(Type && Type.k3_1 === GOLD_BOX_TYPE); + return !!(Type && Type[FTeamId] === GOLD_BOX_TYPE); } function RegisterGoldBox(Entity) { @@ -3019,12 +3252,12 @@ try { var V = Bonus[K]; if (!V || typeof V !== 'object') continue; - if (V.n35_1 && V.n35_1.length >= 15) { - var M = V.n35_1; + if (V[FNodeMatrixOld] && V[FNodeMatrixOld].length >= 15) { + var M = V[FNodeMatrixOld]; return { x: M[12], y: M[13], z: M[14] }; } - if (typeof V.f20_1 === 'number' && typeof V.g20_1 === 'number' && typeof V.h20_1 === 'number') { - return { x: V.f20_1, y: V.g20_1, z: V.h20_1 }; + if (typeof V[FVecX] === 'number' && typeof V[FVecY] === 'number' && typeof V[FVecZ] === 'number') { + return { x: V[FVecX], y: V[FVecY], z: V[FVecZ] }; } } catch (e) {} } @@ -3139,7 +3372,7 @@ UpdateCam(camInfo, screenW, screenH); // 每帧只算一次相机基(需在相机位置兜底之后),供顶点变换/投影复用 - var tankList = gm.m10b_1.j11y(); + var tankList = gm[FGameModeTankManager].j11y(); var count = tankList.e1(); var drawnCount = 0, botDrawn = 0; @@ -3166,7 +3399,7 @@ // 本帧该坦克是否在移动(供「残留节点」检测:坦克在动而节点矩阵不动 = 旧位置遗留) // 用位移的指数滑动平均,而不是「单帧位移 > 10 单位」——后者对慢速移动几乎永远为假, // 会让残留检测形同虚设(残留漏掉的根因就在这里) - var tankId = tank.iz1_1; + var tankId = tank[FEntityId]; var prevPos = TankPrevPos.get(tankId); var tankMove = 0; if (prevPos) { @@ -3184,7 +3417,7 @@ if (distance > 50000) continue; var friendly = IsFriendly(tank, playerTank); - var tankName = GetTankName(tank) || ('ID:' + tank.iz1_1); + var tankName = GetTankName(tank) || ('ID:' + tank[FEntityId]); // 根据 HighlightMode 判断是否渲染该坦克 if (!ShouldRender(tankName)) continue; // 颜色按队伍:友方=蓝,敌方=红 @@ -3380,6 +3613,44 @@ // 原理:Hook gl.bindFramebuffer 重定向到自定义 FBO,用后视相机参数调用游戏渲染器,readback 显示 (function() { + // ===== 字段抽象层(语义名 → 匿名字段,总表在 hornet_schema.js)===== + var Schema = window.__hornetSchema__ || null; + function Fld(Key, Fallback) { var N = Schema ? Schema.FieldName(Key) : null; return N || Fallback; } + function Mth(Key, Fallback) { var N = Schema ? Schema.MethodName(Key) : null; return N || Fallback; } + + // ===== 场景 / 渲染器 ===== + var FSceneRef = Fld('Camera.SceneRef', 'jz1_1'); // 场景实体上的相机引用 + var FRenderer = Fld('Render.Renderer', 'gyw_1'); // 渲染器实例 + var FDrawer = Fld('Render.Drawer', 'd75_1'); // Ff 绘制器实例 + var FRootNode = Fld('Scene.RootNode', 'fyx_1'); // 场景根节点 + var FCameraObject = Fld('Camera.Object', 'iyx_1'); // 主相机对象 + var FGl = Fld('Render.Gl', 'w39_1'); // GL 包装对象(o3c/n3c) + var FGlNative = Fld('Render.GlNative', 'b74_1'); // 原生 WebGL 上下文 + + // ===== 相机 ===== + var FAngles = Fld('Camera.Angles', 'u35_1'); // 朝向角(pitch/roll/yaw) + var FPosition = Fld('Camera.Position', 'r35_1'); // 位置向量 + var FFov = Fld('Camera.Fov', 'b37_1'); // 视图矩阵数据 + var FViewportWidth = Fld('Camera.ViewportWidth', 'u36_1'); // 渲染宽 + var FViewportHeight = Fld('Camera.ViewportHeight', 'v36_1');// 渲染高 + var FFovValue = Fld('Camera.FovValue', 'z36_1'); // 视场角 + var FNear = Fld('Camera.Near', 'x36_1'); // 近裁剪面 + var FFar = Fld('Camera.Far', 'y36_1'); // 远裁剪面 + var MCameraUpdate = Mth('Camera.Update', 'o37'); // 重算相机矩阵 + var FX = Fld('Vec.X', 'f20_1'); + var FY = Fld('Vec.Y', 'g20_1'); + var FZ = Fld('Vec.Z', 'h20_1'); + + // ===== 渲染开关 / 队列 ===== + var FSettings = Fld('Render.Settings', 'y38_1'); // 渲染开关集合 + var FShadowEnabled = Fld('Render.ShadowEnabled', 'e3o_1'); // 阴影开关 + var FPostProcessEnabled = Fld('Render.PostProcessEnabled', 'f3o_1'); // 后处理开关 + var FExtraPassEnabled = Fld('Render.ExtraPassEnabled', 'k3o_1'); // 额外 pass 开关 + var FLayerQueue = Fld('Render.LayerQueue', 'e39_1'); // 渲染层队列 + var MDrawScene = Mth('Render.Draw', 'f3t'); // 渲染主场景 + var MGetDrawingWidth = Mth('Render.DrawingWidth', 'o3c'); // drawingBufferWidth + var MGetDrawingHeight = Mth('Render.DrawingHeight', 'n3c'); // drawingBufferHeight + function _start() { console.log('===== 3DTank后视镜(原生渲染版)启动 ====='); @@ -3457,27 +3728,27 @@ var initError = null; try { - scene = gm.lz4().jz1_1; + scene = gm.lz4()[FSceneRef]; console.log('[后视] 场景对象:', scene); // hC 渲染器实例(jr.lye_1) - var renderer = scene.gyw_1; + var renderer = scene[FRenderer]; console.log('[后视] 渲染器:', renderer); // Ff 绘制器实例(hC.r74_1) - ff = renderer.d75_1; + ff = renderer[FDrawer]; console.log('[后视] Ff绘制器:', ff); // 场景根节点(jr.kyf_1) - rootNode = scene.fyx_1; + rootNode = scene[FRootNode]; console.log('[后视] 根节点:', rootNode); // 主相机 Ha 实例(jr.nyf_1) - mainCamera = scene.iyx_1; + mainCamera = scene[FCameraObject]; console.log('[后视] 主相机:', mainCamera); // 游戏 gl context(Ff.p39_1.p73_1 = BS.p73_1) - gl = ff.w39_1.b74_1; + gl = ff[FGl][FGlNative]; console.log('[后视] GL上下文:', gl); if (!ff || !rootNode || !mainCamera || !gl) { @@ -3485,11 +3756,11 @@ } // 探测相机字段 - console.log('[后视] 相机u35_1:', mainCamera.u35_1); - console.log('[后视] 相机r35_1:', mainCamera.r35_1); - console.log('[后视] 相机b37_1:', mainCamera.b37_1); + console.log('[后视] 相机u35_1:', mainCamera[FAngles]); + console.log('[后视] 相机r35_1:', mainCamera[FPosition]); + console.log('[后视] 相机b37_1:', mainCamera[FFov]); - if (!mainCamera.u35_1 || !mainCamera.r35_1) { + if (!mainCamera[FAngles] || !mainCamera[FPosition]) { throw new Error('相机字段缺失'); } @@ -3621,10 +3892,10 @@ // Hook BS.h3c/g3c:后视渲染时返回 FBO 尺寸(修复 screenSize/aspect ratio) // y3s 会调用 BS.h3c()/g3c() 获取 drawingBufferWidth/Height 设置相机 l36_1/m36_1 // 不 Hook 的话 aspect ratio 会用游戏 canvas 尺寸(如1920x1080),导致画面比例错误 - origH3c = ff.w39_1.o3c.bind(ff.w39_1); - origG3c = ff.w39_1.n3c.bind(ff.w39_1); - ff.w39_1.o3c = function() { return rearviewRendering ? rw : origH3c(); }; - ff.w39_1.n3c = function() { return rearviewRendering ? rh : origG3c(); }; + origH3c = ff[FGl][MGetDrawingWidth].bind(ff[FGl]); + origG3c = ff[FGl][MGetDrawingHeight].bind(ff[FGl]); + ff[FGl][MGetDrawingWidth] = function() { return rearviewRendering ? rw : origH3c(); }; + ff[FGl][MGetDrawingHeight] = function() { return rearviewRendering ? rh : origG3c(); }; console.log('[后视] BS.o3c/n3c 已 Hook'); } @@ -3685,17 +3956,17 @@ var camPosZ = playerPos.z + Config.cameraHeight; // ===== 保存主相机参数 ===== - 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.u36_1; - var savedM36 = mainCamera.v36_1; - var savedS36 = mainCamera.z36_1; - var savedQ36 = mainCamera.x36_1; - var savedR36 = mainCamera.y36_1; + var savedPitch = mainCamera[FAngles][FX]; + var savedYaw = mainCamera[FAngles][FY]; + var savedRoll = mainCamera[FAngles][FZ]; + var savedX = mainCamera[FPosition][FX]; + var savedY = mainCamera[FPosition][FY]; + var savedZ = mainCamera[FPosition][FZ]; + var savedL36 = mainCamera[FViewportWidth]; + var savedM36 = mainCamera[FViewportHeight]; + var savedS36 = mainCamera[FFovValue]; + var savedQ36 = mainCamera[FNear]; + var savedR36 = mainCamera[FFar]; // 临时暂停自由视角的 h37 hook,避免覆盖后视镜相机参数 var fl = window.__hornetFreeLook__; @@ -3706,41 +3977,41 @@ // g=π 使水平方向反向(朝后看),h=yaw+π 保持水平视角正确 // f=-pitch 使垂直方向与主视一致(低头看后方地形) // up 朝下 → FBO 画面颠倒 → GL readback Y 翻转修正 - 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.u36_1 = rw; - mainCamera.v36_1 = rh; - mainCamera.z36_1 = Config.fov; - mainCamera.x36_1 = Config.near; - mainCamera.y36_1 = Config.far; + mainCamera[FAngles][FX] = rearPitch; + mainCamera[FAngles][FY] = Math.PI; + mainCamera[FAngles][FZ] = camInfo.yaw + Math.PI; + mainCamera[FPosition][FX] = camPosX; + mainCamera[FPosition][FY] = camPosY; + mainCamera[FPosition][FZ] = camPosZ; + mainCamera[FViewportWidth] = rw; + mainCamera[FViewportHeight] = rh; + mainCamera[FFovValue] = Config.fov; + mainCamera[FNear] = Config.near; + mainCamera[FFar] = Config.far; // 更新相机矩阵(viewMatrix, projectionMatrix, viewProjectionMatrix) - mainCamera.o37(); + mainCamera[MCameraUpdate](); // 保存后视视图矩阵副本(用于 UI 标记投影,finally 会恢复相机所以需提前保存) - var rearViewMatrix = new Float32Array(mainCamera.b37_1); + var rearViewMatrix = new Float32Array(mainCamera[FFov]); // ===== 保存并禁用阴影/后处理(避免它们切换 FBO 覆盖 rearFbo)===== // p38_1.b3o_1 控制 z39_1(阴影),c3o_1 控制 c3a_1/d3a_1(后处理),z3n_1 控制 f3a_1 // 禁用后 y3s 只执行主场景渲染 z3k,跳过所有 FBO 切换的 pass - 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; + var savedX3n = ff[FSettings][FShadowEnabled]; + var savedY3n = ff[FSettings][FPostProcessEnabled]; + var savedZ3n = ff[FSettings][FExtraPassEnabled]; + ff[FSettings][FShadowEnabled] = false; + ff[FSettings][FPostProcessEnabled] = false; + ff[FSettings][FExtraPassEnabled] = false; // ===== 禁用 UI 渲染层(防止技能条等 UI 混入后视 FBO)===== // 保存 UI 层队列,替换为空假对象,y3s 后恢复 var savedUiQueues = {}; for (var ui = 0; ui < uiLayerIndices.length; ui++) { var li = uiLayerIndices[ui]; - savedUiQueues[li] = ff.e39_1[li]; - ff.e39_1[li] = fakeQueue; + savedUiQueues[li] = ff[FLayerQueue][li]; + ff[FLayerQueue][li] = fakeQueue; } // ===== 渲染到 FBO ===== @@ -3753,7 +4024,7 @@ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); // 调用游戏原生渲染器(已禁用阴影/后处理/UI层,只渲染 3D 场景) - ff.f3t(rootNode, mainCamera); + ff[MDrawScene](rootNode, mainCamera); rearviewRendering = false; @@ -3806,9 +4077,9 @@ // ===== 恢复阴影/后处理(异常时也要恢复)===== try { if (typeof savedX3n !== 'undefined') { - ff.y38_1.e3o_1 = savedX3n; - ff.y38_1.f3o_1 = savedY3n; - ff.y38_1.k3o_1 = savedZ3n; + ff[FSettings][FShadowEnabled] = savedX3n; + ff[FSettings][FPostProcessEnabled] = savedY3n; + ff[FSettings][FExtraPassEnabled] = savedZ3n; } } catch(e) { console.error('[后视] 恢复阴影/后处理失败:', e.message); @@ -3819,7 +4090,7 @@ if (savedUiQueues) { for (var ui2 = 0; ui2 < uiLayerIndices.length; ui2++) { var li2 = uiLayerIndices[ui2]; - ff.e39_1[li2] = savedUiQueues[li2]; + ff[FLayerQueue][li2] = savedUiQueues[li2]; } } } catch(e) { @@ -3828,18 +4099,18 @@ // ===== 恢复主相机参数 ===== try { - 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.u36_1 = savedL36; - mainCamera.v36_1 = savedM36; - mainCamera.z36_1 = savedS36; - mainCamera.x36_1 = savedQ36; - mainCamera.y36_1 = savedR36; - mainCamera.o37(); + mainCamera[FAngles][FX] = savedPitch; + mainCamera[FAngles][FY] = savedYaw; + mainCamera[FAngles][FZ] = savedRoll; + mainCamera[FPosition][FX] = savedX; + mainCamera[FPosition][FY] = savedY; + mainCamera[FPosition][FZ] = savedZ; + mainCamera[FViewportWidth] = savedL36; + mainCamera[FViewportHeight] = savedM36; + mainCamera[FFovValue] = savedS36; + mainCamera[FNear] = savedQ36; + mainCamera[FFar] = savedR36; + mainCamera[MCameraUpdate](); } catch(e) { console.error('[后视] 恢复相机失败:', e.message); } @@ -3967,9 +4238,9 @@ } // 恢复 BS.h3c/g3c - if (origH3c && ff && ff.w39_1) { - ff.w39_1.o3c = origH3c; - ff.w39_1.n3c = origG3c; + if (origH3c && ff && ff[FGl]) { + ff[FGl][MGetDrawingWidth] = origH3c; + ff[FGl][MGetDrawingHeight] = origG3c; console.log('[后视] BS.o3c/n3c 已恢复'); } @@ -4063,6 +4334,28 @@ // 独立轻量级脚本,不依赖3D透视渲染 (function() { + // ===== 字段抽象层(语义名 → 匿名字段,总表在 hornet_schema.js)===== + var Schema = window.__hornetSchema__ || null; + function Fld(Key, Fallback) { var N = Schema ? Schema.FieldName(Key) : null; return N || Fallback; } + function Mth(Key, Fallback) { var N = Schema ? Schema.MethodName(Key) : null; return N || Fallback; } + + // ===== 匿名字段常量 ===== + var FEntityId = Fld('Entity.Id', 'iz1_1'); + var FEntityRoot = Fld('Entity.Root', 'qz1_1'); + var FEntityComponentMap = Fld('Entity.ComponentMap', 's2a_1'); + var MGameModeScene = Mth('GameMode.Scene', 'lz4'); + var MCollisionRaycast = Mth('CollisionWorld.Raycast', 'a17p'); + var FWeaponComponent = Fld('Weapon.Component', 'n11c_1'); + var FWeaponComponentAlt = Fld('Weapon.ComponentAlt', 'q112_1'); + var FWeaponTurretRoot = Fld('Weapon.TurretRoot', 'm17a_1'); + var FNodeMatrix = Fld('Node.MatrixOld', 'n35_1'); + var FRayHitPoint = Fld('Ray.HitPoint', 'y178_1'); + var FRayHitNormal = Fld('Ray.HitNormal', 'z178_1'); + var FRayDistance = Fld('Ray.Distance', 'a179_1'); + var FRayHitShape = Fld('Ray.HitShape', 'q17o_1'); + var FRayHitBody = Fld('Ray.HitBody', 'b179_1'); + var FRayFilterQuery = Fld('RayFilter.Query', 'l1da_1'); + function _start() { console.log('===== 3DTank瞄准警告启动 ====='); @@ -4129,9 +4422,9 @@ // ===== 获取 TDt 武器皮肤组件(炮塔) ===== function GetTDtComponent(entity) { - var comp = FindComponentByField(entity, 'n11c_1'); + var comp = FindComponentByField(entity, FWeaponComponent); if (comp) return comp; - comp = FindComponentByField(entity, 'q112_1'); + comp = FindComponentByField(entity, FWeaponComponentAlt); return comp; } @@ -4144,9 +4437,9 @@ // 返回 { angle: 炮塔朝向与"敌方→玩家"方向的夹角, distance: 距离, turretAngle: 炮塔朝向角度, turretDir: {x,y} } function GetTurretAimAngle(tank, enemyPos, playerPos) { var tdt = GetTDtComponent(tank); - if (!tdt || !tdt.n11c_1 || !tdt.n11c_1.m17a_1) return null; - var bpNode = tdt.n11c_1.m17a_1; - var m = bpNode.n35_1; + if (!tdt || !tdt[FWeaponComponent] || !tdt[FWeaponComponent][FWeaponTurretRoot]) return null; + var bpNode = tdt[FWeaponComponent][FWeaponTurretRoot]; + var m = bpNode[FNodeMatrix]; if (!m) return null; // 敌方→玩家方向(x,y 水平面) @@ -4186,13 +4479,13 @@ function GetUMtComponent() { if (UMtComponent) return UMtComponent; try { - var gmEntity = gm.lz4(); - if (!gmEntity || !gmEntity.qz1_1 || !gmEntity.qz1_1.s2a_1) return null; - gmEntity.qz1_1.s2a_1.forEach(function(valueList, key) { + var gmEntity = gm[MGameModeScene](); + if (!gmEntity || !gmEntity[FEntityRoot] || !gmEntity[FEntityRoot][FEntityComponentMap]) return null; + gmEntity[FEntityRoot][FEntityComponentMap].forEach(function(valueList, key) { if (UMtComponent) return; try { var comp = (valueList && typeof valueList.g1 === 'function') ? valueList.g1(0) : valueList; - if (comp && typeof comp.a17p === 'function') UMtComponent = comp; + if (comp && typeof comp[MCollisionRaycast] === 'function') UMtComponent = comp; } catch(e) {} }); if (UMtComponent) console.log('[瞄准] uMt 碰撞检测器已获取'); @@ -4234,30 +4527,30 @@ var maxDist = dist - 300; var result = { - y178_1: MkVec(0,0,0), - z178_1: MkVec(0,0,0), - a179_1: maxDist, - q17o_1: null, - b179_1: null, + [FRayHitPoint]: MkVec(0,0,0), + [FRayHitNormal]: MkVec(0,0,0), + [FRayDistance]: maxDist, + [FRayHitShape]: null, + [FRayHitBody]: null, r17o: function(t, n, i, r, e) { - this.y178_1.k26(t); - this.z178_1.k26(n); - this.a179_1 = i; - this.q17o_1 = r; - this.b179_1 = e; + this[FRayHitPoint].k26(t); + this[FRayHitNormal].k26(n); + this[FRayDistance] = i; + this[FRayHitShape] = r; + this[FRayHitBody] = e; }, - d3: function() { this.b179_1 = null; } + d3: function() { this[FRayHitBody] = null; } }; var filter = { - l1da_1: { h1: function() { return false; }, d3: function() {} }, + [FRayFilterQuery]: { h1: function() { return false; }, d3: function() {} }, w179: function() { return true; }, - d3: function() { this.l1da_1.d3(); }, + d3: function() { this[FRayFilterQuery].d3(); }, m1da: function() {} }; - var hit = uMt.a17p(startVec, dirVec, maxDist, result, filter); - blocked = !!hit && result.a179_1 < (maxDist - 50); + var hit = uMt[MCollisionRaycast](startVec, dirVec, maxDist, result, filter); + blocked = !!hit && result[FRayDistance] < (maxDist - 50); RaycastFailCount = 0; } catch(e) { RaycastFailCount++; @@ -4369,19 +4662,19 @@ if (aimInfo.angle >= aimThreshold) continue; // 墙体阻隔判断 - var blocked = IsLineOfSightBlocked(pos, playerPos, tank.iz1_1, frameCount); + var blocked = IsLineOfSightBlocked(pos, playerPos, tank[FEntityId], frameCount); if (blocked) continue; // 记录威胁 var proj = Project3D(pos, camInfo.pos, camInfo.yaw, camInfo.pitch, camInfo.fov, screenW, screenH); - var tankName = GetTankName(tank) || ('ID:' + tank.iz1_1); + var tankName = GetTankName(tank) || ('ID:' + tank[FEntityId]); threats.push({ pos: pos, distance: aimInfo.distance, angle: aimInfo.angle, turretDir: aimInfo.turretDir, screenProj: proj, - tankId: tank.iz1_1, + tankId: tank[FEntityId], tankName: tankName }); } @@ -4711,6 +5004,23 @@ // 原理:Hook mainCamera.h37,在相机矩阵更新前覆盖l35_1/k35_1参数 (function() { + // ===== 字段抽象层(语义名 → 匿名字段,总表在 hornet_schema.js)===== + var Schema = window.__hornetSchema__ || null; + function Fld(Key, Fallback) { var N = Schema ? Schema.FieldName(Key) : null; return N || Fallback; } + function Mth(Key, Fallback) { var N = Schema ? Schema.MethodName(Key) : null; return N || Fallback; } + + // ===== 匿名字段常量 ===== + var FVecX = Fld('Vec.X', 'f20_1'); + var FVecY = Fld('Vec.Y', 'g20_1'); + var FVecZ = Fld('Vec.Z', 'h20_1'); + var FCameraSceneRef = Fld('Camera.SceneRef', 'jz1_1'); + var FCameraObject = Fld('Camera.Object', 'iyx_1'); + var FCameraEuler = Fld('Camera.Euler', 'u35_1'); + var FCameraPosition = Fld('Node.Children', 'r35_1'); + var FViewMatrix = Fld('Camera.Fov', 'b37_1'); + var FCameraSceneFx = Fld('Camera.SceneFx', 'd75_1'); + var MGameModeScene = Mth('GameMode.Scene', 'lz4'); + function _start() { console.log('===== 3DTank自由视角启动 ====='); @@ -4740,15 +5050,15 @@ var initError = null; try { - scene = gm.lz4().jz1_1; - mainCamera = scene.iyx_1; - ff = scene.gyw_1.d75_1; - if (!mainCamera || !mainCamera.u35_1 || !mainCamera.r35_1 || !mainCamera.o37) { + scene = gm[MGameModeScene]()[FCameraSceneRef]; + mainCamera = scene[FCameraObject]; + ff = scene.gyw_1[FCameraSceneFx]; + if (!mainCamera || !mainCamera[FCameraEuler] || !mainCamera[FCameraPosition] || !mainCamera.o37) { throw new Error('相机字段缺失'); } console.log('[自由视角] 主相机:', mainCamera.constructor.name, - '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) + ')'); + 'l35=(' + mainCamera[FCameraEuler][FVecX].toFixed(2) + ',' + mainCamera[FCameraEuler][FVecY].toFixed(2) + ',' + mainCamera[FCameraEuler][FVecZ].toFixed(2) + ')', + 'k35=(' + mainCamera[FCameraPosition][FVecX].toFixed(0) + ',' + mainCamera[FCameraPosition][FVecY].toFixed(0) + ',' + mainCamera[FCameraPosition][FVecZ].toFixed(0) + ')'); } catch(e) { initError = e; console.error('[自由视角] 初始化失败:', e.message); @@ -4774,12 +5084,12 @@ var origH37 = mainCamera.o37; function applyFreeLookParams() { - 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; + mainCamera[FCameraEuler][FVecX] = freePitch; // pitch (f) + mainCamera[FCameraEuler][FVecY] = freeRoll; // roll (g) 保持初始值 + mainCamera[FCameraEuler][FVecZ] = freeYaw; // yaw (h) 鼠标左右控制 + mainCamera[FCameraPosition][FVecX] = freePosX; + mainCamera[FCameraPosition][FVecY] = freePosY; + mainCamera[FCameraPosition][FVecZ] = freePosZ; } // 缓存从 viewMatrix 提取的前方向/右方向(h37 更新后设置) @@ -4796,11 +5106,11 @@ applyFreeLookParams(); origH37.call(this); // 从更新后的 viewMatrix (u36_1) 提取前方向和右方向(水平分量 x,y) - if (this.b37_1) { - cachedFwdX = -this.b37_1[2]; // forward.x - cachedFwdY = -this.b37_1[6]; // forward.y - cachedRightX = this.b37_1[0]; // right.x - cachedRightY = this.b37_1[4]; // right.y + if (this[FViewMatrix]) { + cachedFwdX = -this[FViewMatrix][2]; // forward.x + cachedFwdY = -this[FViewMatrix][6]; // forward.y + cachedRightX = this[FViewMatrix][0]; // right.x + cachedRightY = this[FViewMatrix][4]; // right.y cachedInitialized = true; } } else { @@ -4813,12 +5123,12 @@ function enableFreeLook() { // 从当前相机参数初始化(平滑过渡) // l35_1: f=pitch, g=roll, h=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; + freePitch = mainCamera[FCameraEuler][FVecX]; + freeRoll = mainCamera[FCameraEuler][FVecY]; // roll 保持初始值 + freeYaw = mainCamera[FCameraEuler][FVecZ]; // yaw 鼠标控制 + freePosX = mainCamera[FCameraPosition][FVecX]; + freePosY = mainCamera[FCameraPosition][FVecY]; + freePosZ = mainCamera[FCameraPosition][FVecZ]; freeLookActive = true; console.log('[自由视角] 已开启 — WASD移动 | 鼠标旋转 | 空格上升/Shift下降 | 滚轮调速 | Alt+F关闭'); updateHud(); @@ -5067,6 +5377,50 @@ // 操作:拖拽标题移动窗口 | 拖拽地图平移 | 滚轮缩放 | 双击居中 | Alt+M 开关 (function() { + // ===== 字段抽象层(语义名 → 匿名字段,总表在 hornet_schema.js)===== + var Schema = window.__hornetSchema__ || null; + function Fld(Key, Fallback) { var N = Schema ? Schema.FieldName(Key) : null; return N || Fallback; } + function Mth(Key, Fallback) { var N = Schema ? Schema.MethodName(Key) : null; return N || Fallback; } + + // 向量 + var FVecX = Fld('Vec.X', 'f20_1'); + var FVecY = Fld('Vec.Y', 'g20_1'); + var FVecZ = Fld('Vec.Z', 'h20_1'); + + // 战局 / 场景 + var MScene = Mth('GameMode.Scene', 'lz4'); + var FCameraSceneRef = Fld('Camera.SceneRef', 'jz1_1'); + var FTankManager = Fld('GameMode.TankManager', 'm10b_1'); + var MTankList = Mth('GameMode.TankList', 'j11y'); + + // 渲染器 + var FRenderer = Fld('Renderer.Object', 'd75_1'); + var FRenderDevice = Fld('Renderer.Device', 'w39_1'); + var FRenderGl = Fld('Renderer.Gl', 'b74_1'); + var FRenderFlags = Fld('Renderer.Flags', 'y38_1'); + var FRenderShadow = Fld('Renderer.Shadow', 'e3o_1'); + var FRenderPostFx = Fld('Renderer.PostFx', 'f3o_1'); + var FRenderFlagAux = Fld('Renderer.FlagAux', 'k3o_1'); + var FRenderQueueList = Fld('Renderer.QueueList', 'e39_1'); + + // 相机 + var FCameraRotation = Fld('Camera.Rotation', 'u35_1'); + var FNodeChildren = Fld('Node.Children', 'r35_1'); // 本文件里该字段是相机位置向量(与节点子列表同名复用) + var FCameraRenderWidth = Fld('Camera.RenderWidth', 'u36_1'); + var FCameraRenderHeight = Fld('Camera.RenderHeight', 'v36_1'); + var FCameraFov = Fld('Camera.Fov', 'z36_1'); + var FCameraNear = Fld('Camera.Near', 'x36_1'); + var FCameraFar = Fld('Camera.Far', 'y36_1'); + + // 皮肤 / 节点 + var FSkinSlotHull = Fld('Skin.SlotHull', 'w14w_1'); + var FSkinSlotHullLegacy = Fld('Skin.SlotHullLegacy', 't14l_1'); + var FSkinSlotHullAlt = Fld('Skin.SlotHullAlt', 'w14x_1'); + var FSkinSlotHullOld = Fld('Skin.SlotHullOld', 'p14l_1'); + var FSkinParts = Fld('Skin.Parts', 'g17r_1'); + var FSkinPartNode = Fld('Skin.PartNode', 'j17r_1'); + var FNodeMatrixOld = Fld('Node.MatrixOld', 'n35_1'); + function _start() { console.log('===== 3DTank 战争沙盘(3D俯瞰版)启动 ====='); @@ -5099,27 +5453,27 @@ // 从车体皮肤组件(KMt)的Bp节点世界矩阵提取前向方向 // dzc_1 是局部空间向量且可能未设置,不可靠 // 皮肤字段:新 build 是 w14w_1;旧 build 是 t14l_1(新版 t14l_1 已改成悬停车体状态 Qmt) - var hullComp = FindComponentByField(tank, 'w14w_1') || - FindComponentByField(tank, 't14l_1') || - FindComponentByField(tank, 'w14x_1') || - FindComponentByField(tank, 'p14l_1'); + var hullComp = FindComponentByField(tank, FSkinSlotHull) || + FindComponentByField(tank, FSkinSlotHullLegacy) || + FindComponentByField(tank, FSkinSlotHullAlt) || + FindComponentByField(tank, FSkinSlotHullOld); if (!hullComp) return 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; + var skin = (hullComp[FSkinSlotHull] && hullComp[FSkinSlotHull][FSkinParts]) ? hullComp[FSkinSlotHull] : + (hullComp[FSkinSlotHullLegacy] && hullComp[FSkinSlotHullLegacy][FSkinParts]) ? hullComp[FSkinSlotHullLegacy] : + (hullComp[FSkinSlotHullOld] && hullComp[FSkinSlotHullOld][FSkinParts]) ? hullComp[FSkinSlotHullOld] : null; if (!skin) return null; try { - var partCount = skin.g17r_1.e1(); + var partCount = skin[FSkinParts].e1(); for (var p = 0; p < partCount; p++) { - var part = skin.g17r_1.g1(p); - if (!part || !part.j17r_1) continue; - var nodeCount = part.j17r_1.e1(); + var part = skin[FSkinParts].g1(p); + if (!part || !part[FSkinPartNode]) continue; + var nodeCount = part[FSkinPartNode].e1(); for (var n = 0; n < nodeCount; n++) { - var bpNode = part.j17r_1.g1(n); - if (!bpNode || !bpNode.n35_1) continue; + var bpNode = part[FSkinPartNode].g1(n); + if (!bpNode || !bpNode[FNodeMatrixOld]) continue; // 不按 g35_1 过滤:它是引擎的细节/淡出权重(0~1),不是幽灵标志, // 过滤会漏掉低细节节点、导致取不到朝向 - var m = bpNode.n35_1; + var m = bpNode[FNodeMatrixOld]; // 列主序世界矩阵:Col0=(m[0],m[1],m[2]) Col1=(m[4],m[5],m[6]) Col2=(m[8],m[9],m[10]) // 游戏坐标:x,y 水平面; z(h20_1)=高度。找"上"列(|z|最大),其余为水平方向 var az0 = Math.abs(m[2]), az1 = Math.abs(m[6]), az2 = Math.abs(m[10]); @@ -5149,11 +5503,11 @@ var scene = null, ff = null, rootNode = null, mainCamera = null, gl = null; var initError = null; try { - scene = gm.lz4().jz1_1; - ff = scene.gyw_1.d75_1; + scene = gm[MScene]()[FCameraSceneRef]; + ff = scene.gyw_1[FRenderer]; rootNode = scene.fyx_1; mainCamera = scene.iyx_1; - gl = ff.w39_1.b74_1; + gl = ff[FRenderDevice][FRenderGl]; if (!ff || !rootNode || !mainCamera || !gl) throw new Error('关键字段缺失'); } catch(e) { initError = e; @@ -5340,10 +5694,10 @@ } return origBindFramebuffer(target, fbo); }; - var origH3c = ff.w39_1.o3c.bind(ff.w39_1); - var origG3c = ff.w39_1.n3c.bind(ff.w39_1); - ff.w39_1.o3c = function() { return sandboxRendering ? FBO_W : origH3c(); }; - ff.w39_1.n3c = function() { return sandboxRendering ? FBO_H : origG3c(); }; + var origH3c = ff[FRenderDevice].o3c.bind(ff[FRenderDevice]); + var origG3c = ff[FRenderDevice].n3c.bind(ff[FRenderDevice]); + ff[FRenderDevice].o3c = function() { return sandboxRendering ? FBO_W : origH3c(); }; + ff[FRenderDevice].n3c = function() { return sandboxRendering ? FBO_H : origG3c(); }; // UI 层禁用 var uiLayerIndices = [20, 21, 22, 23, 24]; @@ -5543,7 +5897,7 @@ // 坦克标记 var tanks = []; try { - var list = gm.m10b_1.j11y(); + var list = gm[FTankManager][MTankList](); var n = list.e1(); for (var i = 0; i < n; i++) { var t = list.g1(i); if (t) tanks.push(t); } } catch(e) {} @@ -5723,44 +6077,44 @@ try { // 保存主相机参数 - 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.u36_1, sM36 = mainCamera.v36_1; - var sS36 = mainCamera.z36_1, sQ36 = mainCamera.x36_1, sR36 = mainCamera.y36_1; + var sPitch = mainCamera[FCameraRotation][FVecX]; + var sRoll = mainCamera[FCameraRotation][FVecY]; + var sYaw = mainCamera[FCameraRotation][FVecZ]; + var sX = mainCamera[FNodeChildren][FVecX]; + var sY = mainCamera[FNodeChildren][FVecY]; + var sZ = mainCamera[FNodeChildren][FVecZ]; + var sL36 = mainCamera[FCameraRenderWidth], sM36 = mainCamera[FCameraRenderHeight]; + var sS36 = mainCamera[FCameraFov], sQ36 = mainCamera[FCameraNear], sR36 = mainCamera[FCameraFar]; // 设置俯瞰相机参数 // pitch=-π/2 朝下看,roll=0 不翻转,yaw=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.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[FCameraRotation][FVecX] = -Math.PI / 2; + mainCamera[FCameraRotation][FVecY] = 0; + mainCamera[FCameraRotation][FVecZ] = 0; + mainCamera[FNodeChildren][FVecX] = viewCenterX; + mainCamera[FNodeChildren][FVecY] = viewCenterY; + mainCamera[FNodeChildren][FVecZ] = camHeight; + mainCamera[FCameraRenderWidth] = FBO_W; + mainCamera[FCameraRenderHeight] = FBO_H; + mainCamera[FCameraFov] = Config.fov; + mainCamera[FCameraNear] = Config.near; + mainCamera[FCameraFar] = Config.far; mainCamera.o37(); // 禁用阴影/后处理 - 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; + var savedX3n = ff[FRenderFlags][FRenderShadow]; + var savedY3n = ff[FRenderFlags][FRenderPostFx]; + var savedZ3n = ff[FRenderFlags][FRenderFlagAux]; + ff[FRenderFlags][FRenderShadow] = false; + ff[FRenderFlags][FRenderPostFx] = false; + ff[FRenderFlags][FRenderFlagAux] = false; // 禁用 UI 层 var savedUiQueues = {}; for (var ui = 0; ui < uiLayerIndices.length; ui++) { var li = uiLayerIndices[ui]; - savedUiQueues[li] = ff.e39_1[li]; - ff.e39_1[li] = fakeQueue; + savedUiQueues[li] = ff[FRenderQueueList][li]; + ff[FRenderQueueList][li] = fakeQueue; } // 渲染到 FBO @@ -5806,22 +6160,22 @@ origBindFramebuffer(gl.FRAMEBUFFER, mainFbo); // 恢复渲染状态 - ff.y38_1.e3o_1 = savedX3n; - ff.y38_1.f3o_1 = savedY3n; - ff.y38_1.k3o_1 = savedZ3n; + ff[FRenderFlags][FRenderShadow] = savedX3n; + ff[FRenderFlags][FRenderPostFx] = savedY3n; + ff[FRenderFlags][FRenderFlagAux] = savedZ3n; for (var ui2 = 0; ui2 < uiLayerIndices.length; ui2++) { - ff.e39_1[uiLayerIndices[ui2]] = savedUiQueues[uiLayerIndices[ui2]]; + ff[FRenderQueueList][uiLayerIndices[ui2]] = savedUiQueues[uiLayerIndices[ui2]]; } // 恢复主相机 - 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.u36_1 = sL36; mainCamera.v36_1 = sM36; - mainCamera.z36_1 = sS36; mainCamera.x36_1 = sQ36; mainCamera.y36_1 = sR36; + mainCamera[FCameraRotation][FVecX] = sPitch; + mainCamera[FCameraRotation][FVecY] = sRoll; + mainCamera[FCameraRotation][FVecZ] = sYaw; + mainCamera[FNodeChildren][FVecX] = sX; + mainCamera[FNodeChildren][FVecY] = sY; + mainCamera[FNodeChildren][FVecZ] = sZ; + mainCamera[FCameraRenderWidth] = sL36; mainCamera[FCameraRenderHeight] = sM36; + mainCamera[FCameraFov] = sS36; mainCamera[FCameraNear] = sQ36; mainCamera[FCameraFar] = sR36; mainCamera.o37(); // 绘制 2D 叠加层(坦克标记/网格/罗盘) @@ -5857,8 +6211,8 @@ if (rafId) cancelAnimationFrame(rafId); // 恢复 hook gl.bindFramebuffer = origBindFramebuffer; - ff.w39_1.o3c = origH3c; - ff.w39_1.n3c = origG3c; + ff[FRenderDevice].o3c = origH3c; + ff[FRenderDevice].n3c = origG3c; window.removeEventListener('resize', resizeCanvas); if (container.parentNode) container.parentNode.removeChild(container); window.__hornetWarSandbox__ = null; diff --git a/tools/hook_gamemode.js b/tools/hook_gamemode.js index cb0c93f..2de8780 100644 --- a/tools/hook_gamemode.js +++ b/tools/hook_gamemode.js @@ -13,6 +13,15 @@ // 坦克列表 = dct(scene).m10b_1.j11y() (function() { + // ===== 字段抽象层(语义名 → 匿名字段,总表在 hornet_schema.js)===== + var Schema = window.__hornetSchema__ || null; + function Fld(Key, Fallback) { var N = Schema ? Schema.FieldName(Key) : null; return N || Fallback; } + function Mth(Key, Fallback) { var N = Schema ? Schema.MethodName(Key) : null; return N || Fallback; } + + // ===== 语义字段常量(取不到抽象层时用 fallback 字面名) ===== + var FGameModeTankManager = Fld('GameMode.TankManager', 'm10b_1'); // 坦克列表管理器 + var MTankList = Mth('GameMode.TankList', 'j11y'); // 坦克列表 + console.log('===== GameMode Hook v2 开始 ====='); // 1. 检查 webpack 内部对象 @@ -47,7 +56,7 @@ // 尝试获取坦克列表(失败也不影响 gameMode 设置) var tankCount = -1; try { - var list = gm.m10b_1.j11y(); + var list = gm[FGameModeTankManager][MTankList](); tankCount = list.e1(); window.__tankList__ = list; } catch(e) { @@ -103,7 +112,7 @@ if (window.__gameMode__) { // 验证当前引用是否有效 try { - var list = window.__gameMode__.m10b_1.j11y(); + var list = window.__gameMode__[FGameModeTankManager][MTankList](); window.__tankList__ = list; } catch(e) { // 引用失效(新战斗/退出战斗),重置等待重新捕获 @@ -142,7 +151,7 @@ console.log('ght 实例未捕获,请等待'); return; } - var list = window.__gameMode__.m10b_1.j11y(); + var list = window.__gameMode__[FGameModeTankManager][MTankList](); var count = list.e1(); console.log('=== 坦克列表 (' + count + " 个) ==="); for (var i = 0; i < count; i++) { @@ -157,7 +166,7 @@ console.log('ght 实例未捕获'); return; } - var list = window.__gameMode__.m10b_1.j11y(); + var list = window.__gameMode__[FGameModeTankManager][MTankList](); var tank = list.g1(idx || 0); console.log('坦克[' + (idx || 0) + ']:', tank); console.log('字段:', Object.keys(tank)); @@ -181,7 +190,7 @@ window.__refreshGameMode__ = function() { if (window.__gameMode__) { try { - var list = window.__gameMode__.m10b_1.j11y(); + var list = window.__gameMode__[FGameModeTankManager][MTankList](); console.log('%c[刷新] ght 引用有效,坦克数:' + list.e1(), 'color:#0f0;font-weight:bold'); return window.__gameMode__; } catch(e) { diff --git a/tools/hornet_3d_wireframe.js b/tools/hornet_3d_wireframe.js index e09a472..3c5b845 100644 --- a/tools/hornet_3d_wireframe.js +++ b/tools/hornet_3d_wireframe.js @@ -38,6 +38,39 @@ var FSkinHullRoot = Fld('Skin.HullRoot', 'h159_1'); // 车身渲染树根 var FSkinParts = Fld('Skin.Parts', 'g17r_1'); // 部件表 var FSkinPartNode = Fld('Skin.PartNode', 'j17r_1'); // 部件 → BP 节点 + var FNodeMatrixOld = Fld('Node.MatrixOld', 'n35_1'); // 旧版世界矩阵(兜底) + + // 实体 / 向量 / 战斗实例 + var FEntityId = Fld('Entity.Id', 'iz1_1'); // 实体 ID + var FEntityRoot = Fld('Entity.Root', 'qz1_1'); // 组件宿主根对象 + var FEntityComponentMap = Fld('Entity.ComponentMap', 's2a_1'); // 宿主上的组件 Map + var FVecX = Fld('Vec.X', 'f20_1'); // 向量 X + var FVecY = Fld('Vec.Y', 'g20_1'); // 向量 Y + var FVecZ = Fld('Vec.Z', 'h20_1'); // 向量 Z + var FGameModeTankManager = Fld('GameMode.TankManager', 'm10b_1'); // 坦克管理器 + var FTeamId = Fld('Team.Id', 'k3_1'); // 队伍编号 + + // 炮塔皮肤(LUt / CUt 包装) + var FTurretWrapper = Fld('Turret.Wrapper', 'a11p_1'); // 炮塔组件包装对象 + var FTurretInnerWrapper = Fld('Turret.InnerWrapper', 'e11p_1'); // 包装内的 CUt 包装 + var FTurretRootNode = Fld('Turret.RootNode', 'a17q_1'); // 炮塔 BP 根节点 + + // mesh 资源(Tp / J$ / Y$) + var FMeshVertexGroups = Fld('Mesh.VertexGroups', 'x4w_1'); // 顶点组列表 + var FMeshVertexBuffer = Fld('Mesh.VertexBuffer', 'h3x_1'); // 顶点缓冲资源 + var FMeshGpuBuffer = Fld('Mesh.GpuBuffer', 'v3x_1'); // GPU 缓冲包装 + var FMeshVertexCount = Fld('Mesh.VertexCount', 'w3x_1'); // 顶点数 + var FMeshCpuVertexData = Fld('Mesh.CpuVertexData', 'x3x_1'); // CPU 端顶点原始数据 + var FMeshIndexGroups = Fld('Mesh.IndexGroups', 'y4w_1'); // 索引组列表 + var FMeshIndexBuffer = Fld('Mesh.IndexBuffer', 'q4w_1'); // 索引缓冲资源 + var FMeshIndexBufferOld = Fld('Mesh.IndexBufferOld', 'u3p_1'); // 旧版索引缓冲字段 + var FMeshCpuIndexData = Fld('Mesh.CpuIndexData', 'p3x_1'); // CPU 端索引原始数据 + var FMeshIndexGpuBuffer = Fld('Mesh.IndexGpuBuffer', 'n3x_1'); // 索引 GPU 缓冲包装 + + // 渲染设备 / 原生缓冲 + var FRenderDevice = Fld('Render.Device', 's3h_1'); // GL 包装器 + var FRenderDeviceNative = Fld('Render.DeviceNative', 'f75_1'); // 原生 WebGL2RenderingContext + var FRenderDrawBuffer = Fld('Render.DrawBuffer', 'd75_1'); // 原生 WebGLBuffer // ===== 当前模式 ===== var Mode = 'outline'; // 'wireframe' | 'outline' @@ -250,11 +283,11 @@ var Cached = SkinCompCache.get(entity); if (Cached) return Cached; var comp = FindComponentByField(entity, FSkinHullSkin); - if (!comp) comp = FindComponentByField(entity, 'a17q_1'); + if (!comp) comp = FindComponentByField(entity, FTurretRootNode); // 兜底:按结构找 —— 组件字段值是"皮肤对象"(含 g17r_1 部件表) if (!comp) { try { - entity.qz1_1.s2a_1.forEach(function (VL) { + entity[FEntityRoot][FEntityComponentMap].forEach(function (VL) { if (comp) return; var C = (VL && typeof VL.g1 === 'function') ? VL.g1(0) : VL; if (!C) return; @@ -281,13 +314,13 @@ // 炮塔 BP 根节点 // 新版:weaponSkin 组件(类 LUt,构造首字段 a11p_1)里 e11p_1 = CUt 包装,其 a17q_1 = 炮塔节点 function GetTurretNode(entity) { - var wp = FindComponentByField(entity, 'a11p_1'); + var wp = FindComponentByField(entity, FTurretWrapper); if (wp) { - if (wp.e11p_1 && wp.e11p_1.a17q_1) return wp.e11p_1.a17q_1; + if (wp[FTurretInnerWrapper] && wp[FTurretInnerWrapper][FTurretRootNode]) return wp[FTurretInnerWrapper][FTurretRootNode]; if (typeof wp.u11p === 'function') { try { return wp.u11p(); } catch (e) {} } } var comp = GetSkinComponent(entity); - if (comp && comp.a17q_1) return comp.a17q_1; + if (comp && comp[FTurretRootNode]) return comp[FTurretRootNode]; return null; } @@ -314,8 +347,8 @@ // ⚠ 旧字段 d3x_1 / s3h_1.b74_1 / v3x_1.z73_1 在新版分别是"渲染参数 / 雾色 uniform / uniform 槽位",全错 function GetGlFromResource(Res) { if (!Res) return null; - var Dev = Res.s3h_1; - return (Dev && Dev.f75_1) ? Dev.f75_1 : null; + var Dev = Res[FRenderDevice]; + return (Dev && Dev[FRenderDeviceNative]) ? Dev[FRenderDeviceNative] : null; } // 把引擎的 CPU 端缓冲区包装转成 TypedArray @@ -348,17 +381,17 @@ } function ReadVertexPositionsFromTp(tp) { - if (!tp || !tp.x4w_1 || typeof tp.x4w_1.e1 !== 'function' || tp.x4w_1.e1() === 0) return null; - var group = tp.x4w_1.g1(0); + if (!tp || !tp[FMeshVertexGroups] || typeof tp[FMeshVertexGroups].e1 !== 'function' || tp[FMeshVertexGroups].e1() === 0) return null; + var group = tp[FMeshVertexGroups].g1(0); if (!group) return null; - var Buf = group.h3x_1; // J$ + var Buf = group[FMeshVertexBuffer]; // J$ if (!Buf) return null; // ① 优先 CPU 端原始数据:未上传 GPU 的 mesh 也能读到(v3x_1.d75_1 此时为 null) - var Cpu = ToTypedArray(Buf.x3x_1, 'f32'); + var Cpu = ToTypedArray(Buf[FMeshCpuVertexData], 'f32'); if (Cpu && Cpu.length) return Cpu; // ② GPU 回读(引擎已上传并释放 CPU 数据时) var gl = GetGlFromResource(Buf); - var webglBuf = Buf.v3x_1 && Buf.v3x_1.d75_1; + var webglBuf = Buf[FMeshGpuBuffer] && Buf[FMeshGpuBuffer][FRenderDrawBuffer]; if (!gl || !webglBuf) return null; gl.bindBuffer(gl.ARRAY_BUFFER, webglBuf); var bufSize = gl.getBufferParameter(gl.ARRAY_BUFFER, gl.BUFFER_SIZE); @@ -371,21 +404,21 @@ // ===== 从GPU读取索引数据 ===== // y4w_1 = 索引列表,每项 { q4w_1: 索引缓冲资源 Y$ }(旧版是 il.u3p_1) function ReadIndicesFromTp(tp) { - if (!tp.y4w_1 || typeof tp.y4w_1.e1 !== 'function') return null; - var meshCount = tp.y4w_1.e1(); + if (!tp[FMeshIndexGroups] || typeof tp[FMeshIndexGroups].e1 !== 'function') return null; + var meshCount = tp[FMeshIndexGroups].e1(); if (meshCount === 0) return null; var allIndices = []; for (var m = 0; m < meshCount; m++) { - var il = tp.y4w_1.g1(m); + var il = tp[FMeshIndexGroups].g1(m); if (!il) continue; - var IdxBuf = il.q4w_1 || il.u3p_1; + var IdxBuf = il[FMeshIndexBuffer] || il[FMeshIndexBufferOld]; if (!IdxBuf) continue; // ① CPU 端索引数据(Y$.p3x_1),未上传时也能用 - var Cpu = ToTypedArray(IdxBuf.p3x_1, 'u16'); + var Cpu = ToTypedArray(IdxBuf[FMeshCpuIndexData], 'u16'); if (Cpu && Cpu.length) { allIndices.push(Cpu); continue; } // ② GPU 回读 var gl = GetGlFromResource(IdxBuf); - var webglBuf = IdxBuf.n3x_1 && IdxBuf.n3x_1.d75_1; + var webglBuf = IdxBuf[FMeshIndexGpuBuffer] && IdxBuf[FMeshIndexGpuBuffer][FRenderDrawBuffer]; if (!gl || !webglBuf) continue; gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, webglBuf); var bufSize = gl.getBufferParameter(gl.ELEMENT_ARRAY_BUFFER, gl.BUFFER_SIZE); @@ -421,8 +454,8 @@ var stride = 0; // 优先用引擎记录的顶点数反推 stride(J$.w3x_1 = 顶点数) try { - var G0 = tp.x4w_1.g1(0); - var Vc = G0 && G0.h3x_1 && G0.h3x_1.w3x_1; + var G0 = tp[FMeshVertexGroups].g1(0); + var Vc = G0 && G0[FMeshVertexBuffer] && G0[FMeshVertexBuffer][FMeshVertexCount]; if (Vc > 0 && positions.length % Vc === 0) stride = Math.round(positions.length / Vc); } catch (e) {} if (!stride || stride < 3 || stride > 20) { @@ -450,23 +483,23 @@ GpuFailLogged.add(tp); var D = {}; try { - var G = tp.x4w_1 && typeof tp.x4w_1.g1 === 'function' ? tp.x4w_1.g1(0) : null; - var B = G && G.h3x_1; - var Dev = B && B.s3h_1; - var I = tp.y4w_1 && typeof tp.y4w_1.e1 === 'function' && tp.y4w_1.e1() > 0 ? tp.y4w_1.g1(0) : null; - var IB = I && (I.q4w_1 || I.u3p_1); + var G = tp[FMeshVertexGroups] && typeof tp[FMeshVertexGroups].g1 === 'function' ? tp[FMeshVertexGroups].g1(0) : null; + var B = G && G[FMeshVertexBuffer]; + var Dev = B && B[FRenderDevice]; + var I = tp[FMeshIndexGroups] && typeof tp[FMeshIndexGroups].e1 === 'function' && tp[FMeshIndexGroups].e1() > 0 ? tp[FMeshIndexGroups].g1(0) : null; + var IB = I && (I[FMeshIndexBuffer] || I[FMeshIndexBufferOld]); D = { - 顶点组: tp.x4w_1 && tp.x4w_1.e1 ? tp.x4w_1.e1() : -1, + 顶点组: tp[FMeshVertexGroups] && tp[FMeshVertexGroups].e1 ? tp[FMeshVertexGroups].e1() : -1, 顶点缓冲资源: !!B, - 缓冲包装: !!(B && B.v3x_1), - 原生Buffer: !!(B && B.v3x_1 && B.v3x_1.d75_1), + 缓冲包装: !!(B && B[FMeshGpuBuffer]), + 原生Buffer: !!(B && B[FMeshGpuBuffer] && B[FMeshGpuBuffer][FRenderDrawBuffer]), 设备: !!Dev, - 原生GL: !!(Dev && Dev.f75_1), - 顶点数: B ? B.w3x_1 : -1, + 原生GL: !!(Dev && Dev[FRenderDeviceNative]), + 顶点数: B ? B[FMeshVertexCount] : -1, 位置长度: positions ? positions.length : -1, - 索引组: tp.y4w_1 && tp.y4w_1.e1 ? tp.y4w_1.e1() : -1, + 索引组: tp[FMeshIndexGroups] && tp[FMeshIndexGroups].e1 ? tp[FMeshIndexGroups].e1() : -1, 索引资源: !!IB, - 索引Buffer: !!(IB && IB.n3x_1 && IB.n3x_1.d75_1), + 索引Buffer: !!(IB && IB[FMeshIndexGpuBuffer] && IB[FMeshIndexGpuBuffer][FRenderDrawBuffer]), 索引个数: indices ? indices.length : -1 }; } catch (e) {} @@ -555,7 +588,7 @@ if (!Node) return null; var M = Node[FNodeMatrix]; if (!M || typeof M.length !== 'number' || M.length < 16) { - M = Node.n35_1; // 旧版矩阵字段 + M = Node[FNodeMatrixOld]; // 旧版矩阵字段 if (!M || typeof M.length !== 'number' || M.length < 16) return null; } return { x: M[12], y: M[13], z: M[14] }; @@ -622,7 +655,7 @@ Srcs.push({ Comp: Comp, Path: Path }); } try { - tank.qz1_1.s2a_1.forEach(function (VL) { + tank[FEntityRoot][FEntityComponentMap].forEach(function (VL) { var comp = (VL && typeof VL.g1 === 'function') ? VL.g1(0) : VL; if (!IsPlainObj(comp)) return; for (var K in comp) { @@ -730,9 +763,9 @@ function IsNodeValid(bpNode, pos, tankIsMoving, diagStats) { // 世界矩阵:新版是 p35_1(zl 基类构造里 = hl(),s3k/t3k/u3k 读 p35_1[12..14]) // 旧版是 n35_1;新版 n35_1 已变成布尔标志 - var M = bpNode.p35_1; + var M = bpNode[FNodeMatrix]; if (!M || typeof M.length !== 'number' || M.length < 16) { - var OldM = bpNode.n35_1; + var OldM = bpNode[FNodeMatrixOld]; M = (OldM && typeof OldM.length === 'number' && OldM.length >= 16) ? OldM : null; } if (!M) return null; @@ -1084,7 +1117,7 @@ for (var K in Obj) { try { var V = Obj[K]; - if (V && typeof V === 'object' && typeof V.k3_1 === 'number' && typeof V.equals === 'function') return V; + if (V && typeof V === 'object' && typeof V[FTeamId] === 'number' && typeof V.equals === 'function') return V; } catch (e) {} } return null; @@ -1094,7 +1127,7 @@ function IsGoldBoxEntity(Entity) { if (!Entity || !Entity.j10q_1) return false; var Type = FindEnumOf(Entity.j10q_1); - return !!(Type && Type.k3_1 === GOLD_BOX_TYPE); + return !!(Type && Type[FTeamId] === GOLD_BOX_TYPE); } function RegisterGoldBox(Entity) { @@ -1132,12 +1165,12 @@ try { var V = Bonus[K]; if (!V || typeof V !== 'object') continue; - if (V.n35_1 && V.n35_1.length >= 15) { - var M = V.n35_1; + if (V[FNodeMatrixOld] && V[FNodeMatrixOld].length >= 15) { + var M = V[FNodeMatrixOld]; return { x: M[12], y: M[13], z: M[14] }; } - if (typeof V.f20_1 === 'number' && typeof V.g20_1 === 'number' && typeof V.h20_1 === 'number') { - return { x: V.f20_1, y: V.g20_1, z: V.h20_1 }; + if (typeof V[FVecX] === 'number' && typeof V[FVecY] === 'number' && typeof V[FVecZ] === 'number') { + return { x: V[FVecX], y: V[FVecY], z: V[FVecZ] }; } } catch (e) {} } @@ -1252,7 +1285,7 @@ UpdateCam(camInfo, screenW, screenH); // 每帧只算一次相机基(需在相机位置兜底之后),供顶点变换/投影复用 - var tankList = gm.m10b_1.j11y(); + var tankList = gm[FGameModeTankManager].j11y(); var count = tankList.e1(); var drawnCount = 0, botDrawn = 0; @@ -1279,7 +1312,7 @@ // 本帧该坦克是否在移动(供「残留节点」检测:坦克在动而节点矩阵不动 = 旧位置遗留) // 用位移的指数滑动平均,而不是「单帧位移 > 10 单位」——后者对慢速移动几乎永远为假, // 会让残留检测形同虚设(残留漏掉的根因就在这里) - var tankId = tank.iz1_1; + var tankId = tank[FEntityId]; var prevPos = TankPrevPos.get(tankId); var tankMove = 0; if (prevPos) { @@ -1297,7 +1330,7 @@ if (distance > 50000) continue; var friendly = IsFriendly(tank, playerTank); - var tankName = GetTankName(tank) || ('ID:' + tank.iz1_1); + var tankName = GetTankName(tank) || ('ID:' + tank[FEntityId]); // 根据 HighlightMode 判断是否渲染该坦克 if (!ShouldRender(tankName)) continue; // 颜色按队伍:友方=蓝,敌方=红 diff --git a/tools/hornet_aim_warning.js b/tools/hornet_aim_warning.js index e37ede3..724dd3b 100644 --- a/tools/hornet_aim_warning.js +++ b/tools/hornet_aim_warning.js @@ -4,6 +4,28 @@ // 独立轻量级脚本,不依赖3D透视渲染 (function() { + // ===== 字段抽象层(语义名 → 匿名字段,总表在 hornet_schema.js)===== + var Schema = window.__hornetSchema__ || null; + function Fld(Key, Fallback) { var N = Schema ? Schema.FieldName(Key) : null; return N || Fallback; } + function Mth(Key, Fallback) { var N = Schema ? Schema.MethodName(Key) : null; return N || Fallback; } + + // ===== 匿名字段常量 ===== + var FEntityId = Fld('Entity.Id', 'iz1_1'); + var FEntityRoot = Fld('Entity.Root', 'qz1_1'); + var FEntityComponentMap = Fld('Entity.ComponentMap', 's2a_1'); + var MGameModeScene = Mth('GameMode.Scene', 'lz4'); + var MCollisionRaycast = Mth('CollisionWorld.Raycast', 'a17p'); + var FWeaponComponent = Fld('Weapon.Component', 'n11c_1'); + var FWeaponComponentAlt = Fld('Weapon.ComponentAlt', 'q112_1'); + var FWeaponTurretRoot = Fld('Weapon.TurretRoot', 'm17a_1'); + var FNodeMatrix = Fld('Node.MatrixOld', 'n35_1'); + var FRayHitPoint = Fld('Ray.HitPoint', 'y178_1'); + var FRayHitNormal = Fld('Ray.HitNormal', 'z178_1'); + var FRayDistance = Fld('Ray.Distance', 'a179_1'); + var FRayHitShape = Fld('Ray.HitShape', 'q17o_1'); + var FRayHitBody = Fld('Ray.HitBody', 'b179_1'); + var FRayFilterQuery = Fld('RayFilter.Query', 'l1da_1'); + function _start() { console.log('===== 3DTank瞄准警告启动 ====='); @@ -70,9 +92,9 @@ // ===== 获取 TDt 武器皮肤组件(炮塔) ===== function GetTDtComponent(entity) { - var comp = FindComponentByField(entity, 'n11c_1'); + var comp = FindComponentByField(entity, FWeaponComponent); if (comp) return comp; - comp = FindComponentByField(entity, 'q112_1'); + comp = FindComponentByField(entity, FWeaponComponentAlt); return comp; } @@ -85,9 +107,9 @@ // 返回 { angle: 炮塔朝向与"敌方→玩家"方向的夹角, distance: 距离, turretAngle: 炮塔朝向角度, turretDir: {x,y} } function GetTurretAimAngle(tank, enemyPos, playerPos) { var tdt = GetTDtComponent(tank); - if (!tdt || !tdt.n11c_1 || !tdt.n11c_1.m17a_1) return null; - var bpNode = tdt.n11c_1.m17a_1; - var m = bpNode.n35_1; + if (!tdt || !tdt[FWeaponComponent] || !tdt[FWeaponComponent][FWeaponTurretRoot]) return null; + var bpNode = tdt[FWeaponComponent][FWeaponTurretRoot]; + var m = bpNode[FNodeMatrix]; if (!m) return null; // 敌方→玩家方向(x,y 水平面) @@ -127,13 +149,13 @@ function GetUMtComponent() { if (UMtComponent) return UMtComponent; try { - var gmEntity = gm.lz4(); - if (!gmEntity || !gmEntity.qz1_1 || !gmEntity.qz1_1.s2a_1) return null; - gmEntity.qz1_1.s2a_1.forEach(function(valueList, key) { + var gmEntity = gm[MGameModeScene](); + if (!gmEntity || !gmEntity[FEntityRoot] || !gmEntity[FEntityRoot][FEntityComponentMap]) return null; + gmEntity[FEntityRoot][FEntityComponentMap].forEach(function(valueList, key) { if (UMtComponent) return; try { var comp = (valueList && typeof valueList.g1 === 'function') ? valueList.g1(0) : valueList; - if (comp && typeof comp.a17p === 'function') UMtComponent = comp; + if (comp && typeof comp[MCollisionRaycast] === 'function') UMtComponent = comp; } catch(e) {} }); if (UMtComponent) console.log('[瞄准] uMt 碰撞检测器已获取'); @@ -175,30 +197,30 @@ var maxDist = dist - 300; var result = { - y178_1: MkVec(0,0,0), - z178_1: MkVec(0,0,0), - a179_1: maxDist, - q17o_1: null, - b179_1: null, + [FRayHitPoint]: MkVec(0,0,0), + [FRayHitNormal]: MkVec(0,0,0), + [FRayDistance]: maxDist, + [FRayHitShape]: null, + [FRayHitBody]: null, r17o: function(t, n, i, r, e) { - this.y178_1.k26(t); - this.z178_1.k26(n); - this.a179_1 = i; - this.q17o_1 = r; - this.b179_1 = e; + this[FRayHitPoint].k26(t); + this[FRayHitNormal].k26(n); + this[FRayDistance] = i; + this[FRayHitShape] = r; + this[FRayHitBody] = e; }, - d3: function() { this.b179_1 = null; } + d3: function() { this[FRayHitBody] = null; } }; var filter = { - l1da_1: { h1: function() { return false; }, d3: function() {} }, + [FRayFilterQuery]: { h1: function() { return false; }, d3: function() {} }, w179: function() { return true; }, - d3: function() { this.l1da_1.d3(); }, + d3: function() { this[FRayFilterQuery].d3(); }, m1da: function() {} }; - var hit = uMt.a17p(startVec, dirVec, maxDist, result, filter); - blocked = !!hit && result.a179_1 < (maxDist - 50); + var hit = uMt[MCollisionRaycast](startVec, dirVec, maxDist, result, filter); + blocked = !!hit && result[FRayDistance] < (maxDist - 50); RaycastFailCount = 0; } catch(e) { RaycastFailCount++; @@ -310,19 +332,19 @@ if (aimInfo.angle >= aimThreshold) continue; // 墙体阻隔判断 - var blocked = IsLineOfSightBlocked(pos, playerPos, tank.iz1_1, frameCount); + var blocked = IsLineOfSightBlocked(pos, playerPos, tank[FEntityId], frameCount); if (blocked) continue; // 记录威胁 var proj = Project3D(pos, camInfo.pos, camInfo.yaw, camInfo.pitch, camInfo.fov, screenW, screenH); - var tankName = GetTankName(tank) || ('ID:' + tank.iz1_1); + var tankName = GetTankName(tank) || ('ID:' + tank[FEntityId]); threats.push({ pos: pos, distance: aimInfo.distance, angle: aimInfo.angle, turretDir: aimInfo.turretDir, screenProj: proj, - tankId: tank.iz1_1, + tankId: tank[FEntityId], tankName: tankName }); } diff --git a/tools/hornet_freelook.js b/tools/hornet_freelook.js index 6223f1a..a707840 100644 --- a/tools/hornet_freelook.js +++ b/tools/hornet_freelook.js @@ -4,6 +4,23 @@ // 原理:Hook mainCamera.h37,在相机矩阵更新前覆盖l35_1/k35_1参数 (function() { + // ===== 字段抽象层(语义名 → 匿名字段,总表在 hornet_schema.js)===== + var Schema = window.__hornetSchema__ || null; + function Fld(Key, Fallback) { var N = Schema ? Schema.FieldName(Key) : null; return N || Fallback; } + function Mth(Key, Fallback) { var N = Schema ? Schema.MethodName(Key) : null; return N || Fallback; } + + // ===== 匿名字段常量 ===== + var FVecX = Fld('Vec.X', 'f20_1'); + var FVecY = Fld('Vec.Y', 'g20_1'); + var FVecZ = Fld('Vec.Z', 'h20_1'); + var FCameraSceneRef = Fld('Camera.SceneRef', 'jz1_1'); + var FCameraObject = Fld('Camera.Object', 'iyx_1'); + var FCameraEuler = Fld('Camera.Euler', 'u35_1'); + var FCameraPosition = Fld('Node.Children', 'r35_1'); + var FViewMatrix = Fld('Camera.Fov', 'b37_1'); + var FCameraSceneFx = Fld('Camera.SceneFx', 'd75_1'); + var MGameModeScene = Mth('GameMode.Scene', 'lz4'); + function _start() { console.log('===== 3DTank自由视角启动 ====='); @@ -33,15 +50,15 @@ var initError = null; try { - scene = gm.lz4().jz1_1; - mainCamera = scene.iyx_1; - ff = scene.gyw_1.d75_1; - if (!mainCamera || !mainCamera.u35_1 || !mainCamera.r35_1 || !mainCamera.o37) { + scene = gm[MGameModeScene]()[FCameraSceneRef]; + mainCamera = scene[FCameraObject]; + ff = scene.gyw_1[FCameraSceneFx]; + if (!mainCamera || !mainCamera[FCameraEuler] || !mainCamera[FCameraPosition] || !mainCamera.o37) { throw new Error('相机字段缺失'); } console.log('[自由视角] 主相机:', mainCamera.constructor.name, - '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) + ')'); + 'l35=(' + mainCamera[FCameraEuler][FVecX].toFixed(2) + ',' + mainCamera[FCameraEuler][FVecY].toFixed(2) + ',' + mainCamera[FCameraEuler][FVecZ].toFixed(2) + ')', + 'k35=(' + mainCamera[FCameraPosition][FVecX].toFixed(0) + ',' + mainCamera[FCameraPosition][FVecY].toFixed(0) + ',' + mainCamera[FCameraPosition][FVecZ].toFixed(0) + ')'); } catch(e) { initError = e; console.error('[自由视角] 初始化失败:', e.message); @@ -67,12 +84,12 @@ var origH37 = mainCamera.o37; function applyFreeLookParams() { - 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; + mainCamera[FCameraEuler][FVecX] = freePitch; // pitch (f) + mainCamera[FCameraEuler][FVecY] = freeRoll; // roll (g) 保持初始值 + mainCamera[FCameraEuler][FVecZ] = freeYaw; // yaw (h) 鼠标左右控制 + mainCamera[FCameraPosition][FVecX] = freePosX; + mainCamera[FCameraPosition][FVecY] = freePosY; + mainCamera[FCameraPosition][FVecZ] = freePosZ; } // 缓存从 viewMatrix 提取的前方向/右方向(h37 更新后设置) @@ -89,11 +106,11 @@ applyFreeLookParams(); origH37.call(this); // 从更新后的 viewMatrix (u36_1) 提取前方向和右方向(水平分量 x,y) - if (this.b37_1) { - cachedFwdX = -this.b37_1[2]; // forward.x - cachedFwdY = -this.b37_1[6]; // forward.y - cachedRightX = this.b37_1[0]; // right.x - cachedRightY = this.b37_1[4]; // right.y + if (this[FViewMatrix]) { + cachedFwdX = -this[FViewMatrix][2]; // forward.x + cachedFwdY = -this[FViewMatrix][6]; // forward.y + cachedRightX = this[FViewMatrix][0]; // right.x + cachedRightY = this[FViewMatrix][4]; // right.y cachedInitialized = true; } } else { @@ -106,12 +123,12 @@ function enableFreeLook() { // 从当前相机参数初始化(平滑过渡) // l35_1: f=pitch, g=roll, h=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; + freePitch = mainCamera[FCameraEuler][FVecX]; + freeRoll = mainCamera[FCameraEuler][FVecY]; // roll 保持初始值 + freeYaw = mainCamera[FCameraEuler][FVecZ]; // yaw 鼠标控制 + freePosX = mainCamera[FCameraPosition][FVecX]; + freePosY = mainCamera[FCameraPosition][FVecY]; + freePosZ = mainCamera[FCameraPosition][FVecZ]; freeLookActive = true; console.log('[自由视角] 已开启 — WASD移动 | 鼠标旋转 | 空格上升/Shift下降 | 滚轮调速 | Alt+F关闭'); updateHud(); diff --git a/tools/hornet_lib.js b/tools/hornet_lib.js index 43206e4..baab292 100644 --- a/tools/hornet_lib.js +++ b/tools/hornet_lib.js @@ -4,6 +4,11 @@ // 用法:各工具文件通过 var lib = window.__hornetLib__ 访问 (function() { + // ===== 字段抽象层(语义名 → 匿名字段,总表在 hornet_schema.js)===== + var Schema = window.__hornetSchema__ || null; + function Fld(Key, Fallback) { var N = Schema ? Schema.FieldName(Key) : null; return N || Fallback; } + function Mth(Key, Fallback) { var N = Schema ? Schema.MethodName(Key) : null; return N || Fallback; } + console.log('===== 3DTank公共库加载 ====='); if (!window.__gameMode__) { @@ -13,17 +18,58 @@ // ===== 缓存队伍判断函数(ZCt/m2Fn) ===== var ZCt = null, m2Fn = null; - // ===== 字段抽象层引用(字段名统一在 hornet_schema.js 里维护) ===== - // 取不到抽象层时退回本地常量,保证单独加载本文件也能工作 - var Schema = window.__hornetSchema__ || null; - function FieldOf(Key, Fallback) { - if (!Schema) return Fallback; - var P = Schema.PathOf(Key); - return (P && P.indexOf('.') < 0 && P.indexOf('(') < 0) ? P : Fallback; - } - var TEAM_FIELD = FieldOf('Team.Field', 'b135_1'); // 队伍组件上的队伍对象字段 - var TEAM_ID_FIELD = FieldOf('Team.Id', 'k3_1'); // 队伍编号:0/1/2(2=中立) + // ===== 语义字段常量(取不到抽象层时用 fallback 字面名,保证单独加载也能工作) ===== + // 实体 + var FEntityRoot = Fld('Entity.Root', 'qz1_1'); // 组件宿主根对象 + var FEntityComponentMap = Fld('Entity.ComponentMap', 's2a_1'); // 宿主上的组件 Map + var FEntityId = Fld('Entity.Id', 'iz1_1'); // 实体 ID + // 向量 + var FVecX = Fld('Vec.X', 'f20_1'); + var FVecY = Fld('Vec.Y', 'g20_1'); + var FVecZ = Fld('Vec.Z', 'h20_1'); + // 队伍 + var FTeamField = Fld('Team.Field', 'b135_1'); // 队伍组件上的队伍对象字段 + var FTeamId = Fld('Team.Id', 'k3_1'); // 队伍编号:0/1/2(2=中立) var NEUTRAL_TEAM_ID = 2; + // 物理 + var FPhysicsPosition = Fld('Physics.Position', 'bzr_1'); // 世界位置向量 + var FPhysicsPositionOld1 = Fld('Physics.PositionOld1', 'gzh_1'); // 旧版位置字段名(兜底候选) + var FPhysicsPositionOld2 = Fld('Physics.PositionOld2', 'pz7_1'); // 更旧位置字段名(兜底候选) + // 坦克状态 + var FTankFsm = Fld('Tank.Fsm', 'p10c_1'); // 状态机字段(组件识别用) + var FTankState = Fld('Tank.State', 'k10c_1'); // 状态枚举对象 + var FTankStateValue = Fld('Tank.StateValue', 'k3_1'); // 状态数值 + // 用户信息 / 血量 / 装备分 + var FUserName = Fld('User.Name', 'c12n_1'); + var FUserClan = Fld('User.Clan', 'e12n_1'); + var FHealthMax = Fld('Health.Max', 'p12o_1'); + var FHealthCurrent = Fld('Health.Current', 'q12o_1'); + var FGearScore = Fld('Gear.Score', 'cjf_1'); + // GameMode + var FGameModeTankManager = Fld('GameMode.TankManager', 'm10b_1'); + var FGameModeOwnedTank = Fld('GameMode.OwnedTank', 'u10b_1'); + var FGameModeObservedTank = Fld('GameMode.ObservedTank', 't10b_1'); + var FGameModeLocalTeam = Fld('GameMode.LocalTeam', 'v10b_1'); + // 相机 + var FCameraSceneRef = Fld('Camera.SceneRef', 'jz1_1'); + var FCameraObject = Fld('Camera.Object', 'iyx_1'); + var FCameraPosition = Fld('Camera.Position', 't35_1'); + var FCameraViewMatrix = Fld('Camera.ViewMatrix', 'd37_1'); + var FCameraClipMatrix = Fld('Camera.ClipMatrix', 'g37_1'); + var FCameraViewport = Fld('Camera.Viewport', 'w36_1'); + var FCameraFov = Fld('Camera.Fov', 'b37_1'); + // 射线检测结果 / 过滤器 + var FRayStartPoint = Fld('Ray.StartPoint', 'y178_1'); + var FRayEndPoint = Fld('Ray.EndPoint', 'z178_1'); + var FRayDistance = Fld('Ray.Distance', 'a179_1'); + var FRayHitEntity = Fld('Ray.HitEntity', 'q17o_1'); + var FRayHitShape = Fld('Ray.HitShape', 'b179_1'); + var FRayFilterContext = Fld('RayFilter.Context', 'l1da_1'); + // 方法 + var MGameModeScene = Mth('GameMode.Scene', 'lz4'); // 场景实体 + var MTankList = Mth('GameMode.TankList', 'j11y'); // 坦克列表 + var MIsTankAlive = Mth('Tank.IsAlive', 's14j'); // 引擎可见性判定 + var MRaycast = Mth('Ray.Cast', 'a17p'); // 全局射线检测 // 加载队伍判断函数(从 module_87072 导出) // 新旧差异(main.7cfa4058): @@ -57,9 +103,9 @@ // 遍历 entity.wyj_1.r2a_1(Map<组件标识, 组件列表>),value 是列表类(s_),g1(0) 获取实际组件 // 注意:用 !== undefined 检查字段存在性,避免 falsy 值(0/""/null/false)漏判 function FindComponentByField(entity, fieldName) { - if (!entity || !entity.qz1_1 || !entity.qz1_1.s2a_1) return null; + if (!entity || !entity[FEntityRoot] || !entity[FEntityRoot][FEntityComponentMap]) return null; var result = null; - entity.qz1_1.s2a_1.forEach(function(valueList, key) { + entity[FEntityRoot][FEntityComponentMap].forEach(function(valueList, key) { if (result) return; try { var comp = (valueList && typeof valueList.g1 === 'function') ? valueList.g1(0) : valueList; @@ -77,18 +123,18 @@ // njt() 里 `bzr_1.l27(上一物理态位置, 当前物理态位置, 插值)` —— bzr_1 就是世界位置。 // ⚠ xzq_1 是车体半尺寸(用于包围盒/出生点:`-xzq_1.h20_1/2 + 10`),绝不能当位置用。 // 返回 {x, y, z},x/y 水平面,z 高度 - var POS_FIELDS = ['bzr_1', 'gzh_1', 'pz7_1']; + var POS_FIELDS = [FPhysicsPosition, FPhysicsPositionOld1, FPhysicsPositionOld2]; function GetTankPosition(tank) { for (var i = 0; i < POS_FIELDS.length; i++) { var Key = POS_FIELDS[i]; var Comp = null; try { Comp = FindComponentByField(tank, Key); } catch (e) { Comp = null; } var V = Comp && Comp[Key]; - if (V && typeof V.f20_1 === 'number' && typeof V.g20_1 === 'number' && typeof V.h20_1 === 'number') { + if (V && typeof V[FVecX] === 'number' && typeof V[FVecY] === 'number' && typeof V[FVecZ] === 'number') { // 数值有效性校验:NaN/超大值一律视为读取失败(避免雷达画到画布外) - if (isFinite(V.f20_1) && isFinite(V.g20_1) && isFinite(V.h20_1) && - Math.abs(V.f20_1) < 1e7 && Math.abs(V.g20_1) < 1e7 && Math.abs(V.h20_1) < 1e7) { - return { x: V.f20_1, y: V.g20_1, z: V.h20_1 }; + if (isFinite(V[FVecX]) && isFinite(V[FVecY]) && isFinite(V[FVecZ]) && + Math.abs(V[FVecX]) < 1e7 && Math.abs(V[FVecY]) < 1e7 && Math.abs(V[FVecZ]) < 1e7) { + return { x: V[FVecX], y: V[FVecY], z: V[FVecZ] }; } } } @@ -98,13 +144,13 @@ // ===== 坦克组件(lxt / TankComponent)===== // 特征:有 k10c_1(状态枚举)与 p10c_1(fsm) function GetTankComponent(tank) { - if (!tank || !tank.qz1_1 || !tank.qz1_1.s2a_1) return null; + if (!tank || !tank[FEntityRoot] || !tank[FEntityRoot][FEntityComponentMap]) return null; var Found = null; - tank.qz1_1.s2a_1.forEach(function(valueList, key) { + tank[FEntityRoot][FEntityComponentMap].forEach(function(valueList, key) { if (Found) return; try { var comp = (valueList && typeof valueList.g1 === 'function') ? valueList.g1(0) : valueList; - if (comp && comp.p10c_1 && comp.k10c_1 !== undefined) Found = comp; + if (comp && comp[FTankFsm] && comp[FTankState] !== undefined) Found = comp; } catch(e) {} }); return Found; @@ -114,7 +160,7 @@ // 状态枚举:0=DEAD, 1=DEAD_PHANTOM, 2=SEMI_ACTIVE, 3=ACTIVE, 4=DISABLED, 5=TELEPORT function GetTankState(tank) { var C = GetTankComponent(tank); - return (C && C.k10c_1) ? C.k10c_1.k3_1 : -1; + return (C && C[FTankState]) ? C[FTankState][FTankStateValue] : -1; } // 可见/战斗中的坦克:**直接调用引擎自己的判定 s14j()** @@ -122,7 +168,7 @@ // 旧实现只认 2/3,会漏掉传送态;死亡态 0/1/4 会被正确排除 function IsTankAlive(tank) { var C = GetTankComponent(tank); - try { if (C && typeof C.s14j === 'function') return !!C.s14j(); } catch (e) {} + try { if (C && typeof C[MIsTankAlive] === 'function') return !!C[MIsTankAlive](); } catch (e) {} var s = GetTankState(tank); return s === 2 || s === 3 || s === 5; } @@ -130,20 +176,20 @@ // ===== 获取坦克名称(yot 用户信息组件) ===== // s120_1 = userName, u120_1 = clanTag(可为null), t120_1 = userRank function GetTankName(tank) { - var userComp = FindComponentByField(tank, 'c12n_1'); + var userComp = FindComponentByField(tank, FUserName); if (!userComp) return null; - var name = userComp.c12n_1 || ''; - var clanTag = userComp.e12n_1 || ''; + var name = userComp[FUserName] || ''; + var clanTag = userComp[FUserClan] || ''; return clanTag ? (clanTag + ' ' + name) : name; } // ===== 获取坦克血量(Vkt 血量组件) ===== // f122_1 = 最大血量(max), g122_1 = 当前血量(current, 死亡时=0) function GetTankHealth(tank) { - var healthComp = FindComponentByField(tank, 'q12o_1'); + var healthComp = FindComponentByField(tank, FHealthCurrent); if (!healthComp) return null; - var max = healthComp.p12o_1 || 0; - var cur = healthComp.q12o_1 || 0; + var max = healthComp[FHealthMax] || 0; + var cur = healthComp[FHealthCurrent] || 0; if (max <= 0) return null; return { current: cur, max: max, percent: cur / max }; } @@ -151,9 +197,9 @@ // ===== 装备分数(战斗 GS:BattleGearScoreCC.score 落在组件的 cjf_1 上) ===== // 来源:module_85183 的 BattleGearScoreCC(score = cjf_1);战斗内每辆坦克各有一份 function GetGearScore(tank) { - var c = FindComponentByField(tank, 'cjf_1'); - if (!c || typeof c.cjf_1 !== 'number') return null; - return c.cjf_1; + var c = FindComponentByField(tank, FGearScore); + if (!c || typeof c[FGearScore] !== 'number') return null; + return c[FGearScore]; } // ===== 人机识别 ===== @@ -166,10 +212,10 @@ var MaxScore = typeof opt.maxScore === 'number' ? opt.maxScore : 8000; BotSet = new Set(); var gm = window.__gameMode__; - if (!gm || !gm.m10b_1) return BotSet; + if (!gm || !gm[FGameModeTankManager]) return BotSet; var playerTank = GetPlayerTank(gm); var list, n; - try { list = gm.m10b_1.j11y(); n = list.e1(); } catch (e) { return BotSet; } + try { list = gm[FGameModeTankManager][MTankList](); n = list.e1(); } catch (e) { return BotSet; } var Groups = { ally: {}, enemy: {} }; // 组 → { 分数 → [坦克...] } for (var i = 0; i < n; i++) { var tank = list.g1(i); @@ -197,9 +243,9 @@ var g = window.__gameMode__; R.gm = !!g; if (!g) return R; - try { R.scene = !!g.lz4().jz1_1; } catch (e) { R.errors.push('scene: ' + e.message); } + try { R.scene = !!g[MGameModeScene]()[FCameraSceneRef]; } catch (e) { R.errors.push('scene: ' + e.message); } var list = null; - try { list = g.m10b_1.j11y(); R.tanks = list.e1(); } catch (e) { R.errors.push('tankList: ' + e.message); } + try { list = g[FGameModeTankManager][MTankList](); R.tanks = list.e1(); } catch (e) { R.errors.push('tankList: ' + e.message); } if (list) { var N = Math.min(list.e1(), 8); for (var i = 0; i < N; i++) { @@ -213,7 +259,7 @@ for (var j = 0; j < POS_FIELDS.length; j++) { var K = POS_FIELDS[j], C = null; try { C = FindComponentByField(T, K); } catch (e) { C = null; } - if (C && C[K] && typeof C[K].f20_1 === 'number') { Src = K; break; } + if (C && C[K] && typeof C[K][FVecX] === 'number') { Src = K; break; } } R.posHit[Src] = (R.posHit[Src] || 0) + 1; } else { @@ -221,13 +267,13 @@ // 失败时把该坦克组件的字段名与"候选向量"打印出来,便于定位 try { var Fields = []; - T.qz1_1.s2a_1.forEach(function (VL) { + T[FEntityRoot][FEntityComponentMap].forEach(function (VL) { var C = (VL && typeof VL.g1 === 'function') ? VL.g1(0) : VL; if (!C) return; for (var K2 in C) { var V2 = C[K2]; if (V2 && typeof V2 === 'object') { - var Kind = (typeof V2.f20_1 === 'number') ? 'O$' : (typeof V2.length === 'number' && V2.length >= 15 ? 'M' : ''); + var Kind = (typeof V2[FVecX] === 'number') ? 'O$' : (typeof V2.length === 'number' && V2.length >= 15 ? 'M' : ''); if (Kind) Fields.push(K2 + ':' + Kind); } } @@ -248,12 +294,12 @@ // checkPosition: 是否检查位置有效性(默认 true) function GetPlayerTank(gm, checkPosition) { if (checkPosition === undefined) checkPosition = true; - var t = gm.u10b_1; + var t = gm[FGameModeOwnedTank]; if (t && (!checkPosition || GetTankPosition(t))) return t; - t = gm.t10b_1; + t = gm[FGameModeObservedTank]; if (t && (!checkPosition || GetTankPosition(t))) return t; try { - var tl = gm.m10b_1.j11y(); + var tl = gm[FGameModeTankManager][MTankList](); if (tl.e1() > 0) { t = tl.g1(0); if (t && (!checkPosition || GetTankPosition(t))) return t; @@ -278,8 +324,8 @@ if (ZCt) { try { var T = ZCt(tank); if (T) return T; } catch (e) {} } - var Comp = FindComponentByField(tank, TEAM_FIELD); - return Comp ? Comp[TEAM_FIELD] : null; + var Comp = FindComponentByField(tank, FTeamField); + return Comp ? Comp[FTeamField] : null; } // 读队伍编号:0 / 1 = 两个阵营,2 = 中立(q$());读不到返回 -1 @@ -287,7 +333,7 @@ // (module_87072 里 P7()/$ct() 都按 k3_1 分派) function GetTeamId(Team) { if (!Team) return -1; - var Id = Team[TEAM_ID_FIELD]; + var Id = Team[FTeamId]; return (typeof Id === 'number') ? Id : -1; } @@ -296,9 +342,9 @@ function RememberLocalTeam(gm) { var t = null; - try { t = gm.u10b_1; } catch (e) { t = null; } + try { t = gm[FGameModeOwnedTank]; } catch (e) { t = null; } if (t) { - var Id = t.iz1_1; + var Id = t[FEntityId]; if (LocalTeam && LocalTeamTankId === Id) return; var Team = ReadTeamRaw(t); var TeamId = GetTeamId(Team); @@ -308,7 +354,7 @@ // u10b_1 为空(死亡/观战):保持已有缓存;若还没缓存过,再试 GameMode 上的本地队伍字段 if (LocalTeam) return; try { - var G = gm.v10b_1; + var G = gm[FGameModeLocalTeam]; var GId = GetTeamId(G); if (GId >= 0 && GId !== NEUTRAL_TEAM_ID) LocalTeam = G; } catch (e) {} @@ -334,7 +380,7 @@ // 可靠基准:本地拥有坦克的队伍(缓存)→ GameMode 上的本地队伍字段 // 观战目标(t10b_1)可能是敌人,绝不作为基准 var BaseTeam = LocalTeam; - if (!BaseTeam && gm) { try { BaseTeam = gm.v10b_1; } catch (e) {} } + if (!BaseTeam && gm) { try { BaseTeam = gm[FGameModeLocalTeam]; } catch (e) {} } var BaseId = GetTeamId(BaseTeam); if (BaseId < 0 || BaseId === NEUTRAL_TEAM_ID) { var Last2 = TankFriendCache.get(tank); // 基准不可靠(极早期):沿用上次 @@ -346,7 +392,7 @@ if (!TeamJudgeLogged.has(tank)) { TeamJudgeLogged.add(tank); try { - console.log('[敌我] id=' + tank.iz1_1 + ' 队伍=' + TankId + ' 本地=' + BaseId + + console.log('[敌我] id=' + tank[FEntityId] + ' 队伍=' + TankId + ' 本地=' + BaseId + ' → ' + (Friendly ? '友方' : '敌方') + ' name=' + (GetTankName(tank) || '?')); } catch (e) {} } @@ -367,12 +413,12 @@ // heading 供雷达使用(forward = (sin(heading), cos(heading)),水平面) function GetCameraInfo(gm) { try { - var scene = gm.lz4().jz1_1; - var cam = scene && scene.iyx_1; - if (!cam || !cam.t35_1) return null; - var P = cam.t35_1; - var pos = { x: P.f20_1, y: P.g20_1, z: P.h20_1 }; - var V = cam.d37_1; + var scene = gm[MGameModeScene]()[FCameraSceneRef]; + var cam = scene && scene[FCameraObject]; + if (!cam || !cam[FCameraPosition]) return null; + var P = cam[FCameraPosition]; + var pos = { x: P[FVecX], y: P[FVecY], z: P[FVecZ] }; + var V = cam[FCameraViewMatrix]; var fx = 0, fy = 1, fz = 0; if (V && typeof V.length === 'number' && V.length >= 11) { fx = -V[2]; fy = -V[6]; fz = -V[10]; @@ -380,14 +426,14 @@ fx /= len; fy /= len; fz /= len; } var viewW = 0, viewH = 0; - try { viewW = cam.w36_1.h29(); viewH = cam.w36_1.g29(); } catch (e) {} + try { viewW = cam[FCameraViewport].h29(); viewH = cam[FCameraViewport].g29(); } catch (e) {} return { pos: pos, yaw: Math.atan2(-fx, fy), pitch: Math.asin(Math.max(-1, Math.min(1, fz))), heading: Math.atan2(fx, fy), - fov: cam.b37_1 || 1.0471976, - matrix: cam.g37_1 || null, + fov: cam[FCameraFov] || 1.0471976, + matrix: cam[FCameraClipMatrix] || null, viewW: viewW, viewH: viewH }; @@ -400,14 +446,14 @@ // g16x(startVec, dirVec, maxDist, resultObj, filterObj) = 全局射线检测 function GetUMtComponent(gm) { try { - var gmEntity = gm.lz4(); - if (!gmEntity || !gmEntity.qz1_1 || !gmEntity.qz1_1.s2a_1) return null; + var gmEntity = gm[MGameModeScene](); + if (!gmEntity || !gmEntity[FEntityRoot] || !gmEntity[FEntityRoot][FEntityComponentMap]) return null; var uMt = null; - gmEntity.qz1_1.s2a_1.forEach(function(valueList, key) { + gmEntity[FEntityRoot][FEntityComponentMap].forEach(function(valueList, key) { if (uMt) return; try { var comp = (valueList && typeof valueList.g1 === 'function') ? valueList.g1(0) : valueList; - if (comp && typeof comp.a17p === 'function') uMt = comp; + if (comp && typeof comp[MRaycast] === 'function') uMt = comp; } catch(e) {} }); return uMt; @@ -419,9 +465,9 @@ // ===== 构造 mock O$ 向量(用于射线检测接口) ===== function MkVec(x, y, z) { return { - f20_1: x || 0, g20_1: y || 0, h20_1: z || 0, - k26: function(t) { this.f20_1 = t.f20_1; this.g20_1 = t.g20_1; this.h20_1 = t.h20_1; }, - n23: function(x, y, z) { this.f20_1 = x; this.g20_1 = y; this.h20_1 = z; return this; } + [FVecX]: x || 0, [FVecY]: y || 0, [FVecZ]: z || 0, + k26: function(t) { this[FVecX] = t[FVecX]; this[FVecY] = t[FVecY]; this[FVecZ] = t[FVecZ]; }, + n23: function(x, y, z) { this[FVecX] = x; this[FVecY] = y; this[FVecZ] = z; return this; } }; } @@ -434,28 +480,28 @@ var startVec = MkVec(fromPos.x, fromPos.y, fromPos.z); var dirVec = MkVec(dirX, dirY, dirZ); var result = { - y178_1: MkVec(0, 0, 0), - z178_1: MkVec(0, 0, 0), - a179_1: maxDist, - q17o_1: null, - b179_1: null, + [FRayStartPoint]: MkVec(0, 0, 0), + [FRayEndPoint]: MkVec(0, 0, 0), + [FRayDistance]: maxDist, + [FRayHitEntity]: null, + [FRayHitShape]: null, r17o: function(t, n, i, r, e) { - this.y178_1.k26(t); - this.z178_1.k26(n); - this.a179_1 = i; - this.q17o_1 = r; - this.b179_1 = e; + this[FRayStartPoint].k26(t); + this[FRayEndPoint].k26(n); + this[FRayDistance] = i; + this[FRayHitEntity] = r; + this[FRayHitShape] = e; }, - d3: function() { this.b179_1 = null; } + d3: function() { this[FRayHitShape] = null; } }; var filter = { - l1da_1: { h1: function() { return false; }, d3: function() {} }, + [FRayFilterContext]: { h1: function() { return false; }, d3: function() {} }, w179: function() { return true; }, - d3: function() { this.l1da_1.d3(); }, + d3: function() { this[FRayFilterContext].d3(); }, m1da: function() {} }; - var hit = uMt.a17p(startVec, dirVec, maxDist, result, filter); - return (hit && result.a179_1 < maxDist) ? result.a179_1 : -1; + var hit = uMt[MRaycast](startVec, dirVec, maxDist, result, filter); + return (hit && result[FRayDistance] < maxDist) ? result[FRayDistance] : -1; } catch(e) { return -1; } diff --git a/tools/hornet_radar.js b/tools/hornet_radar.js index 3176673..388db7e 100644 --- a/tools/hornet_radar.js +++ b/tools/hornet_radar.js @@ -3,6 +3,29 @@ // 功能:在屏幕右上角显示2D雷达,显示所有坦克的相对位置 (function() { + // ===== 字段抽象层(语义名 → 匿名字段,总表在 hornet_schema.js)===== + var Schema = window.__hornetSchema__ || null; + function Fld(Key, Fallback) { var N = Schema ? Schema.FieldName(Key) : null; return N || Fallback; } + function Mth(Key, Fallback) { var N = Schema ? Schema.MethodName(Key) : null; return N || Fallback; } + + // ===== 匿名字段常量 ===== + var FVecX = Fld('Vec.X', 'f20_1'); + var FVecY = Fld('Vec.Y', 'g20_1'); + var FVecZ = Fld('Vec.Z', 'h20_1'); + var FEntityId = Fld('Entity.Id', 'iz1_1'); + var FEntityRoot = Fld('Entity.Root', 'qz1_1'); + var FEntityComponentMap = Fld('Entity.ComponentMap', 's2a_1'); + var FTankHullComponent = Fld('Tank.HullComponent', 'qzv_1'); + var FTankHullDirection = Fld('Tank.HullDirection', 'wzl_1'); + var MGameModeScene = Mth('GameMode.Scene', 'lz4'); + var MCollisionRaycast = Mth('CollisionWorld.Raycast', 'a17p'); + var FRayHitPoint = Fld('Ray.HitPoint', 'y178_1'); + var FRayHitNormal = Fld('Ray.HitNormal', 'z178_1'); + var FRayDistance = Fld('Ray.Distance', 'a179_1'); + var FRayHitShape = Fld('Ray.HitShape', 'q17o_1'); + var FRayHitBody = Fld('Ray.HitBody', 'b179_1'); + var FRayFilterQuery = Fld('RayFilter.Query', 'l1da_1'); + function _start() { console.log('===== 3DTank雷达启动 ====='); @@ -38,12 +61,12 @@ // ===== 获取坦克方向(Kkt 组件的 dzc_1) ===== function getTankDirection(tank) { - var hullComp = FindComponentByField(tank, 'qzv_1'); - if (hullComp && hullComp.wzl_1) { + var hullComp = FindComponentByField(tank, FTankHullComponent); + if (hullComp && hullComp[FTankHullDirection]) { return { - x: hullComp.wzl_1.f20_1, - y: hullComp.wzl_1.g20_1, - z: hullComp.wzl_1.h20_1 + x: hullComp[FTankHullDirection][FVecX], + y: hullComp[FTankHullDirection][FVecY], + z: hullComp[FTankHullDirection][FVecZ] }; } return null; @@ -82,13 +105,13 @@ function getUMtComponent() { if (UMtComponent) return UMtComponent; try { - var gmEntity = gm.lz4(); - if (!gmEntity || !gmEntity.qz1_1 || !gmEntity.qz1_1.s2a_1) return null; - gmEntity.qz1_1.s2a_1.forEach(function(valueList, key) { + var gmEntity = gm[MGameModeScene](); + if (!gmEntity || !gmEntity[FEntityRoot] || !gmEntity[FEntityRoot][FEntityComponentMap]) return null; + gmEntity[FEntityRoot][FEntityComponentMap].forEach(function(valueList, key) { if (UMtComponent) return; try { var comp = (valueList && typeof valueList.g1 === 'function') ? valueList.g1(0) : valueList; - if (comp && typeof comp.a17p === 'function') UMtComponent = comp; + if (comp && typeof comp[MCollisionRaycast] === 'function') UMtComponent = comp; } catch(e) {} }); if (UMtComponent) console.log('[雷达] uMt 碰撞检测器已获取'); @@ -110,21 +133,21 @@ var startVec = MkVec(fromPos.x, fromPos.y, fromPos.z); var dirVec = MkVec(dirX, dirY, dirZ); var result = { - y178_1: MkVec(0,0,0), z178_1: MkVec(0,0,0), - a179_1: maxDist, q17o_1: null, b179_1: null, + [FRayHitPoint]: MkVec(0,0,0), [FRayHitNormal]: MkVec(0,0,0), + [FRayDistance]: maxDist, [FRayHitShape]: null, [FRayHitBody]: null, r17o: function(t, n, i, r, e) { - this.y178_1.k26(t); this.z178_1.k26(n); - this.a179_1 = i; this.q17o_1 = r; this.b179_1 = e; + this[FRayHitPoint].k26(t); this[FRayHitNormal].k26(n); + this[FRayDistance] = i; this[FRayHitShape] = r; this[FRayHitBody] = e; }, - d3: function() { this.b179_1 = null; } + d3: function() { this[FRayHitBody] = null; } }; var filter = { - l1da_1: { h1: function() { return false; }, d3: function() {} }, + [FRayFilterQuery]: { h1: function() { return false; }, d3: function() {} }, w179: function() { return true; }, d3: function() {}, m1da: function() {} }; - var hit = uMt.a17p(startVec, dirVec, maxDist, result, filter); - return (hit && result.a179_1 < maxDist) ? result.a179_1 : -1; + var hit = uMt[MCollisionRaycast](startVec, dirVec, maxDist, result, filter); + return (hit && result[FRayDistance] < maxDist) ? result[FRayDistance] : -1; } catch(e) { return -1; } @@ -689,7 +712,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.iz1_1 : '?'), 5, 24); + ctx.fillText('Player: ' + (playerTank ? playerTank[FEntityId] : '?'), 5, 24); } else { ctx.fillText('Player: NOT FOUND', 5, 24); } diff --git a/tools/hornet_rearview.js b/tools/hornet_rearview.js index 60c640a..9e85f11 100644 --- a/tools/hornet_rearview.js +++ b/tools/hornet_rearview.js @@ -4,6 +4,44 @@ // 原理:Hook gl.bindFramebuffer 重定向到自定义 FBO,用后视相机参数调用游戏渲染器,readback 显示 (function() { + // ===== 字段抽象层(语义名 → 匿名字段,总表在 hornet_schema.js)===== + var Schema = window.__hornetSchema__ || null; + function Fld(Key, Fallback) { var N = Schema ? Schema.FieldName(Key) : null; return N || Fallback; } + function Mth(Key, Fallback) { var N = Schema ? Schema.MethodName(Key) : null; return N || Fallback; } + + // ===== 场景 / 渲染器 ===== + var FSceneRef = Fld('Camera.SceneRef', 'jz1_1'); // 场景实体上的相机引用 + var FRenderer = Fld('Render.Renderer', 'gyw_1'); // 渲染器实例 + var FDrawer = Fld('Render.Drawer', 'd75_1'); // Ff 绘制器实例 + var FRootNode = Fld('Scene.RootNode', 'fyx_1'); // 场景根节点 + var FCameraObject = Fld('Camera.Object', 'iyx_1'); // 主相机对象 + var FGl = Fld('Render.Gl', 'w39_1'); // GL 包装对象(o3c/n3c) + var FGlNative = Fld('Render.GlNative', 'b74_1'); // 原生 WebGL 上下文 + + // ===== 相机 ===== + var FAngles = Fld('Camera.Angles', 'u35_1'); // 朝向角(pitch/roll/yaw) + var FPosition = Fld('Camera.Position', 'r35_1'); // 位置向量 + var FFov = Fld('Camera.Fov', 'b37_1'); // 视图矩阵数据 + var FViewportWidth = Fld('Camera.ViewportWidth', 'u36_1'); // 渲染宽 + var FViewportHeight = Fld('Camera.ViewportHeight', 'v36_1');// 渲染高 + var FFovValue = Fld('Camera.FovValue', 'z36_1'); // 视场角 + var FNear = Fld('Camera.Near', 'x36_1'); // 近裁剪面 + var FFar = Fld('Camera.Far', 'y36_1'); // 远裁剪面 + var MCameraUpdate = Mth('Camera.Update', 'o37'); // 重算相机矩阵 + var FX = Fld('Vec.X', 'f20_1'); + var FY = Fld('Vec.Y', 'g20_1'); + var FZ = Fld('Vec.Z', 'h20_1'); + + // ===== 渲染开关 / 队列 ===== + var FSettings = Fld('Render.Settings', 'y38_1'); // 渲染开关集合 + var FShadowEnabled = Fld('Render.ShadowEnabled', 'e3o_1'); // 阴影开关 + var FPostProcessEnabled = Fld('Render.PostProcessEnabled', 'f3o_1'); // 后处理开关 + var FExtraPassEnabled = Fld('Render.ExtraPassEnabled', 'k3o_1'); // 额外 pass 开关 + var FLayerQueue = Fld('Render.LayerQueue', 'e39_1'); // 渲染层队列 + var MDrawScene = Mth('Render.Draw', 'f3t'); // 渲染主场景 + var MGetDrawingWidth = Mth('Render.DrawingWidth', 'o3c'); // drawingBufferWidth + var MGetDrawingHeight = Mth('Render.DrawingHeight', 'n3c'); // drawingBufferHeight + function _start() { console.log('===== 3DTank后视镜(原生渲染版)启动 ====='); @@ -81,27 +119,27 @@ var initError = null; try { - scene = gm.lz4().jz1_1; + scene = gm.lz4()[FSceneRef]; console.log('[后视] 场景对象:', scene); // hC 渲染器实例(jr.lye_1) - var renderer = scene.gyw_1; + var renderer = scene[FRenderer]; console.log('[后视] 渲染器:', renderer); // Ff 绘制器实例(hC.r74_1) - ff = renderer.d75_1; + ff = renderer[FDrawer]; console.log('[后视] Ff绘制器:', ff); // 场景根节点(jr.kyf_1) - rootNode = scene.fyx_1; + rootNode = scene[FRootNode]; console.log('[后视] 根节点:', rootNode); // 主相机 Ha 实例(jr.nyf_1) - mainCamera = scene.iyx_1; + mainCamera = scene[FCameraObject]; console.log('[后视] 主相机:', mainCamera); // 游戏 gl context(Ff.p39_1.p73_1 = BS.p73_1) - gl = ff.w39_1.b74_1; + gl = ff[FGl][FGlNative]; console.log('[后视] GL上下文:', gl); if (!ff || !rootNode || !mainCamera || !gl) { @@ -109,11 +147,11 @@ } // 探测相机字段 - console.log('[后视] 相机u35_1:', mainCamera.u35_1); - console.log('[后视] 相机r35_1:', mainCamera.r35_1); - console.log('[后视] 相机b37_1:', mainCamera.b37_1); + console.log('[后视] 相机u35_1:', mainCamera[FAngles]); + console.log('[后视] 相机r35_1:', mainCamera[FPosition]); + console.log('[后视] 相机b37_1:', mainCamera[FFov]); - if (!mainCamera.u35_1 || !mainCamera.r35_1) { + if (!mainCamera[FAngles] || !mainCamera[FPosition]) { throw new Error('相机字段缺失'); } @@ -245,10 +283,10 @@ // Hook BS.h3c/g3c:后视渲染时返回 FBO 尺寸(修复 screenSize/aspect ratio) // y3s 会调用 BS.h3c()/g3c() 获取 drawingBufferWidth/Height 设置相机 l36_1/m36_1 // 不 Hook 的话 aspect ratio 会用游戏 canvas 尺寸(如1920x1080),导致画面比例错误 - origH3c = ff.w39_1.o3c.bind(ff.w39_1); - origG3c = ff.w39_1.n3c.bind(ff.w39_1); - ff.w39_1.o3c = function() { return rearviewRendering ? rw : origH3c(); }; - ff.w39_1.n3c = function() { return rearviewRendering ? rh : origG3c(); }; + origH3c = ff[FGl][MGetDrawingWidth].bind(ff[FGl]); + origG3c = ff[FGl][MGetDrawingHeight].bind(ff[FGl]); + ff[FGl][MGetDrawingWidth] = function() { return rearviewRendering ? rw : origH3c(); }; + ff[FGl][MGetDrawingHeight] = function() { return rearviewRendering ? rh : origG3c(); }; console.log('[后视] BS.o3c/n3c 已 Hook'); } @@ -309,17 +347,17 @@ var camPosZ = playerPos.z + Config.cameraHeight; // ===== 保存主相机参数 ===== - 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.u36_1; - var savedM36 = mainCamera.v36_1; - var savedS36 = mainCamera.z36_1; - var savedQ36 = mainCamera.x36_1; - var savedR36 = mainCamera.y36_1; + var savedPitch = mainCamera[FAngles][FX]; + var savedYaw = mainCamera[FAngles][FY]; + var savedRoll = mainCamera[FAngles][FZ]; + var savedX = mainCamera[FPosition][FX]; + var savedY = mainCamera[FPosition][FY]; + var savedZ = mainCamera[FPosition][FZ]; + var savedL36 = mainCamera[FViewportWidth]; + var savedM36 = mainCamera[FViewportHeight]; + var savedS36 = mainCamera[FFovValue]; + var savedQ36 = mainCamera[FNear]; + var savedR36 = mainCamera[FFar]; // 临时暂停自由视角的 h37 hook,避免覆盖后视镜相机参数 var fl = window.__hornetFreeLook__; @@ -330,41 +368,41 @@ // g=π 使水平方向反向(朝后看),h=yaw+π 保持水平视角正确 // f=-pitch 使垂直方向与主视一致(低头看后方地形) // up 朝下 → FBO 画面颠倒 → GL readback Y 翻转修正 - 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.u36_1 = rw; - mainCamera.v36_1 = rh; - mainCamera.z36_1 = Config.fov; - mainCamera.x36_1 = Config.near; - mainCamera.y36_1 = Config.far; + mainCamera[FAngles][FX] = rearPitch; + mainCamera[FAngles][FY] = Math.PI; + mainCamera[FAngles][FZ] = camInfo.yaw + Math.PI; + mainCamera[FPosition][FX] = camPosX; + mainCamera[FPosition][FY] = camPosY; + mainCamera[FPosition][FZ] = camPosZ; + mainCamera[FViewportWidth] = rw; + mainCamera[FViewportHeight] = rh; + mainCamera[FFovValue] = Config.fov; + mainCamera[FNear] = Config.near; + mainCamera[FFar] = Config.far; // 更新相机矩阵(viewMatrix, projectionMatrix, viewProjectionMatrix) - mainCamera.o37(); + mainCamera[MCameraUpdate](); // 保存后视视图矩阵副本(用于 UI 标记投影,finally 会恢复相机所以需提前保存) - var rearViewMatrix = new Float32Array(mainCamera.b37_1); + var rearViewMatrix = new Float32Array(mainCamera[FFov]); // ===== 保存并禁用阴影/后处理(避免它们切换 FBO 覆盖 rearFbo)===== // p38_1.b3o_1 控制 z39_1(阴影),c3o_1 控制 c3a_1/d3a_1(后处理),z3n_1 控制 f3a_1 // 禁用后 y3s 只执行主场景渲染 z3k,跳过所有 FBO 切换的 pass - 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; + var savedX3n = ff[FSettings][FShadowEnabled]; + var savedY3n = ff[FSettings][FPostProcessEnabled]; + var savedZ3n = ff[FSettings][FExtraPassEnabled]; + ff[FSettings][FShadowEnabled] = false; + ff[FSettings][FPostProcessEnabled] = false; + ff[FSettings][FExtraPassEnabled] = false; // ===== 禁用 UI 渲染层(防止技能条等 UI 混入后视 FBO)===== // 保存 UI 层队列,替换为空假对象,y3s 后恢复 var savedUiQueues = {}; for (var ui = 0; ui < uiLayerIndices.length; ui++) { var li = uiLayerIndices[ui]; - savedUiQueues[li] = ff.e39_1[li]; - ff.e39_1[li] = fakeQueue; + savedUiQueues[li] = ff[FLayerQueue][li]; + ff[FLayerQueue][li] = fakeQueue; } // ===== 渲染到 FBO ===== @@ -377,7 +415,7 @@ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); // 调用游戏原生渲染器(已禁用阴影/后处理/UI层,只渲染 3D 场景) - ff.f3t(rootNode, mainCamera); + ff[MDrawScene](rootNode, mainCamera); rearviewRendering = false; @@ -430,9 +468,9 @@ // ===== 恢复阴影/后处理(异常时也要恢复)===== try { if (typeof savedX3n !== 'undefined') { - ff.y38_1.e3o_1 = savedX3n; - ff.y38_1.f3o_1 = savedY3n; - ff.y38_1.k3o_1 = savedZ3n; + ff[FSettings][FShadowEnabled] = savedX3n; + ff[FSettings][FPostProcessEnabled] = savedY3n; + ff[FSettings][FExtraPassEnabled] = savedZ3n; } } catch(e) { console.error('[后视] 恢复阴影/后处理失败:', e.message); @@ -443,7 +481,7 @@ if (savedUiQueues) { for (var ui2 = 0; ui2 < uiLayerIndices.length; ui2++) { var li2 = uiLayerIndices[ui2]; - ff.e39_1[li2] = savedUiQueues[li2]; + ff[FLayerQueue][li2] = savedUiQueues[li2]; } } } catch(e) { @@ -452,18 +490,18 @@ // ===== 恢复主相机参数 ===== try { - 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.u36_1 = savedL36; - mainCamera.v36_1 = savedM36; - mainCamera.z36_1 = savedS36; - mainCamera.x36_1 = savedQ36; - mainCamera.y36_1 = savedR36; - mainCamera.o37(); + mainCamera[FAngles][FX] = savedPitch; + mainCamera[FAngles][FY] = savedYaw; + mainCamera[FAngles][FZ] = savedRoll; + mainCamera[FPosition][FX] = savedX; + mainCamera[FPosition][FY] = savedY; + mainCamera[FPosition][FZ] = savedZ; + mainCamera[FViewportWidth] = savedL36; + mainCamera[FViewportHeight] = savedM36; + mainCamera[FFovValue] = savedS36; + mainCamera[FNear] = savedQ36; + mainCamera[FFar] = savedR36; + mainCamera[MCameraUpdate](); } catch(e) { console.error('[后视] 恢复相机失败:', e.message); } @@ -591,9 +629,9 @@ } // 恢复 BS.h3c/g3c - if (origH3c && ff && ff.w39_1) { - ff.w39_1.o3c = origH3c; - ff.w39_1.n3c = origG3c; + if (origH3c && ff && ff[FGl]) { + ff[FGl][MGetDrawingWidth] = origH3c; + ff[FGl][MGetDrawingHeight] = origG3c; console.log('[后视] BS.o3c/n3c 已恢复'); } diff --git a/tools/hornet_schema.js b/tools/hornet_schema.js index f3de0d8..2b41dba 100644 --- a/tools/hornet_schema.js +++ b/tools/hornet_schema.js @@ -27,12 +27,20 @@ // ===== 字段映射总表 ===== // 命名规则:<对象类型>.<语义名> var Fields = { + // ---- 向量(O$)---- + 'Vec.X': 'f20_1', + 'Vec.Y': 'g20_1', + 'Vec.Z': 'h20_1', + // ---- 战斗实体基类(fe / BattleEntity)---- 'Entity.Id': 'iz1_1', // 实体 ID(坦克/战场唯一) + 'Entity.Root': 'qz1_1', // 组件宿主根对象 + 'Entity.ComponentMap': 's2a_1', // 宿主上的组件 Map 'Entity.Components': 'qz1_1.s2a_1', // Map<组件标识, 组件列表>,取值要 valueList.g1(0) // ---- GameMode(ght,window.__gameMode__)---- 'GameMode.Scene': 'lz4()', // 场景实体 + 'GameMode.TankManager': 'm10b_1', // 坦克管理器 'GameMode.TankList': 'm10b_1.j11y()', // 坦克列表(e1() 数量 / g1(i) 取) 'GameMode.OwnedTank': 'u10b_1', // 本地玩家拥有的坦克(onTankPossessed) 'GameMode.ObservedTank': 't10b_1', // 当前观察目标(死亡/观战时可能指向敌人) @@ -52,6 +60,8 @@ // ---- 坦克物理组件(ejt,组件名 tankPhysics)---- 'Physics.Position': 'bzr_1', // 世界位置向量(f20_1/g20_1/h20_1) + 'Physics.PositionOld1': 'gzh_1', // 旧版位置字段名(兜底候选) + 'Physics.PositionOld2': 'pz7_1', // 更旧位置字段名(兜底候选) 'Physics.Size': 'xzq_1', // 车体半尺寸(**不是位置**) 'Physics.Hull': 'zzq_1', // 车体主碰撞形状(x9j_1 === 'hull-main') 'Physics.Body': 'pbk()', // 物理刚体(jM) @@ -101,17 +111,107 @@ // ---- BP 场景节点(zl)---- 'Node.Name': 'g35_1', 'Node.Matrix': 'p35_1', // 世界矩阵(列主序,[12][13][14] = 平移) + 'Node.MatrixOld': 'n35_1', // 旧版矩阵字段(兜底候选) 'Node.Children': 'r35_1', 'Node.Mesh': 'v3m_1', 'Node.Lod': 'u4x_1', // ---- 相机(场景实体 .jz1_1.iyx_1)---- 'Camera.SceneRef': 'jz1_1', + 'Camera.Object': 'iyx_1', // 主相机对象(挂在场景实体的 jz1_1 上) + 'Camera.Viewport': 'w36_1', // 视口(h29() 宽 / g29() 高) 'Camera.Main': 'jz1_1.iyx_1', 'Camera.Position': 'jz1_1.iyx_1.t35_1', 'Camera.ViewMatrix': 'jz1_1.iyx_1.d37_1', 'Camera.ClipMatrix': 'jz1_1.iyx_1.g37_1', - 'Camera.Fov': 'jz1_1.iyx_1.b37_1' + 'Camera.Fov': 'jz1_1.iyx_1.b37_1', + + // ---- 相机:单段叶子字段(FieldName 只认单段,这里给同一批字段补叶子别名)---- + 'Camera.PositionLeaf': 't35_1', + 'Camera.ViewMatrixLeaf': 'd37_1', + 'Camera.ClipMatrixLeaf': 'g37_1', + 'Camera.FovLeaf': 'b37_1', + 'Camera.PositionNode': 'r35_1', // 旧版相机位置字段(与 Node.Children 同名但不通用) + 'Camera.Angles': 'u35_1', // 欧拉角(pitch/roll/yaw) + 'Camera.Euler': 'u35_1', // 同上(自由视角叫法) + 'Camera.Rotation': 'u35_1', // 同上(沙盘叫法) + 'Camera.Update': 'o37()', // 相机矩阵刷新 + 'Camera.ViewportWidth': 'u36_1', + 'Camera.ViewportHeight': 'v36_1', + 'Camera.RenderWidth': 'u36_1', // 别名 + 'Camera.RenderHeight': 'v36_1', // 别名 + 'Camera.FovValue': 'z36_1', + 'Camera.Near': 'x36_1', + 'Camera.Far': 'y36_1', + 'Camera.SceneFx': 'd75_1', + + // ---- 渲染器 / 绘制流程(module_92980)---- + 'Render.Renderer': 'gyw_1', // 场景渲染器 + 'Render.Drawer': 'd75_1', // Ff 绘制器 + 'Render.DrawBuffer': 'd75_1', // 别名(透视里按"绘制缓冲"称呼) + 'Renderer.Object': 'd75_1', // 别名(沙盘里按"渲染器对象"称呼) + 'Render.Gl': 'w39_1', // GL 包装器 + 'Renderer.Device': 'w39_1', // 别名 + 'Render.GlNative': 'b74_1', // 原生 WebGL2RenderingContext + 'Renderer.Gl': 'b74_1', // 别名 + 'Render.Device': 's3h_1', // 资源侧设备包装 + 'Render.DeviceNative': 'f75_1', // 设备里的原生 GL + 'Render.Settings': 'y38_1', // 渲染开关组 + 'Renderer.Flags': 'y38_1', // 别名 + 'Render.ShadowEnabled': 'e3o_1', + 'Renderer.Shadow': 'e3o_1', // 别名 + 'Render.PostProcessEnabled': 'f3o_1', + 'Renderer.PostFx': 'f3o_1', // 别名 + 'Render.ExtraPassEnabled': 'k3o_1', + 'Renderer.FlagAux': 'k3o_1', // 别名 + 'Render.LayerQueue': 'e39_1', // 渲染层队列(20=Hud 21-24=GUI) + 'Renderer.QueueList': 'e39_1', // 别名 + 'Render.Draw': 'f3t()', // y3s → f3t 主渲染 + 'Render.DrawingWidth': 'o3c()', + 'Render.DrawingHeight': 'n3c()', + 'Scene.RootNode': 'fyx_1', // 场景根节点 + + // ---- 网格资源(Tp / sm)---- + 'Mesh.VertexGroups': 'x4w_1', + 'Mesh.VertexBuffer': 'h3x_1', + 'Mesh.VertexCount': 'w3x_1', + 'Mesh.CpuVertexData': 'x3x_1', + 'Mesh.GpuBuffer': 'v3x_1', + 'Mesh.IndexGroups': 'y4w_1', + 'Mesh.IndexBuffer': 'q4w_1', + 'Mesh.IndexBufferOld': 'u3p_1', + 'Mesh.CpuIndexData': 'p3x_1', + 'Mesh.IndexGpuBuffer': 'n3x_1', + + // ---- 射线检测(碰撞世界的射线接口)---- + 'CollisionWorld.Raycast': 'a17p()', + 'Ray.Cast': 'a17p()', // 别名 + 'Ray.StartPoint': 'y178_1', + 'Ray.HitPoint': 'y178_1', // 别名 + 'Ray.EndPoint': 'z178_1', + 'Ray.HitNormal': 'z178_1', // 别名 + 'Ray.Distance': 'a179_1', + 'Ray.HitEntity': 'q17o_1', + 'Ray.HitShape': 'q17o_1', // 别名(雷达/瞄准把第 4 参数叫 HitShape) + 'Ray.HitBody': 'b179_1', // 第 5 参数 + 'RayFilter.Context': 'l1da_1', + 'RayFilter.Query': 'l1da_1', // 别名 + + // ---- 车体朝向 / 武器(雷达、瞄准警告)---- + 'Tank.HullComponent': 'qzv_1', // 车体朝向组件识别字段 + 'Tank.HullDirection': 'wzl_1', // 车体朝向向量 + 'Tank.StateId': 'k3_1', // 状态枚举数值(与 Team.Id 同名字段,按用途分开) + 'Weapon.Component': 'n11c_1', + 'Weapon.ComponentAlt': 'q112_1', + 'Weapon.TurretRoot': 'm17a_1', + + // ---- 炮塔 / 皮肤槽位 ---- + 'Turret.InnerWrapper': 'e11p_1', + 'Turret.RootNode': 'a17q_1', + 'Skin.SlotHull': 'w14w_1', + 'Skin.SlotHullLegacy': 't14l_1', + 'Skin.SlotHullAlt': 'w14x_1', + 'Skin.SlotHullOld': 'p14l_1' }; // ===== module_87072 的导出键($_.xx),每版都可能平移 ===== @@ -186,6 +286,21 @@ return Out; } + // 自检项(只查核心字段:别名/扩展项不在样本对象上,查了会误报) + var CoreKeys = { + Entity: ['Entity.Id', 'Entity.Root', 'Entity.ComponentMap', 'Entity.Components'], + GameMode: ['GameMode.Scene', 'GameMode.TankManager', 'GameMode.TankList', + 'GameMode.OwnedTank', 'GameMode.ObservedTank', 'GameMode.LocalTeam'], + Tank: ['Tank.Fsm', 'Tank.State', 'Tank.StateValue', 'Tank.IsAlive', 'Tank.TeamSource'], + Physics: ['Physics.Position', 'Physics.Size', 'Physics.Hull', 'Physics.Body', 'Physics.BodyShapes'], + Shape: ['Shape.Name', 'Shape.Category', 'Shape.Mask', 'Shape.Body', 'Body.Owner'], + Skin: ['Skin.HullSkin', 'Skin.HullRoot', 'Skin.Parts', 'Skin.PartNode'], + Turret: ['Turret.Wrapper', 'Turret.GetRoot'], + Node: ['Node.Name', 'Node.Matrix', 'Node.Children', 'Node.Mesh'], + Camera: ['Camera.SceneRef', 'Camera.Main', 'Camera.Position', 'Camera.ViewMatrix', + 'Camera.ClipMatrix', 'Camera.Fov'] + }; + // 用当前战局对象做一次全量自检(控制台调用 __hornetSchema__.SelfCheck()) function SelfCheck() { var Gm = window.__gameMode__; @@ -200,29 +315,36 @@ if (!Tank && TankList && typeof TankList.e1 === 'function' && TankList.e1() > 0) Tank = TankList.g1(0); } catch (e) {} + var Physics = Tank ? FindPhysics(Tank) : null; var Targets = { Entity: Tank || Scene, GameMode: Gm, Tank: Tank, - Physics: Tank ? FindPhysics(Tank) : null, - Shape: null, + Physics: Physics, + Shape: Physics ? Get(Physics, 'Physics.Hull') : null, Skin: Tank, Turret: Tank, - Node: null, + Node: Tank ? Get(Tank, 'Skin.HullRoot') : null, Camera: Scene }; - Object.keys(Targets).forEach(function (Prefix) { - if (!Targets[Prefix]) { Report.Groups[Prefix] = '无样本对象'; return; } - var R = Check(Targets[Prefix], Prefix); - Report.Groups[Prefix] = R; - R.Missing.forEach(function (M) { Report.Missing.push(Prefix + '.' + M); }); + Object.keys(CoreKeys).forEach(function (Prefix) { + var Target = Targets[Prefix]; + if (!Target) { Report.Groups[Prefix] = '无样本对象'; return; } + var Ok = [], Missing = []; + CoreKeys[Prefix].forEach(function (Key) { + var V = Get(Target, Key); + if (V === undefined) Missing.push(Key + ' → ' + PathOf(Key)); + else Ok.push(Key); + }); + Report.Groups[Prefix] = { Ok: Ok, Missing: Missing }; + Missing.forEach(function (M) { Report.Missing.push(Prefix + '.' + M); }); }); if (Report.Missing.length) { console.warn('[字段抽象] 失配 ' + Report.Missing.length + ' 项:', Report.Missing); } else { - console.log('[字段抽象] 全部语义字段解析正常'); + console.log('[字段抽象] 全部核心字段解析正常'); } return Report; } diff --git a/tools/hornet_warsandbox.js b/tools/hornet_warsandbox.js index bec9b6b..e0d0a23 100644 --- a/tools/hornet_warsandbox.js +++ b/tools/hornet_warsandbox.js @@ -5,6 +5,50 @@ // 操作:拖拽标题移动窗口 | 拖拽地图平移 | 滚轮缩放 | 双击居中 | Alt+M 开关 (function() { + // ===== 字段抽象层(语义名 → 匿名字段,总表在 hornet_schema.js)===== + var Schema = window.__hornetSchema__ || null; + function Fld(Key, Fallback) { var N = Schema ? Schema.FieldName(Key) : null; return N || Fallback; } + function Mth(Key, Fallback) { var N = Schema ? Schema.MethodName(Key) : null; return N || Fallback; } + + // 向量 + var FVecX = Fld('Vec.X', 'f20_1'); + var FVecY = Fld('Vec.Y', 'g20_1'); + var FVecZ = Fld('Vec.Z', 'h20_1'); + + // 战局 / 场景 + var MScene = Mth('GameMode.Scene', 'lz4'); + var FCameraSceneRef = Fld('Camera.SceneRef', 'jz1_1'); + var FTankManager = Fld('GameMode.TankManager', 'm10b_1'); + var MTankList = Mth('GameMode.TankList', 'j11y'); + + // 渲染器 + var FRenderer = Fld('Renderer.Object', 'd75_1'); + var FRenderDevice = Fld('Renderer.Device', 'w39_1'); + var FRenderGl = Fld('Renderer.Gl', 'b74_1'); + var FRenderFlags = Fld('Renderer.Flags', 'y38_1'); + var FRenderShadow = Fld('Renderer.Shadow', 'e3o_1'); + var FRenderPostFx = Fld('Renderer.PostFx', 'f3o_1'); + var FRenderFlagAux = Fld('Renderer.FlagAux', 'k3o_1'); + var FRenderQueueList = Fld('Renderer.QueueList', 'e39_1'); + + // 相机 + var FCameraRotation = Fld('Camera.Rotation', 'u35_1'); + var FNodeChildren = Fld('Node.Children', 'r35_1'); // 本文件里该字段是相机位置向量(与节点子列表同名复用) + var FCameraRenderWidth = Fld('Camera.RenderWidth', 'u36_1'); + var FCameraRenderHeight = Fld('Camera.RenderHeight', 'v36_1'); + var FCameraFov = Fld('Camera.Fov', 'z36_1'); + var FCameraNear = Fld('Camera.Near', 'x36_1'); + var FCameraFar = Fld('Camera.Far', 'y36_1'); + + // 皮肤 / 节点 + var FSkinSlotHull = Fld('Skin.SlotHull', 'w14w_1'); + var FSkinSlotHullLegacy = Fld('Skin.SlotHullLegacy', 't14l_1'); + var FSkinSlotHullAlt = Fld('Skin.SlotHullAlt', 'w14x_1'); + var FSkinSlotHullOld = Fld('Skin.SlotHullOld', 'p14l_1'); + var FSkinParts = Fld('Skin.Parts', 'g17r_1'); + var FSkinPartNode = Fld('Skin.PartNode', 'j17r_1'); + var FNodeMatrixOld = Fld('Node.MatrixOld', 'n35_1'); + function _start() { console.log('===== 3DTank 战争沙盘(3D俯瞰版)启动 ====='); @@ -37,27 +81,27 @@ // 从车体皮肤组件(KMt)的Bp节点世界矩阵提取前向方向 // dzc_1 是局部空间向量且可能未设置,不可靠 // 皮肤字段:新 build 是 w14w_1;旧 build 是 t14l_1(新版 t14l_1 已改成悬停车体状态 Qmt) - var hullComp = FindComponentByField(tank, 'w14w_1') || - FindComponentByField(tank, 't14l_1') || - FindComponentByField(tank, 'w14x_1') || - FindComponentByField(tank, 'p14l_1'); + var hullComp = FindComponentByField(tank, FSkinSlotHull) || + FindComponentByField(tank, FSkinSlotHullLegacy) || + FindComponentByField(tank, FSkinSlotHullAlt) || + FindComponentByField(tank, FSkinSlotHullOld); if (!hullComp) return 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; + var skin = (hullComp[FSkinSlotHull] && hullComp[FSkinSlotHull][FSkinParts]) ? hullComp[FSkinSlotHull] : + (hullComp[FSkinSlotHullLegacy] && hullComp[FSkinSlotHullLegacy][FSkinParts]) ? hullComp[FSkinSlotHullLegacy] : + (hullComp[FSkinSlotHullOld] && hullComp[FSkinSlotHullOld][FSkinParts]) ? hullComp[FSkinSlotHullOld] : null; if (!skin) return null; try { - var partCount = skin.g17r_1.e1(); + var partCount = skin[FSkinParts].e1(); for (var p = 0; p < partCount; p++) { - var part = skin.g17r_1.g1(p); - if (!part || !part.j17r_1) continue; - var nodeCount = part.j17r_1.e1(); + var part = skin[FSkinParts].g1(p); + if (!part || !part[FSkinPartNode]) continue; + var nodeCount = part[FSkinPartNode].e1(); for (var n = 0; n < nodeCount; n++) { - var bpNode = part.j17r_1.g1(n); - if (!bpNode || !bpNode.n35_1) continue; + var bpNode = part[FSkinPartNode].g1(n); + if (!bpNode || !bpNode[FNodeMatrixOld]) continue; // 不按 g35_1 过滤:它是引擎的细节/淡出权重(0~1),不是幽灵标志, // 过滤会漏掉低细节节点、导致取不到朝向 - var m = bpNode.n35_1; + var m = bpNode[FNodeMatrixOld]; // 列主序世界矩阵:Col0=(m[0],m[1],m[2]) Col1=(m[4],m[5],m[6]) Col2=(m[8],m[9],m[10]) // 游戏坐标:x,y 水平面; z(h20_1)=高度。找"上"列(|z|最大),其余为水平方向 var az0 = Math.abs(m[2]), az1 = Math.abs(m[6]), az2 = Math.abs(m[10]); @@ -87,11 +131,11 @@ var scene = null, ff = null, rootNode = null, mainCamera = null, gl = null; var initError = null; try { - scene = gm.lz4().jz1_1; - ff = scene.gyw_1.d75_1; + scene = gm[MScene]()[FCameraSceneRef]; + ff = scene.gyw_1[FRenderer]; rootNode = scene.fyx_1; mainCamera = scene.iyx_1; - gl = ff.w39_1.b74_1; + gl = ff[FRenderDevice][FRenderGl]; if (!ff || !rootNode || !mainCamera || !gl) throw new Error('关键字段缺失'); } catch(e) { initError = e; @@ -278,10 +322,10 @@ } return origBindFramebuffer(target, fbo); }; - var origH3c = ff.w39_1.o3c.bind(ff.w39_1); - var origG3c = ff.w39_1.n3c.bind(ff.w39_1); - ff.w39_1.o3c = function() { return sandboxRendering ? FBO_W : origH3c(); }; - ff.w39_1.n3c = function() { return sandboxRendering ? FBO_H : origG3c(); }; + var origH3c = ff[FRenderDevice].o3c.bind(ff[FRenderDevice]); + var origG3c = ff[FRenderDevice].n3c.bind(ff[FRenderDevice]); + ff[FRenderDevice].o3c = function() { return sandboxRendering ? FBO_W : origH3c(); }; + ff[FRenderDevice].n3c = function() { return sandboxRendering ? FBO_H : origG3c(); }; // UI 层禁用 var uiLayerIndices = [20, 21, 22, 23, 24]; @@ -481,7 +525,7 @@ // 坦克标记 var tanks = []; try { - var list = gm.m10b_1.j11y(); + var list = gm[FTankManager][MTankList](); var n = list.e1(); for (var i = 0; i < n; i++) { var t = list.g1(i); if (t) tanks.push(t); } } catch(e) {} @@ -661,44 +705,44 @@ try { // 保存主相机参数 - 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.u36_1, sM36 = mainCamera.v36_1; - var sS36 = mainCamera.z36_1, sQ36 = mainCamera.x36_1, sR36 = mainCamera.y36_1; + var sPitch = mainCamera[FCameraRotation][FVecX]; + var sRoll = mainCamera[FCameraRotation][FVecY]; + var sYaw = mainCamera[FCameraRotation][FVecZ]; + var sX = mainCamera[FNodeChildren][FVecX]; + var sY = mainCamera[FNodeChildren][FVecY]; + var sZ = mainCamera[FNodeChildren][FVecZ]; + var sL36 = mainCamera[FCameraRenderWidth], sM36 = mainCamera[FCameraRenderHeight]; + var sS36 = mainCamera[FCameraFov], sQ36 = mainCamera[FCameraNear], sR36 = mainCamera[FCameraFar]; // 设置俯瞰相机参数 // pitch=-π/2 朝下看,roll=0 不翻转,yaw=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.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[FCameraRotation][FVecX] = -Math.PI / 2; + mainCamera[FCameraRotation][FVecY] = 0; + mainCamera[FCameraRotation][FVecZ] = 0; + mainCamera[FNodeChildren][FVecX] = viewCenterX; + mainCamera[FNodeChildren][FVecY] = viewCenterY; + mainCamera[FNodeChildren][FVecZ] = camHeight; + mainCamera[FCameraRenderWidth] = FBO_W; + mainCamera[FCameraRenderHeight] = FBO_H; + mainCamera[FCameraFov] = Config.fov; + mainCamera[FCameraNear] = Config.near; + mainCamera[FCameraFar] = Config.far; mainCamera.o37(); // 禁用阴影/后处理 - 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; + var savedX3n = ff[FRenderFlags][FRenderShadow]; + var savedY3n = ff[FRenderFlags][FRenderPostFx]; + var savedZ3n = ff[FRenderFlags][FRenderFlagAux]; + ff[FRenderFlags][FRenderShadow] = false; + ff[FRenderFlags][FRenderPostFx] = false; + ff[FRenderFlags][FRenderFlagAux] = false; // 禁用 UI 层 var savedUiQueues = {}; for (var ui = 0; ui < uiLayerIndices.length; ui++) { var li = uiLayerIndices[ui]; - savedUiQueues[li] = ff.e39_1[li]; - ff.e39_1[li] = fakeQueue; + savedUiQueues[li] = ff[FRenderQueueList][li]; + ff[FRenderQueueList][li] = fakeQueue; } // 渲染到 FBO @@ -744,22 +788,22 @@ origBindFramebuffer(gl.FRAMEBUFFER, mainFbo); // 恢复渲染状态 - ff.y38_1.e3o_1 = savedX3n; - ff.y38_1.f3o_1 = savedY3n; - ff.y38_1.k3o_1 = savedZ3n; + ff[FRenderFlags][FRenderShadow] = savedX3n; + ff[FRenderFlags][FRenderPostFx] = savedY3n; + ff[FRenderFlags][FRenderFlagAux] = savedZ3n; for (var ui2 = 0; ui2 < uiLayerIndices.length; ui2++) { - ff.e39_1[uiLayerIndices[ui2]] = savedUiQueues[uiLayerIndices[ui2]]; + ff[FRenderQueueList][uiLayerIndices[ui2]] = savedUiQueues[uiLayerIndices[ui2]]; } // 恢复主相机 - 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.u36_1 = sL36; mainCamera.v36_1 = sM36; - mainCamera.z36_1 = sS36; mainCamera.x36_1 = sQ36; mainCamera.y36_1 = sR36; + mainCamera[FCameraRotation][FVecX] = sPitch; + mainCamera[FCameraRotation][FVecY] = sRoll; + mainCamera[FCameraRotation][FVecZ] = sYaw; + mainCamera[FNodeChildren][FVecX] = sX; + mainCamera[FNodeChildren][FVecY] = sY; + mainCamera[FNodeChildren][FVecZ] = sZ; + mainCamera[FCameraRenderWidth] = sL36; mainCamera[FCameraRenderHeight] = sM36; + mainCamera[FCameraFov] = sS36; mainCamera[FCameraNear] = sQ36; mainCamera[FCameraFar] = sR36; mainCamera.o37(); // 绘制 2D 叠加层(坦克标记/网格/罗盘) @@ -795,8 +839,8 @@ if (rafId) cancelAnimationFrame(rafId); // 恢复 hook gl.bindFramebuffer = origBindFramebuffer; - ff.w39_1.o3c = origH3c; - ff.w39_1.n3c = origG3c; + ff[FRenderDevice].o3c = origH3c; + ff[FRenderDevice].n3c = origG3c; window.removeEventListener('resize', resizeCanvas); if (container.parentNode) container.parentNode.removeChild(container); window.__hornetWarSandbox__ = null;