type 4 是克隆包装(hu.c9w_1 = 原形状.d21()),地图三角形都以它出现,之前按三角形判定永远不命中。现在 IsTriangle/TriangleNormalZ/ShapeMaxZ/ShapePoint 统一先解包 c9w_1,再套「高于车体 1m + |法线z|<0.6」规则;schema 补 Shape.Clone。
393 lines
17 KiB
JavaScript
393 lines
17 KiB
JavaScript
// hornet_schema.js — 引擎匿名字段抽象层(字段映射总表)
|
||
//
|
||
// 为什么需要它:
|
||
// 游戏每个构建版本都会把类的字段名整体平移(如 ejt 的 zzq_1 / bzr_1),
|
||
// 工具里到处硬编码 `comp.zzq_1`、`tank.qz1_1.s2a_1` 这类匿名名,
|
||
// 版本一换就要全局搜替换,还容易改错(同名复用极严重)。
|
||
// 这里把「语义名 → 匿名字段路径」集中成一张表:
|
||
// · 工具只用语义名(Schema.Get(tank, 'Tank.StateValue'))
|
||
// · 换版本时只改这张表,并跑一次 SelfCheck() 看哪几项失配
|
||
//
|
||
// 路径语法:
|
||
// 'a.b.c' 属性链
|
||
// 'lz4()' 方法调用(末尾带括号即调用,中间也可混用,如 'm10b_1.j11y()')
|
||
//
|
||
// 用法:
|
||
// var S = window.__hornetSchema__;
|
||
// S.Get(gm, 'GameMode.TankList')
|
||
// S.Get(tank, 'Tank.StateValue')
|
||
// S.Check(gm, 'GameMode') // → { Ok: [...], Missing: [...] }
|
||
// S.SelfCheck() // 用当前战局对象自检并打印
|
||
//
|
||
// 版本:main.7cfa4058(字段核对自 module_87072 / 61378 / 92980 / 55088)
|
||
|
||
(function () {
|
||
'use strict';
|
||
|
||
// ===== 字段映射总表 =====
|
||
// 命名规则:<对象类型>.<语义名>
|
||
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', // 当前观察目标(死亡/观战时可能指向敌人)
|
||
'GameMode.LocalTeam': 'v10b_1', // 本地队伍(与 u10b_1 同生命周期)
|
||
|
||
// ---- 坦克组件(lxt,含状态机)----
|
||
'Tank.Fsm': 'p10c_1', // 状态机字段(组件识别用)
|
||
'Tank.State': 'k10c_1', // 状态枚举对象
|
||
'Tank.StateValue': 'k10c_1.k3_1', // 状态数值:0=DEAD 1=DEAD_PHANTOM 2=SEMI_ACTIVE 3=ACTIVE 4=DISABLED 5=TELEPORT
|
||
'Tank.IsAlive': 's14j()', // 引擎可见性判定(2/3/5)
|
||
'Tank.TeamSource': 'l10c_1', // 状态机里下发的队伍对象
|
||
|
||
// ---- 队伍组件(GIt)与队伍对象 ----
|
||
'Team.Field': 'b135_1', // 队伍组件上保存队伍对象的字段
|
||
'Team.Id': 'k3_1', // 队伍编号:0 / 1 / 2(2 = 中立)
|
||
'Team.NeutralId': 2,
|
||
|
||
// ---- 坦克物理组件(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)
|
||
'Physics.AddMask': 'n155()', // z9j_1 |= t
|
||
'Physics.RemoveMask': 'm155()', // z9j_1 &= ~t
|
||
'Physics.BodyShapes': 'pbk().y9i_1', // 刚体上的形状列表(e1()/g1(i))
|
||
|
||
// ---- 碰撞形状(yi)----
|
||
'Shape.Name': 'x9j_1',
|
||
'Shape.Category': 'y9j_1', // bit0=坦克(1) bit3=地雷(8) bit4=地形(16)
|
||
'Shape.Mask': 'z9j_1', // 愿意碰撞的分类位
|
||
'Shape.Ghost': 'g9k_1', // true = 只探测不产生碰撞响应
|
||
'Shape.Body': 'b9k_1', // 形状 → 所属刚体(Fn)
|
||
'Shape.Type': 'v9j_1', // 形状类型位组(1/2/4/8/16/32;8 = 三角形,4 = 克隆包装)
|
||
'Shape.Clone': 'c9w_1', // 类型 4 的克隆包装:真实几何在 c9w_1 里
|
||
'Shape.Vertex0': 'u9x_1', // 三角形顶点 0
|
||
'Shape.Vertex1': 'v9x_1', // 三角形顶点 1
|
||
'Shape.Vertex2': 'w9x_1', // 三角形顶点 2
|
||
'Body.Owner': 'x9i_1', // 刚体 → 拥有者(坦克物理组件 ejt)
|
||
'Body.State': 'f9j_1', // 刚体当前物理态
|
||
'Body.PositionVec': 's9j_1', // 刚体位置向量(f9j_1.s9j_1)
|
||
|
||
// ---- 碰撞世界(module_55088 的 ki)----
|
||
'CollisionWorld.ShouldCollide': 'q9q()', // 唯一配对过滤:mask + 形状类型 → 生成接触
|
||
'CollisionWorld.Overlap': 'x9o()', // 形状相交查询(真形状检测,不是包围盒)
|
||
|
||
// ---- 实体事件总线 / 外力事件(ODt)----
|
||
'Entity.Dispatch': 'kz7()', // 向实体内所有订阅者派发事件
|
||
'Event.PushPoint': 'o13i_1', // 外力事件:作用点(O$ 向量)
|
||
'Event.PushDir': 'p13i_1', // 外力事件:方向(O$ 向量)
|
||
'Event.PushStrength': 'q13i_1', // 外力事件:强度(坦克物理交给 m9k)
|
||
|
||
// ---- 血量组件(Vkt)----
|
||
'Health.Max': 'p12o_1',
|
||
'Health.Current': 'q12o_1',
|
||
|
||
// ---- 用户信息组件(yot)----
|
||
'User.Name': 'c12n_1',
|
||
'User.Clan': 'e12n_1',
|
||
|
||
// ---- 装备分组件(BattleGearScoreCC)----
|
||
'Gear.Score': 'cjf_1',
|
||
|
||
// ---- 皮肤组件(eUt)----
|
||
'Skin.HullSkin': 'g159_1', // 皮肤对象
|
||
'Skin.HullRoot': 'h159_1', // 车身渲染树根(BP 节点)
|
||
'Skin.Parts': 'g159_1.g17r_1', // 部件表
|
||
'Skin.PartNode': 'j17r_1', // 部件 → BP 节点
|
||
|
||
// ---- 炮塔皮肤组件(LUt)----
|
||
'Turret.Wrapper': 'a11p_1', // 组件首字段(包装对象)
|
||
'Turret.Root': 'e11p_1.a17q_1', // 炮塔 BP 根节点
|
||
'Turret.GetRoot': 'u11p()',
|
||
|
||
// ---- 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',
|
||
|
||
// ---- 相机:单段叶子字段(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),每版都可能平移 =====
|
||
// 队伍:vd = WIt(t) 取实体队伍;dl = R7(a,b) 同队且非中立
|
||
// ⚠ 旧版是 kd / rk;新版 kd 已指向无关构造器
|
||
var Exports = {
|
||
'Export.TeamOf': ['vd', 'kd'],
|
||
'Export.TeamSame': ['dl', 'rk'],
|
||
'Export.PhysicsWorld': ['h'] // module_55088.$_$.h = 碰撞世界类 ki
|
||
};
|
||
|
||
// ===== 路径解析 =====
|
||
function IsCall(Token) {
|
||
var N = Token.length;
|
||
return N > 2 && Token.charAt(N - 1) === ')' && Token.charAt(N - 2) === '(';
|
||
}
|
||
|
||
function ResolvePath(Target, Path) {
|
||
if (Target === null || Target === undefined || !Path) return undefined;
|
||
var Tokens = String(Path).split('.');
|
||
var Cur = Target;
|
||
for (var i = 0; i < Tokens.length; i++) {
|
||
if (Cur === null || Cur === undefined) return undefined;
|
||
var Tok = Tokens[i];
|
||
if (IsCall(Tok)) {
|
||
var Name = Tok.slice(0, -2);
|
||
if (typeof Cur[Name] !== 'function') return undefined;
|
||
try { Cur = Cur[Name](); } catch (e) { return undefined; }
|
||
} else {
|
||
Cur = Cur[Tok];
|
||
}
|
||
}
|
||
return Cur;
|
||
}
|
||
|
||
// 取字段值:Key 是总表键(如 'Tank.StateValue'),未命中则把 Key 当字面路径
|
||
function Get(Target, Key) {
|
||
var Path = Fields[Key];
|
||
return ResolvePath(Target, Path !== undefined ? Path : Key);
|
||
}
|
||
|
||
// 取字段路径(字符串),用于打印/自检
|
||
function PathOf(Key) {
|
||
return Fields[Key] !== undefined ? Fields[Key] : String(Key);
|
||
}
|
||
|
||
// 取单段属性名:'Node.Matrix' → 'p35_1';带方法或多段时返回 null
|
||
// 工具里写成 `Node[FieldName('Node.Matrix') || 'p35_1']`,即「优先用总表,缺表时用字面名」
|
||
function FieldName(Key) {
|
||
var P = Fields[Key];
|
||
if (!P || P.indexOf('.') >= 0 || IsCall(P)) return null;
|
||
return P;
|
||
}
|
||
|
||
// 取单段方法名:'Physics.RemoveMask' → 'm155'
|
||
function MethodName(Key) {
|
||
var P = Fields[Key];
|
||
if (!P || !IsCall(P) || P.indexOf('.') >= 0) return null;
|
||
return P.slice(0, -2);
|
||
}
|
||
|
||
// 自检:按前缀检查某对象上哪些语义字段能解析
|
||
function Check(Target, Prefix) {
|
||
var Out = { Ok: [], Missing: [] };
|
||
Object.keys(Fields).forEach(function (Key) {
|
||
if (Prefix && Key.indexOf(Prefix + '.') !== 0) return;
|
||
if (typeof Fields[Key] !== 'string') return; // 常量项(如 Team.NeutralId)不参与自检
|
||
var V = Get(Target, Key);
|
||
if (V === undefined) Out.Missing.push(Key + ' → ' + Fields[Key]);
|
||
else Out.Ok.push(Key + ' → ' + Fields[Key]);
|
||
});
|
||
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__;
|
||
var Report = { HasGameMode: !!Gm, Groups: {}, Missing: [] };
|
||
if (!Gm) return Report;
|
||
|
||
var Scene = Get(Gm, 'GameMode.Scene');
|
||
var TankList = Get(Gm, 'GameMode.TankList');
|
||
var Tank = null;
|
||
try {
|
||
Tank = Get(Gm, 'GameMode.OwnedTank');
|
||
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: Physics,
|
||
Shape: Physics ? Get(Physics, 'Physics.Hull') : null,
|
||
Skin: Tank,
|
||
Turret: Tank,
|
||
Node: Tank ? Get(Tank, 'Skin.HullRoot') : null,
|
||
Camera: Scene
|
||
};
|
||
|
||
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('[字段抽象] 全部核心字段解析正常');
|
||
}
|
||
return Report;
|
||
}
|
||
|
||
// 找坦克物理组件(与工具内实现同源,放这里方便自检)
|
||
function FindPhysics(Tank) {
|
||
if (!Tank) return null;
|
||
var Map = Get(Tank, 'Entity.Components');
|
||
if (!Map || typeof Map.forEach !== 'function') return null;
|
||
var Found = null;
|
||
Map.forEach(function (ValueList) {
|
||
if (Found) return;
|
||
var Comp = (ValueList && typeof ValueList.g1 === 'function') ? ValueList.g1(0) : ValueList;
|
||
if (!Comp) return;
|
||
var Hull = Get(Comp, 'Physics.Hull');
|
||
if (Hull && Get(Hull, 'Shape.Name') === 'hull-main') Found = Comp;
|
||
});
|
||
return Found;
|
||
}
|
||
|
||
// ===== 导出 =====
|
||
window.__hornetSchema__ = {
|
||
Version: 'main.7cfa4058',
|
||
Fields: Fields,
|
||
Exports: Exports,
|
||
Get: Get,
|
||
PathOf: PathOf,
|
||
FieldName: FieldName,
|
||
MethodName: MethodName,
|
||
Check: Check,
|
||
SelfCheck: SelfCheck,
|
||
ResolvePath: ResolvePath,
|
||
FindPhysics: FindPhysics
|
||
};
|
||
|
||
console.log('[3DTank] 字段抽象层已加载(版本 main.7cfa4058,语义字段 ' + Object.keys(Fields).length + ' 项)');
|
||
})();
|