// ===== 3DTank自由视角 ===== // 在浏览器控制台运行(需先运行 hook_gamemode.js 和 hornet_lib.js) // 功能:解锁主相机,WASD移动/鼠标右键旋转/QE升降/Shift加速/滚轮调速 // 原理:Hook mainCamera.h37,在相机矩阵更新前覆盖l35_1/k35_1参数 (function() { function _start() { console.log('===== 3DTank自由视角启动 ====='); if (!window.__gameMode__) { console.error('请先运行 hook_gamemode.js'); return; } if (window.__hornetFreeLook__) { window.__hornetFreeLook__.Stop(); console.log('已停止旧自由视角'); } var gm = window.__gameMode__; // ===== 引用公共库 ===== var lib = window.__hornetLib__; if (!lib) { console.error('[自由视角] 请先运行 hornet_lib.js'); return; } function GetPlayerTank() { return lib.GetPlayerTank(gm); } function GetTankPosition(tank) { return lib.GetTankPosition(tank); } // ===== 获取游戏渲染对象 ===== var scene = null, mainCamera = null, ff = null; var initError = null; try { scene = gm.jyw().iyt_1; mainCamera = scene.hyp_1; ff = scene.fyo_1.d75_1; if (!mainCamera || !mainCamera.s35_1 || !mainCamera.r35_1 || !mainCamera.o37) { throw new Error('相机字段缺失'); } console.log('[自由视角] 主相机:', mainCamera.constructor.name, 'l35=(' + mainCamera.s35_1.f20_1.toFixed(2) + ',' + mainCamera.s35_1.g20_1.toFixed(2) + ',' + mainCamera.s35_1.h20_1.toFixed(2) + ')', 'k35=(' + mainCamera.r35_1.f20_1.toFixed(0) + ',' + mainCamera.r35_1.g20_1.toFixed(0) + ',' + mainCamera.r35_1.h20_1.toFixed(0) + ')'); } catch(e) { initError = e; console.error('[自由视角] 初始化失败:', e.message); } if (initError) return; // ===== 自由视角状态 ===== var freeLookActive = false; var running = true; // 自由视角参数(从当前相机参数初始化) var freeYaw = 0, freePitch = 0, freeRoll = 0; var freePosX = 0, freePosY = 0, freePosZ = 0; var moveSpeed = 1200; // 基础移动速度(世界单位/秒) var speedMultiplier = 1; // 滚轮调速倍数(0.2~5.0) // 按键状态 var keys = {}; var mouseSensitivity = 0.005; // 鼠标灵敏度 // ===== Hook mainCamera.h37 ===== var hadOwnH37 = mainCamera.hasOwnProperty('o37'); var origH37 = mainCamera.o37; function applyFreeLookParams() { mainCamera.s35_1.f20_1 = freePitch; // pitch (f) mainCamera.s35_1.g20_1 = freeRoll; // roll (g) 保持初始值 mainCamera.s35_1.h20_1 = freeYaw; // yaw (h) 鼠标左右控制 mainCamera.r35_1.f20_1 = freePosX; mainCamera.r35_1.g20_1 = freePosY; mainCamera.r35_1.h20_1 = freePosZ; } // 缓存从 viewMatrix 提取的前方向/右方向(h37 更新后设置) // o37 行向量布局(列主序存储): // 行0 = (d[0], d[4], d[8]) = right 向量 // 行1 = (d[1], d[5], d[9]) = up 向量 // 行2 = (d[2], d[6], d[10]) = -forward 向量 var cachedFwdX = 0, cachedFwdY = 0, cachedRightX = 0, cachedRightY = 0; var cachedInitialized = false; var suspended = false; // 后视镜等模块渲染时临时暂停 hook mainCamera.o37 = function() { if (freeLookActive && !suspended) { 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 cachedInitialized = true; } } else { return origH37.call(this); } }; console.log('[自由视角] mainCamera.o37 已 Hook'); // ===== 开启/关闭自由视角 ===== function enableFreeLook() { // 从当前相机参数初始化(平滑过渡) // l35_1: f=pitch, g=roll, h=yaw freePitch = mainCamera.s35_1.f20_1; freeRoll = mainCamera.s35_1.g20_1; // roll 保持初始值 freeYaw = mainCamera.s35_1.h20_1; // yaw 鼠标控制 freePosX = mainCamera.r35_1.f20_1; freePosY = mainCamera.r35_1.g20_1; freePosZ = mainCamera.r35_1.h20_1; freeLookActive = true; console.log('[自由视角] 已开启 — WASD移动 | 鼠标旋转 | 空格上升/Shift下降 | 滚轮调速 | Alt+F关闭'); updateHud(); } function disableFreeLook() { freeLookActive = false; keys = {}; // 清空按键状态,防止卡死 console.log('[自由视角] 已关闭,恢复游戏相机控制'); updateHud(); } function toggleFreeLook() { if (freeLookActive) disableFreeLook(); else enableFreeLook(); } // ===== 键盘事件(capture阶段拦截,防止游戏接收WASD) ===== // 用 e.code(物理键位)匹配,比 e.key 更稳定,不受 Shift 修饰影响 var keyMap = { 'KeyW': 'w', 'KeyA': 'a', 'KeyS': 's', 'KeyD': 'd', 'KeyQ': 'q', 'KeyE': 'e', 'Space': ' ', 'ShiftLeft': 'shift', 'ShiftRight': 'shift' }; function onKeyDown(e) { // 焦点在输入框/选择框时不拦截(允许面板输入) var ae = document.activeElement; if (ae && (ae.tagName === 'INPUT' || ae.tagName === 'SELECT' || ae.tagName === 'TEXTAREA')) return; // Alt+F 切换(全局,无论是否开启) if (e.altKey && (e.key === 'f' || e.key === 'F')) { e.preventDefault(); e.stopPropagation(); toggleFreeLook(); return; } if (!freeLookActive) return; var key = keyMap[e.code] || e.key.toLowerCase(); if (keyMap[e.code]) { keys[key] = true; e.preventDefault(); e.stopPropagation(); } } function onKeyUp(e) { // 焦点在输入框/选择框时不拦截 var ae = document.activeElement; if (ae && (ae.tagName === 'INPUT' || ae.tagName === 'SELECT' || ae.tagName === 'TEXTAREA')) return; var key = keyMap[e.code] || e.key.toLowerCase(); if (keyMap[e.code]) { keys[key] = false; // 总是清理按键状态,防止卡死 // 只在自由视角激活时拦截事件,暂停时放行给游戏 if (freeLookActive) { e.preventDefault(); e.stopPropagation(); } } } // ===== 鼠标事件(自由视角激活时鼠标移动直接旋转,不按键) ===== function onMouseMove(e) { if (!freeLookActive) return; // 鼠标左移→视角左转(yaw减),鼠标上移→视角上抬(pitch增) freeYaw -= e.movementX * mouseSensitivity; freePitch -= e.movementY * mouseSensitivity; // 限制俯仰角到±80°,避免接近90°时的万向锁翻转 var limit = 80 * Math.PI / 180; if (freePitch > limit) freePitch = limit; if (freePitch < -limit) freePitch = -limit; } function onWheel(e) { if (!freeLookActive) return; e.preventDefault(); e.stopPropagation(); // 滚轮上=加速,下=减速 if (e.deltaY < 0) speedMultiplier = Math.min(5.0, speedMultiplier + 0.2); else speedMultiplier = Math.max(0.2, speedMultiplier - 0.2); updateHud(); } // capture 阶段拦截键盘 window.addEventListener('keydown', onKeyDown, true); window.addEventListener('keyup', onKeyUp, true); document.addEventListener('mousemove', onMouseMove); window.addEventListener('wheel', onWheel, true); // ===== HUD 显示 ===== var hud = document.createElement('div'); hud.id = 'hornetFreeLookHud'; hud.style.cssText = 'position:fixed;bottom:10px;left:10px;z-index:99999;' + 'background:rgba(0,0,0,0.7);color:#8f8;padding:6px 10px;border-radius:4px;' + 'font:12px monospace;pointer-events:none;border:1px solid rgba(120,255,120,0.5);' + 'display:none;user-select:none;'; document.body.appendChild(hud); function updateHud() { if (!freeLookActive) { hud.style.display = 'none'; return; } hud.style.display = 'block'; hud.innerHTML = '[自由视角 ON] 速度:' + (moveSpeed * speedMultiplier).toFixed(0) + '/秒
' + '位置:(' + freePosX.toFixed(0) + ',' + freePosY.toFixed(0) + ',' + freePosZ.toFixed(0) + ')
' + 'Yaw:' + freeYaw.toFixed(2) + ' Pitch:' + freePitch.toFixed(2) + '
' + 'WASD移动 鼠标旋转 空格上升/Shift下降 滚轮调速 Alt+F关闭'; } // ===== 主循环:处理按键 → 更新位置 ===== var rafId = null; var lastTime = performance.now(); function loop(now) { if (!running) return; var dt = (now - lastTime) / 1000; lastTime = now; if (dt > 0.1) dt = 0.1; // 防止卡顿后大跳 if (freeLookActive) { var speed = moveSpeed * speedMultiplier * dt; // 每秒 moveSpeed 单位 // 相机前方向/右方向:优先用 viewMatrix 提取的真实方向(跟随鼠标朝向) var fwdX, fwdY, rightX, rightY; if (cachedInitialized) { // 从 viewMatrix 提取的水平方向分量(列主序) fwdX = cachedFwdX; fwdY = cachedFwdY; rightX = cachedRightX; rightY = cachedRightY; // 归一化水平方向(pitch 不为0时向量长度<1,保持移动速度一致) var fwdLen = Math.sqrt(fwdX * fwdX + fwdY * fwdY); if (fwdLen > 1e-6) { fwdX /= fwdLen; fwdY /= fwdLen; } var rightLen = Math.sqrt(rightX * rightX + rightY * rightY); if (rightLen > 1e-6) { rightX /= rightLen; rightY /= rightLen; } } else { // fallback: sin/cos(viewMatrix 尚未初始化时) fwdX = -Math.sin(freeYaw); fwdY = -Math.cos(freeYaw); rightX = Math.cos(freeYaw); rightY = -Math.sin(freeYaw); } var moved = false; // W/S 前后(水平移动,可与其他键组合) if (keys['w']) { freePosX += fwdX * speed; freePosY += fwdY * speed; moved = true; } if (keys['s']) { freePosX -= fwdX * speed; freePosY -= fwdY * speed; moved = true; } // A/D 左右(水平移动,可与其他键组合) if (keys['a']) { freePosX -= rightX * speed; freePosY -= rightY * speed; moved = true; } if (keys['d']) { freePosX += rightX * speed; freePosY += rightY * speed; moved = true; } // 空格上升 / Shift 下降(绝对 z 轴,可与 WASD 组合) if (keys[' ']) { freePosZ += speed; moved = true; } if (keys['shift']) { freePosZ -= speed; moved = true; } // Q/E 升降(备用) if (keys['q']) { freePosZ -= speed; moved = true; } if (keys['e']) { freePosZ += speed; moved = true; } if (moved) updateHud(); } rafId = requestAnimationFrame(loop); } rafId = requestAnimationFrame(loop); // ===== 对外接口 ===== var self = { Stop: function() { running = false; if (rafId) cancelAnimationFrame(rafId); disableFreeLook(); // 恢复 h37 if (hadOwnH37) mainCamera.o37 = origH37; else delete mainCamera.o37; // 移除监听 window.removeEventListener('keydown', onKeyDown, true); window.removeEventListener('keyup', onKeyUp, true); document.removeEventListener('mousemove', onMouseMove); window.removeEventListener('wheel', onWheel, true); if (hud.parentNode) hud.parentNode.removeChild(hud); window.__hornetFreeLook__ = null; console.log('[自由视角] 已停止'); }, Status: function() { // 返回字符串供面板 HUD 显示(其他工具也是字符串) if (!running) return '未运行'; return freeLookActive ? '已开启' : '已暂停'; }, GetRunning: function() { return freeLookActive; }, IsActive: function() { return freeLookActive; }, Toggle: function() { toggleFreeLook(); }, Enable: function() { if (!freeLookActive) enableFreeLook(); }, Disable: function() { if (freeLookActive) disableFreeLook(); }, // 供其他模块(雷达/3D透视)获取虚拟相机参数 GetPosition: function() { return freeLookActive ? { x: freePosX, y: freePosY, z: freePosZ } : null; }, GetDirection: function() { return freeLookActive ? { yaw: freeYaw, pitch: freePitch } : null; }, // 供后视镜等模块渲染时临时暂停 h37 hook SetSuspended: function(s) { suspended = s; } }; window.__hornetFreeLook__ = self; window.__hornetTools__ = window.__hornetTools__ || {}; window.__hornetTools__.FreeLook = self; // 启动时自动激活自由视角 enableFreeLook(); console.log('[自由视角] 就绪 — 已激活,Alt+F 切换激活/暂停'); } function _stop() { if (window.__hornetFreeLook__) window.__hornetFreeLook__.Stop(); } function _status() { return window.__hornetFreeLook__ ? window.__hornetFreeLook__.Status() : '未运行'; } function _getRunning() { return !!(window.__hornetFreeLook__ && window.__hornetFreeLook__.GetRunning()); } window.__hornetTools__ = window.__hornetTools__ || {}; window.__hornetTools__.FreeLook = { Start: _start, Stop: _stop, Status: _status, GetRunning: _getRunning }; })();