Files
portifolio-evandrodevbr/lib/jogo/piloto.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

76 lines
2.6 KiB
TypeScript

/** Jogador automático para testes e medição: função pura do estado, sem memória própria. */
import { temLinhaDeVisao } from "./mapa";
import type { ArmaId, Entrada, Partida, Zumbi } from "./tipos";
import { JOGADOR, ZUMBIS } from "./valores";
const GIRO_MAXIMO = 0.12;
const INCLINACAO_MAXIMA = 0.05;
const ALTURA_DA_MIRA = 0.65;
const ALCANCE_DA_FACA = 1.3;
const ERRO_PARA_ATIRAR = 0.05;
const DISTANCIA_DE_RECUO = 4;
const RAIO_DA_MULTIDAO = 12;
const TAMANHO_DA_MULTIDAO = 3;
const PISTOLA: ArmaId = 0;
const AUTOMATICA: ArmaId = 1;
const FACA: ArmaId = 2;
type Alvo = { zumbi: Zumbi; vivos: Zumbi[]; visao: boolean };
function distanciaAte(p: Partida, zumbi: Zumbi): number {
return Math.hypot(zumbi.x - p.jogador.x, zumbi.z - p.jogador.z);
}
function escolherAlvo(p: Partida): Alvo | undefined {
const { x, z } = p.jogador;
const vivos = p.zumbis.filter((zumbi) => zumbi.estado !== "morrendo");
if (vivos.length === 0) return undefined;
const vistos = vivos.filter((zumbi) =>
temLinhaDeVisao(p.mapa, x, z, zumbi.x, zumbi.z),
);
const lista = vistos.length > 0 ? vistos : vivos;
const zumbi = lista.reduce((a, b) =>
distanciaAte(p, b) < distanciaAte(p, a) ? b : a,
);
return { zumbi, vivos, visao: vistos.length > 0 };
}
function limitar(valor: number, maximo: number): number {
return Math.max(-maximo, Math.min(maximo, valor));
}
function escolherArma(p: Partida, alvo: Alvo): ArmaId {
const { tipo } = alvo.zumbi;
if (distanciaAte(p, alvo.zumbi) - ZUMBIS[tipo].raio <= ALCANCE_DA_FACA) {
return FACA;
}
const perto = alvo.vivos.filter(
(zumbi) => distanciaAte(p, zumbi) <= RAIO_DA_MULTIDAO,
);
const exige =
perto.length >= TAMANHO_DA_MULTIDAO ||
tipo === "blindado" ||
tipo === "chefao";
return p.jogador.municao > 0 && exige ? AUTOMATICA : PISTOLA;
}
export function pilotar(p: Partida): Entrada {
const alvo = escolherAlvo(p);
if (!alvo) return { mover: [0, 0], olhar: [0, 0], atirar: false, arma: 0 };
const { zumbi } = alvo;
const { x, z, rumo, inclinacao } = p.jogador;
const distancia = distanciaAte(p, zumbi);
const desvio = Math.atan2(zumbi.x - x, zumbi.z - z) - rumo;
const erro = Math.atan2(Math.sin(desvio), Math.cos(desvio));
const altura = ZUMBIS[zumbi.tipo].altura * ALTURA_DA_MIRA - JOGADOR.olhos;
const subida = Math.atan2(altura, distancia) - inclinacao;
return {
mover: [0, distancia < DISTANCIA_DE_RECUO ? -1 : 0],
olhar: [limitar(erro, GIRO_MAXIMO), limitar(subida, INCLINACAO_MAXIMA)],
atirar: Math.abs(erro) < ERRO_PARA_ATIRAR && alvo.visao,
arma: escolherArma(p, alvo),
};
}