import type { ArmaId, Entrada } from "@/lib/jogo/tipos"; import type { Controle } from "./contrato"; const SENSIBILIDADE = 0.0022; const CAMPOS = "input, textarea, select, button, [contenteditable]"; const ARMAS_NA_ORDEM = 3; /** Teclas de movimento: [direita, frente]. */ const DIRECAO: Record = { KeyW: [0, 1], ArrowUp: [0, 1], KeyS: [0, -1], ArrowDown: [0, -1], KeyD: [1, 0], ArrowRight: [1, 0], KeyA: [-1, 0], ArrowLeft: [-1, 0], }; const ARMA_DA_TECLA: Record = { Digit1: 0, Digit2: 1, Digit3: 2, }; export function criarControle( alvo: HTMLElement, opcoes: { aoMudarTrava?: (travado: boolean) => void } = {}, ): Controle { const teclas = new Set(); let toque: [number, number] = [0, 0]; let giro: [number, number] = [0, 0]; let mouseAtira = false; let toqueAtira = false; // apertou desde a Ășltima leitura: um clique inteiro entre dois passos ainda vira um tiro let apertou = false; let arma: ArmaId = 0; const travado = () => document.pointerLockElement === alvo; const aoApertar = (e: KeyboardEvent) => { if (e.target instanceof Element && e.target.closest(CAMPOS)) return; if (e.code in ARMA_DA_TECLA) arma = ARMA_DA_TECLA[e.code]; if (e.code in DIRECAO) { teclas.add(e.code); e.preventDefault(); } }; const aoSoltar = (e: KeyboardEvent) => { teclas.delete(e.code); }; const aoPerderFoco = () => teclas.clear(); const aoRolar = (e: WheelEvent) => { const passo = e.deltaY > 0 ? 1 : -1; arma = ((arma + passo + ARMAS_NA_ORDEM) % ARMAS_NA_ORDEM) as ArmaId; }; const aoClicar = () => alvo.requestPointerLock?.(); const aoMover = (e: MouseEvent) => { if (!travado()) return; giro = [ giro[0] - e.movementX * SENSIBILIDADE, giro[1] - e.movementY * SENSIBILIDADE, ]; }; const aoApertarMouse = (e: MouseEvent) => { if (e.button !== 0 || !travado()) return; mouseAtira = true; apertou = true; }; const aoSoltarMouse = (e: MouseEvent) => { if (e.button === 0) mouseAtira = false; }; const aoMudarTrava = () => opcoes.aoMudarTrava?.(travado()); window.addEventListener("keydown", aoApertar); window.addEventListener("keyup", aoSoltar); window.addEventListener("blur", aoPerderFoco); window.addEventListener("wheel", aoRolar); alvo.addEventListener("click", aoClicar); document.addEventListener("mousemove", aoMover); document.addEventListener("mousedown", aoApertarMouse); document.addEventListener("mouseup", aoSoltarMouse); document.addEventListener("pointerlockchange", aoMudarTrava); const soltar = () => { teclas.clear(); toque = [0, 0]; giro = [0, 0]; mouseAtira = false; toqueAtira = false; apertou = false; }; return { ler(): Entrada { let x = toque[0]; let y = toque[1]; for (const t of teclas) { x += DIRECAO[t][0]; y += DIRECAO[t][1]; } const olhar = giro; giro = [0, 0]; const atirar = mouseAtira || toqueAtira || apertou; apertou = false; return { mover: [x, y], olhar, atirar, arma }; }, definirMover: (x, y) => { toque = [x, y]; }, somarOlhar: (rumo, inclinacao) => { giro = [giro[0] + rumo, giro[1] + inclinacao]; }, definirAtirar: (atirando) => { if (atirando && !toqueAtira) apertou = true; toqueAtira = atirando; }, definirArma: (nova) => { arma = nova; }, soltar, descartar: () => { window.removeEventListener("keydown", aoApertar); window.removeEventListener("keyup", aoSoltar); window.removeEventListener("blur", aoPerderFoco); window.removeEventListener("wheel", aoRolar); alvo.removeEventListener("click", aoClicar); document.removeEventListener("mousemove", aoMover); document.removeEventListener("mousedown", aoApertarMouse); document.removeEventListener("mouseup", aoSoltarMouse); document.removeEventListener("pointerlockchange", aoMudarTrava); }, }; }