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.
135 lines
4.0 KiB
TypeScript
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);
|
|
},
|
|
};
|
|
}
|