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

95 lines
2.6 KiB
TypeScript

/** Drones aliados: seguem o jogador em órbita e atiram no zumbi mais próximo à vista. */
import { registrarEliminacao } from "./feed";
import { moverComColisao, temLinhaDeVisao } from "./mapa";
import type { Drone, Partida, Zumbi } from "./tipos";
import { DRONE, PASSO } from "./valores";
import { ferir } from "./zumbis";
const RAIO_DO_DRONE = 0.2;
const PASSO_MAXIMO = DRONE.velocidade * PASSO;
const ANGULO_ENTRE_DRONES = (2 * Math.PI) / 3;
const ALTURA_DO_ACERTO = 1.2;
export function criarDrone(p: Partida, problemaId: string): Drone {
const drone: Drone = {
id: p.proximoId++,
problemaId,
x: p.jogador.x,
z: p.jogador.z,
rumo: p.jogador.rumo,
recarga: DRONE.intervalo,
eliminacoes: 0,
};
p.drones.push(drone);
return drone;
}
function seguirJogador(p: Partida, drone: Drone, indice: number): void {
const angulo = p.jogador.rumo + Math.PI / 2 + indice * ANGULO_ENTRE_DRONES;
const dx = p.jogador.x + Math.sin(angulo) * DRONE.distancia - drone.x;
const dz = p.jogador.z + Math.cos(angulo) * DRONE.distancia - drone.z;
const fracao = Math.min(1, PASSO_MAXIMO / Math.hypot(dx, dz));
[drone.x, drone.z] = moverComColisao(
p.mapa,
drone.x,
drone.z,
dx * fracao,
dz * fracao,
RAIO_DO_DRONE,
);
}
function zumbiMaisProximo(p: Partida, drone: Drone): Zumbi | undefined {
let alvo: Zumbi | undefined;
let melhor = Number.POSITIVE_INFINITY;
for (const zumbi of p.zumbis) {
const d = Math.hypot(zumbi.x - drone.x, zumbi.z - drone.z);
if (
zumbi.estado !== "morrendo" &&
d <= DRONE.alcance &&
d < melhor &&
temLinhaDeVisao(p.mapa, drone.x, drone.z, zumbi.x, zumbi.z)
) {
alvo = zumbi;
melhor = d;
}
}
return alvo;
}
function atirar(p: Partida, drone: Drone, alvo: Zumbi): void {
const dx = alvo.x - drone.x;
const dz = alvo.z - drone.z;
drone.rumo = Math.atan2(dx, dz);
p.eventos.push(
{
tipo: "tiro",
autor: drone.id,
arma: "drone",
distancia: Math.hypot(dx, dz),
},
{
tipo: "acerto",
zumbiId: alvo.id,
cabeca: false,
x: alvo.x,
y: ALTURA_DO_ACERTO,
z: alvo.z,
},
);
const matou = ferir(p, alvo, DRONE.dano, drone.id);
drone.recarga = DRONE.intervalo;
if (matou) registrarEliminacao(p, drone.problemaId, alvo.problemaId);
}
export function avancarDrones(p: Partida): void {
p.drones.forEach((drone, indice) => {
seguirJogador(p, drone, indice);
drone.recarga = Math.max(0, drone.recarga - 1);
if (drone.recarga > 0) return;
const alvo = zumbiMaisProximo(p, drone);
if (alvo) atirar(p, drone, alvo);
});
}