// 移动端虚拟手柄:左手方向、右手跳跃 + 道具(参考 IWBT Creator 的触屏布局) // 左下方:← → 移动;右下方:大跳键 + 射击/下蹲 道具键 + 暂停/重生 import { setTouch, clearTouch } from './input.js'; // 持续按住型动作(通过 action 状态机驱动) const HELD = ['left', 'right', 'jump', 'down']; const api = { pause() {}, suicide() {}, shoot() {}, }; let visible = false; let root = null; function q(sel) { return document.querySelector(sel); } function bind(btn) { const action = btn.dataset.touch; if (!action) return; let timer = null; const press = (e) => { e.preventDefault(); if (btn._pid != null) return; // 已在按住(多指防重入) btn._pid = e.pointerId; try { btn.setPointerCapture(e.pointerId); } catch (_) {} btn.classList.add('on'); if (action === 'shoot') { // 长按连发(受玩家冷却与是否持枪限制) api.shoot(); timer = setInterval(() => api.shoot(), 140); } else if (HELD.includes(action)) { setTouch(action, true); } else if (action === 'pause') { api.pause(); } else if (action === 'suicide') { api.suicide(); } }; const release = (e) => { if (btn._pid != null && e.pointerId !== btn._pid) return; btn._pid = null; try { btn.releasePointerCapture(e.pointerId); } catch (_) {} btn.classList.remove('on'); if (action === 'shoot') { if (timer) { clearInterval(timer); timer = null; } } else if (HELD.includes(action)) { setTouch(action, false); } }; btn.addEventListener('pointerdown', press); btn.addEventListener('pointerup', release); btn.addEventListener('pointercancel', release); btn.addEventListener('lostpointercapture', release); btn.addEventListener('contextmenu', (e) => e.preventDefault()); } export const Touch = { init(handlers) { Object.assign(api, handlers || {}); root = q('#touch'); root.querySelectorAll('[data-touch]').forEach(bind); // 游戏进行中禁止页面滚动/回弹 const stage = q('#stage'); if (stage) { stage.addEventListener('touchmove', (e) => { if (visible) e.preventDefault(); }, { passive: false }); stage.addEventListener('contextmenu', (e) => { if (visible) e.preventDefault(); }); } }, show() { if (!root) root = q('#touch'); if (!root) return; visible = true; root.classList.remove('hidden'); }, hide() { if (!root) root = q('#touch'); if (!root) return; visible = false; root.classList.add('hidden'); clearTouch(); // 防止按键卡住 }, /** 反映是否持有手枪:无枪时射击键置灰 */ setGun(has) { const g = q('#tc-gun'); if (g) g.classList.toggle('off', !has); }, };