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