/** 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), }; }