Files
TankIC/tools/hornet_lib.js
T
TermiNexus 90c377ad79 feat(3dtank): 本地镜像与 hornet 工具链迁到新版 bundle main.6469b966.js
旧 main.b7114b43.js 已被新构建替换(10,011,039 字节 / 1335 模块),本地镜像整体重切到新版并清掉旧产物;hornet 工具链按 tools/map_symbols.js 生成的新旧符号映射改名后重新打包(3dtank_bundle.js 与 unpacked/hornet_bundle.js 同步)。tools/symbol_map.json(8.9MB 映射产物)与切割解包目录不进库,可用 map_symbols.js / split.js 重新生成。
2026-09-17 19:59:40 +08:00

416 lines
16 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ===== 3DTank公共库 - 工具文件共享辅助函数 =====
// 在浏览器控制台运行(需先运行 hook_gamemode.js)
// 功能:提供坦克查询、队伍判断、相机信息、3D投影等公共函数
// 用法:各工具文件通过 var lib = window.__hornetLib__ 访问
(function() {
console.log('===== 3DTank公共库加载 =====');
if (!window.__gameMode__) {
console.warn('[公共库] window.__gameMode__ 暂未捕获,工具启动时将失败。hook_gamemode.js 已安装 Hook,战斗中会自动捕获');
}
// ===== 缓存队伍判断函数(ZCt/m2Fn) =====
var ZCt = null, m2Fn = null;
// 加载队伍判断函数(从 module_87072 导出)
// ZCt (mod.$_$.kd): 从坦克对象获取队伍ID
// m2 (mod.$_$.rk): 判断两个队伍ID是否相同且非中立 = t.equals(n) && !t.equals(E$())
function LoadTeamFunctions() {
if (ZCt && m2Fn) return { ZCt: ZCt, m2Fn: m2Fn };
try {
var mod87072 = window.__wp_require__(87072);
if (mod87072 && mod87072.$_$) {
ZCt = mod87072.$_$.kd;
m2Fn = mod87072.$_$.rk;
console.log('[公共库] 队伍判断函数已加载: ZCt=' + (typeof ZCt) + ', m2=' + (typeof m2Fn));
}
} catch(e) {
console.warn('[公共库] 加载队伍判断函数失败:', e.message);
}
return { ZCt: ZCt, m2Fn: m2Fn };
}
// ===== 从 entity 获取包含指定字段的组件 =====
// 遍历 entity.wyj_1.r2a_1(Map<组件标识, 组件列表>),value 是列表类(s_),g1(0) 获取实际组件
// 注意:用 !== undefined 检查字段存在性,避免 falsy 值(0/""/null/false)漏判
function FindComponentByField(entity, fieldName) {
if (!entity || !entity.pyt_1 || !entity.pyt_1.s2a_1) return null;
var result = null;
entity.pyt_1.s2a_1.forEach(function(valueList, key) {
if (result) return;
try {
var comp = (valueList && typeof valueList.g1 === 'function') ? valueList.g1(0) : valueList;
if (comp && comp[fieldName] !== undefined) result = comp;
} catch(e) {}
});
return result;
}
// ===== 获取坦克位置(G$t 物理组件的 pz7_1 = O$向量) =====
// 返回 {x, y, z},x/y 水平面,z 高度
function GetTankPosition(tank) {
var physicsComp = FindComponentByField(tank, 'gzh_1');
if (physicsComp && physicsComp.gzh_1) {
return {
x: physicsComp.gzh_1.f20_1,
y: physicsComp.gzh_1.g20_1,
z: physicsComp.gzh_1.h20_1
};
}
return null;
}
// ===== 获取坦克状态值(Yjt 组件的 vzr_1.k3_1) =====
// 状态枚举:0=DEAD, 1=DEAD_PHANTOM, 2=SEMI_ACTIVE, 3=ACTIVE, 4=DISABLED
// Yjt 特征:有 azs_1(状态机)和 vzr_1(状态枚举)字段
function GetTankState(tank) {
if (!tank || !tank.pyt_1 || !tank.pyt_1.s2a_1) return -1;
var tankComp = null;
tank.pyt_1.s2a_1.forEach(function(valueList, key) {
if (tankComp) return;
try {
var comp = (valueList && typeof valueList.g1 === 'function') ? valueList.g1(0) : valueList;
if (comp && comp.t101_1 && comp.o101_1 !== undefined) tankComp = comp;
} catch(e) {}
});
if (tankComp && tankComp.o101_1) return tankComp.o101_1.k3_1;
return -1;
}
// 只有 ACTIVE(3) 才是战斗中的坦克
function IsTankAlive(tank) { return GetTankState(tank) === 3; }
// ===== 获取坦克名称(yot 用户信息组件) =====
// s120_1 = userName, u120_1 = clanTag(可为null), t120_1 = userRank
function GetTankName(tank) {
var userComp = FindComponentByField(tank, 'k12a_1');
if (!userComp) return null;
var name = userComp.k12a_1 || '';
var clanTag = userComp.m12a_1 || '';
return clanTag ? (clanTag + ' ' + name) : name;
}
// ===== 获取坦克血量(Vkt 血量组件) =====
// f122_1 = 最大血量(max), g122_1 = 当前血量(current, 死亡时=0)
function GetTankHealth(tank) {
var healthComp = FindComponentByField(tank, 'y12b_1');
if (!healthComp) return null;
var max = healthComp.x12b_1 || 0;
var cur = healthComp.y12b_1 || 0;
if (max <= 0) return null;
return { current: cur, max: max, percent: cur / max };
}
// ===== 获取玩家坦克 =====
// 优先 ezr_1(当前观察坦克),备用 fzr_1(本地控制坦克),再备用坦克列表第一个
// checkPosition: 是否检查位置有效性(默认true;测试脚本传false以在死亡状态控制)
function GetPlayerTank(gm, checkPosition) {
if (checkPosition === undefined) checkPosition = true;
var t = gm.x100_1;
if (t && (!checkPosition || GetTankPosition(t))) return t;
t = gm.y100_1;
if (t && (!checkPosition || GetTankPosition(t))) return t;
try {
var tl = gm.q100_1.r11l();
if (tl.e1() > 0) {
t = tl.g1(0);
if (t && (!checkPosition || GetTankPosition(t))) return t;
}
} catch(e) {}
return null;
}
// ===== 判断是否友方 =====
// 策略1(优先):ZCt 获取双方队伍ID,m2 比较(同队且非中立)
// 策略2(备用):比较 gm.gzr_1(本地队伍ID)与坦克队伍ID
function IsFriendly(tank, playerTank, gm) {
if (!tank || !playerTank) return false;
if (ZCt && m2Fn) {
try {
var tankTeam = ZCt(tank);
var playerTeam = ZCt(playerTank);
if (tankTeam && playerTeam) return m2Fn(tankTeam, playerTeam);
} catch(e) {}
}
try {
if (gm && gm.z100_1 && ZCt) {
var team = ZCt(tank);
if (team && gm.z100_1) {
return team.equals(gm.z100_1) && m2Fn ? m2Fn(team, gm.z100_1) : team.equals(gm.z100_1);
}
}
} catch(e) {}
return false;
}
// ===== 获取相机信息($r/gq 相机组件) =====
// 遍历 GameMode entity 和所有坦克 entity 查找活跃相机组件
// 策略1:查找 $r/gq 相机组件(有 kym_1 位置 和 lym_1 方向)
// 策略2:查找 k7 相机控制器(有 qz9_1 和 gz9_1),从 gz9_1 获取相机组件
// 返回 {yaw, pitch, fov, pos:{x,y,z}}
// 注意:lym_1.h20_1 是 roll(绕Z),lym_1.f20_1 是 pitch(绕X)
function GetCameraInfo(gm) {
var entities = [];
try {
var gameScene = gm.jyw().iyt_1;
var gmEntity = gameScene.xyu("GameMode");
if (gmEntity) entities.push(gmEntity);
} catch(e) {}
try {
var tankList = gm.q100_1.r11l();
var tCount = tankList.e1();
for (var i = 0; i < tCount; i++) {
var t = tankList.g1(i);
if (t) entities.push(t);
}
} catch(e) {}
for (var i = 0; i < entities.length; i++) {
var entity = entities[i];
if (!entity || !entity.pyt_1 || !entity.pyt_1.s2a_1) continue;
var found = null;
entity.pyt_1.s2a_1.forEach(function(valueList, key) {
if (found) return;
try {
var comp = (valueList && typeof valueList.g1 === 'function') ? valueList.g1(0) : valueList;
if (!comp) return;
// 策略1:$r/gq 相机组件
if (comp.cyw_1 && comp.dyw_1 &&
comp.cyw_1.f20_1 !== undefined && comp.dyw_1.f20_1 !== undefined) {
if (Math.abs(comp.dyw_1.h20_1) > 0.001 || Math.abs(comp.dyw_1.f20_1) > 0.001) {
found = comp;
return;
}
}
// 策略2:k7 相机控制器
if (comp.jzj_1 && comp.zzi_1) {
var camComp = comp.zzi_1;
if (camComp && camComp.cyw_1 && camComp.dyw_1 &&
(Math.abs(camComp.dyw_1.h20_1) > 0.001 || Math.abs(camComp.dyw_1.f20_1) > 0.001)) {
found = camComp;
return;
}
}
} catch(e) {}
});
if (found) {
return {
yaw: found.dyw_1.h20_1,
pitch: found.dyw_1.f20_1,
fov: found.byw_1 || 1.0471976,
pos: {
x: found.cyw_1.f20_1,
y: found.cyw_1.g20_1,
z: found.cyw_1.h20_1
}
};
}
}
return null;
}
// ===== 获取全局碰撞检测器(uMt 组件,挂在 GameMode entity 上) =====
// g16x(startVec, dirVec, maxDist, resultObj, filterObj) = 全局射线检测
function GetUMtComponent(gm) {
try {
var gmEntity = gm.jyw();
if (!gmEntity || !gmEntity.pyt_1 || !gmEntity.pyt_1.s2a_1) return null;
var uMt = null;
gmEntity.pyt_1.s2a_1.forEach(function(valueList, key) {
if (uMt) return;
try {
var comp = (valueList && typeof valueList.g1 === 'function') ? valueList.g1(0) : valueList;
if (comp && typeof comp.m179 === 'function') uMt = comp;
} catch(e) {}
});
return uMt;
} catch(e) {
return null;
}
}
// ===== 构造 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; }
};
}
// ===== 射线检测:返回命中距离(-1=未命中/异常) =====
// 从 fromPos 沿 (dirX,dirY,dirZ) 方向,最大距离 maxDist
function RaycastDistance(gm, fromPos, dirX, dirY, dirZ, maxDist) {
var uMt = GetUMtComponent(gm);
if (!uMt) return -1;
try {
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,
c179_1: null,
b179_1: null,
d179: function(t, n, i, r, e) {
this.y178_1.k26(t);
this.z178_1.k26(n);
this.a179_1 = i;
this.c179_1 = r;
this.b179_1 = e;
},
d3: function() { this.b179_1 = null; }
};
var filter = {
o1co_1: { h1: function() { return false; }, d3: function() {} },
w179: function() { return true; },
d3: function() { this.o1co_1.d3(); },
p1co: function() {}
};
var hit = uMt.m179(startVec, dirVec, maxDist, result, filter);
return (hit && result.a179_1 < maxDist) ? result.a179_1 : -1;
} catch(e) {
return -1;
}
}
// ===== 3D 到 2D 投影(用于 UI 标记) =====
// 标准透视投影:forward/right/up 三轴正交基
function Project3D(worldPos, camPos, yaw, pitch, fov, screenW, screenH) {
var cp = Math.cos(pitch);
var sp = Math.sin(pitch);
var cy = Math.cos(yaw);
var sy = Math.sin(yaw);
var forward = { x: cp * (-sy), y: cp * cy, z: sp };
var right = { x: cy, y: sy, z: 0 };
var up = {
x: right.y * forward.z - right.z * forward.y,
y: right.z * forward.x - right.x * forward.z,
z: right.x * forward.y - right.y * forward.x
};
var relX = worldPos.x - camPos.x;
var relY = worldPos.y - camPos.y;
var relZ = worldPos.z - camPos.z;
var camX = relX * right.x + relY * right.y + relZ * right.z;
var camY = relX * up.x + relY * up.y + relZ * up.z;
var camZ = relX * forward.x + relY * forward.y + relZ * forward.z;
if (camZ <= 0.1) return null;
var focalLength = screenH / (2 * Math.tan(fov / 2));
var screenX = screenW / 2 + (camX / camZ) * focalLength;
var screenY = screenH / 2 - (camY / camZ) * focalLength;
return { x: screenX, y: screenY, depth: camZ };
}
// ===== 用世界变换矩阵变换顶点(列主序4x4矩阵) =====
function TransformVertex(matrix, x, y, z) {
return {
x: matrix[0] * x + matrix[4] * y + matrix[8] * z + matrix[12],
y: matrix[1] * x + matrix[5] * y + matrix[9] * z + matrix[13],
z: matrix[2] * x + matrix[6] * y + matrix[10] * z + matrix[14]
};
}
// ===== 让元素可拖动(左键拖动,改变 left/top) =====
// 适用于固定定位的小窗口(雷达、后视镜等)
// 实现要点:Pointer Events + setPointerCapture,保证 pointerup/pointercancel 必定回到元素,
// 并在 window 捕获阶段兜底,避免游戏画布抢事件后"松手仍粘着鼠标"。
function MakeDraggable(elem) {
if (!elem) return null;
elem.style.cursor = 'move';
var Active = false;
var PointerId = null;
var GrabX = 0;
var GrabY = 0;
function MoveTo(ClientX, ClientY) {
var NextX = Math.max(0, Math.min(window.innerWidth - 50, ClientX - GrabX));
var NextY = Math.max(0, Math.min(window.innerHeight - 30, ClientY - GrabY));
elem.style.left = NextX + 'px';
elem.style.top = NextY + 'px';
}
function Begin(ClientX, ClientY) {
var Rect = elem.getBoundingClientRect();
// 移除 right 定位,改用 left
elem.style.right = 'auto';
elem.style.transform = 'none';
elem.style.left = Rect.left + 'px';
elem.style.top = Rect.top + 'px';
GrabX = ClientX - Rect.left;
GrabY = ClientY - Rect.top;
Active = true;
elem.style.cursor = 'grabbing';
}
function End() {
if (!Active) return;
Active = false;
if (PointerId !== null && elem.releasePointerCapture) {
try { elem.releasePointerCapture(PointerId); } catch (Err) {}
}
PointerId = null;
elem.style.cursor = 'move';
}
if (window.PointerEvent) {
elem.addEventListener('pointerdown', function (e) {
if (e.button !== 0) return;
Begin(e.clientX, e.clientY);
PointerId = e.pointerId;
try { elem.setPointerCapture(e.pointerId); } catch (Err) {}
e.preventDefault();
});
elem.addEventListener('pointermove', function (e) {
if (!Active || (PointerId !== null && e.pointerId !== PointerId)) return;
if (e.buttons === 0) { End(); return; }
MoveTo(e.clientX, e.clientY);
});
elem.addEventListener('pointerup', function () { End(); });
elem.addEventListener('pointercancel', function () { End(); });
elem.addEventListener('lostpointercapture', function () { End(); });
window.addEventListener('pointerup', function () { End(); }, true);
} else {
elem.addEventListener('mousedown', function (e) {
if (e.button !== 0) return;
Begin(e.clientX, e.clientY);
e.preventDefault();
});
document.addEventListener('mousemove', function (e) {
if (!Active) return;
if (e.buttons === 0) { End(); return; }
MoveTo(e.clientX, e.clientY);
}, true);
document.addEventListener('mouseup', function () { End(); }, true);
}
window.addEventListener('blur', End);
window.addEventListener('mouseleave', End);
return { End: End, IsActive: function () { return Active; } };
}
// ===== 导出公共函数到 window.__hornetLib__ =====
window.__hornetLib__ = {
LoadTeamFunctions: LoadTeamFunctions,
FindComponentByField: FindComponentByField,
GetTankPosition: GetTankPosition,
GetTankState: GetTankState,
IsTankAlive: IsTankAlive,
GetTankName: GetTankName,
GetTankHealth: GetTankHealth,
GetPlayerTank: GetPlayerTank,
IsFriendly: IsFriendly,
GetCameraInfo: GetCameraInfo,
GetUMtComponent: GetUMtComponent,
MkVec: MkVec,
RaycastDistance: RaycastDistance,
Project3D: Project3D,
TransformVertex: TransformVertex,
MakeDraggable: MakeDraggable
};
console.log('[公共库] 已加载,可用函数:', Object.keys(window.__hornetLib__).join(', '));
})();