Files
portifolio-evandrodevbr/app/jogo/controle.ts
T
evandro 67b32fcab3
CI / audit (push) Successful in 10m48s
CI / verify (push) Successful in 19m2s
CI / deploy (push) Successful in 2m14s
CI / e2e (push) Failing after 36m15s
feat(jogo): expediente dos mortos, jogo de zumbis em primeira pessoa
Etapa 1 inteira (logica com 100% de cobertura, cena 3D, toque, sons, bancada de desempenho, e2e) e a base da etapa 2 (mapa novo no Tiled, texturas, imagens dos itens, sons conferidos, golpe recortado e medido). Pagina /jogo fora do menu e do sitemap ate a medicao no celular.
2026-11-10 19:37:10 -03:00

135 lines
4.0 KiB
TypeScript

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<string, readonly [number, number]> = {
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<string, ArmaId> = {
Digit1: 0,
Digit2: 1,
Digit3: 2,
};
export function criarControle(
alvo: HTMLElement,
opcoes: { aoMudarTrava?: (travado: boolean) => void } = {},
): Controle {
const teclas = new Set<string>();
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);
},
};
}