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,75 @@
|
||||
/** 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),
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user