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

236 lines
6.4 KiB
TypeScript

import { avancarDrones, criarDrone } from "./drones";
import { lerMapa } from "./mapa";
import { PROBLEMAS } from "./problemas";
import type { Drone, Partida, Zumbi } from "./tipos";
import { DRONE, ESCRITORIO, PASSO } from "./valores";
const PASSO_MAX = DRONE.velocidade * PASSO;
const [PROBLEMA_DRONE, PROBLEMA_ZUMBI] = PROBLEMAS;
function partida(x: number, z: number, rumo = 0): Partida {
return {
passo: 0,
rng: 1,
dificuldade: "normal",
problemaId: PROBLEMA_DRONE.id,
hordas: [],
horda: 0,
fase: "horda",
tempoFase: 0,
tetoAVista: 16,
mapa: lerMapa(ESCRITORIO),
jogador: {
x,
z,
rumo,
inclinacao: 0,
vida: 100,
arma: 0,
municao: 90,
recarga: 0,
},
zumbis: [],
fila: [],
drones: [],
caixas: [],
feed: [],
resolvidos: [],
placar: { jogador: 0, drones: 0 },
eventos: [],
proximoId: 1,
};
}
function zumbi(p: Partida, x: number, z: number, vida = 100): Zumbi {
const z1: Zumbi = {
id: p.proximoId++,
tipo: "comum",
problemaId: PROBLEMA_ZUMBI.id,
x,
z,
rumo: 0,
vida,
estado: "andando",
tempo: 0,
};
p.zumbis.push(z1);
return z1;
}
function drone(p: Partida, recarga = 1): Drone {
const d = criarDrone(p, PROBLEMA_DRONE.id);
d.recarga = recarga;
return d;
}
describe("criarDrone", () => {
it("nasce no jogador, com o rumo dele, e entra na lista", () => {
const p = partida(5.5, 8.5, 1.25);
const d = criarDrone(p, PROBLEMA_DRONE.id);
expect(d).toEqual({
id: 1,
problemaId: PROBLEMA_DRONE.id,
x: 5.5,
z: 8.5,
rumo: 1.25,
recarga: DRONE.intervalo,
eliminacoes: 0,
});
expect(p.drones).toEqual([d]);
expect(p.proximoId).toBe(2);
});
});
describe("avancarDrones: movimento", () => {
it("anda no máximo DRONE.velocidade * PASSO por passo e chega ao lugar", () => {
const p = partida(13.5, 8.5);
const d = drone(p, 1000);
for (let i = 0; i < 100; i++) {
const [x, z] = [d.x, d.z];
avancarDrones(p);
expect(Math.hypot(d.x - x, d.z - z)).toBeLessThanOrEqual(
PASSO_MAX + 1e-12,
);
}
expect(d.x).toBeCloseTo(15.5, 9);
expect(d.z).toBeCloseTo(8.5, 9);
});
it("dá um passo cheio quando o lugar está longe", () => {
const p = partida(13.5, 8.5);
const d = drone(p, 1000);
avancarDrones(p);
expect(d.x - 13.5).toBeCloseTo(PASSO_MAX, 9);
expect(d.z).toBeCloseTo(8.5, 9);
});
it("não atravessa parede", () => {
const p = partida(13.5, 15.5, -Math.PI / 2);
const d = drone(p, 1000);
for (let i = 0; i < 100; i++) avancarDrones(p);
expect(d.z).toBeLessThanOrEqual(16 - 0.2);
});
it("três drones ocupam ângulos diferentes em volta do jogador", () => {
const p = partida(24.5, 8.5, 0.4);
const drones = [drone(p, 1000), drone(p, 1000), drone(p, 1000)];
for (let i = 0; i < 200; i++) avancarDrones(p);
drones.forEach((d, i) => {
const a = p.jogador.rumo + Math.PI / 2 + i * ((2 * Math.PI) / 3);
expect(d.x).toBeCloseTo(24.5 + Math.sin(a) * DRONE.distancia, 6);
expect(d.z).toBeCloseTo(8.5 + Math.cos(a) * DRONE.distancia, 6);
});
expect(
Math.hypot(drones[0].x - drones[1].x, drones[0].z - drones[1].z),
).toBeGreaterThan(1);
expect(
Math.hypot(drones[1].x - drones[2].x, drones[1].z - drones[2].z),
).toBeGreaterThan(1);
});
});
describe("avancarDrones: tiro", () => {
it("respeita a recarga: desconta até 0, atira e recarrega", () => {
const p = partida(5.5, 8.5);
const d = drone(p, 3);
const alvo = zumbi(p, 11.5, 8.5);
avancarDrones(p);
avancarDrones(p);
expect(d.recarga).toBe(1);
expect(p.eventos).toEqual([]);
avancarDrones(p);
expect(d.recarga).toBe(DRONE.intervalo);
expect(p.eventos.map((e) => e.tipo)).toEqual(["tiro", "acerto"]);
expect(alvo.vida).toBe(100 - DRONE.dano);
avancarDrones(p);
expect(d.recarga).toBe(DRONE.intervalo - 1);
expect(p.eventos).toHaveLength(2);
expect(p.feed).toEqual([]);
});
it("vira para o zumbi e registra tiro e acerto", () => {
const p = partida(5.5, 8.5);
const d = drone(p);
const alvo = zumbi(p, 5.5, 14.5);
avancarDrones(p);
const distancia = Math.hypot(alvo.x - d.x, alvo.z - d.z);
expect(d.rumo).toBeCloseTo(Math.atan2(alvo.x - d.x, alvo.z - d.z), 9);
expect(p.eventos).toEqual([
{ tipo: "tiro", autor: d.id, arma: "drone", distancia },
{
tipo: "acerto",
zumbiId: alvo.id,
cabeca: false,
x: 5.5,
y: 1.2,
z: 14.5,
},
]);
});
it("escolhe o zumbi mais próximo", () => {
const p = partida(5.5, 8.5);
drone(p);
const longe = zumbi(p, 15.5, 8.5);
const perto = zumbi(p, 9.5, 8.5);
avancarDrones(p);
expect(perto.vida).toBe(100 - DRONE.dano);
expect(longe.vida).toBe(100);
});
it("mantém o mais próximo quando o seguinte está mais longe", () => {
const p = partida(5.5, 8.5);
drone(p);
const perto = zumbi(p, 9.5, 8.5);
const longe = zumbi(p, 15.5, 8.5);
avancarDrones(p);
expect(perto.vida).toBe(100 - DRONE.dano);
expect(longe.vida).toBe(100);
});
it("ignora zumbi morrendo", () => {
const p = partida(5.5, 8.5);
drone(p);
const morto = zumbi(p, 8.5, 8.5);
morto.estado = "morrendo";
const vivo = zumbi(p, 15.5, 8.5);
avancarDrones(p);
expect(morto.vida).toBe(100);
expect(vivo.vida).toBe(100 - DRONE.dano);
});
it("não atira com parede no meio e deixa a recarga em 0", () => {
const p = partida(9.5, 2.5);
const d = drone(p);
const alvo = zumbi(p, 12.5, 2.5);
avancarDrones(p);
expect(alvo.vida).toBe(100);
expect(p.eventos).toEqual([]);
expect(d.recarga).toBe(0);
});
it("não atira além do alcance", () => {
const p = partida(2.5, 8.5);
const d = drone(p);
const alvo = zumbi(p, 2.5 + DRONE.alcance + 1, 8.5);
avancarDrones(p);
expect(alvo.vida).toBe(100);
expect(p.eventos).toEqual([]);
expect(d.recarga).toBe(0);
});
it("a morte gera a linha do feed com a solução do drone e o nome do zumbi", () => {
const p = partida(5.5, 8.5);
drone(p);
const alvo = zumbi(p, 9.5, 8.5, DRONE.dano);
avancarDrones(p);
expect(alvo.estado).toBe("morrendo");
expect(p.feed).toEqual([
{
passo: 0,
texto: `${PROBLEMA_DRONE.solucao} eliminou ${PROBLEMA_ZUMBI.nome}`,
},
]);
});
});