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.
This commit is contained in:
1 parent
c7d09d63d4
commit
67b32fcab3
118 files changed
+16730
No files matched your search
@@ -0,0 +1,134 @@
|
||||
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);
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user