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.
95 lines
2.6 KiB
TypeScript
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);
|
|
});
|
|
}
|