Files
portifolio-evandrodevbr/specs/2026-11-10-jogo-zumbis-etapa-1.md
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

177 lines
30 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Jogo de zumbis do portfólio: etapa 1, trecho jogável
**Autor:** Evandro · **Data:** 2026-11-10 · **Porte:** grande · **Status:** em execução
## 1. Objetivo
Uma página `/jogo` com um jogo de tiro em primeira pessoa em que cada horda de zumbis é um problema de empresa que eu resolvo; horda eliminada vira um aliado com o nome da solução. A etapa 1 entrega um trecho jogável e medido no computador e no S26 Ultra, para decidir com número quanto o jogo pode crescer.
## 2. Escopo
- Dentro: rota `/jogo` (tela inicial, partida, relatório final) e `/jogo/bancada` (medição); um mapa (escritório à noite); um modelo de zumbi realista com 3 variações de textura; 3 comportamentos (comum, rápido, blindado) e o chefão; pistola, automática e faca; 3 drones aliados; feed de eliminações; sangue; efeitos sonoros; controles de teclado e mouse e de toque; a lista dos 27 problemas; créditos dos arquivos.
- Fora (não fazer): botão na entrada do site (etapa 2, depois da medição); outros mapas; comportamentos "divide", "invisível" e "atira de longe"; multiplayer e placar online; música; braços em primeira pessoa; opção sem sangue; arquivos que pedem login (Mixamo, Sketchfab); dependência nova; cartão que cite cliente.
## 3. Critérios de aceite
- CA1 (laço): no Fácil, 3 hordas e o chefão. Ao cair cada horda aparece "Resolvido: <problema>", nasce um drone com o nome da solução e o feed mostra "<solução> eliminou <problema>". O relatório final mostra eliminações da pessoa e dos drones, o link do serviço de cada problema e o WhatsApp com mensagem que cita o problema escolhido → `pnpm test:e2e -- jogo` com piloto automático e semente fixa.
- CA2 (dificuldade): a partida abre no Fácil; Fácil, Normal e Difícil aplicam a tabela da seção 5 → `pnpm test -- lib/jogo/dificuldade`.
- CA3 (armas): pistola com munição infinita; automática gasta e recolhe munição caída; faca só acerta a até 1,5 m → `pnpm test -- lib/jogo/armas`.
- CA4 (celular): em aparelho de toque aparecem direcional, área de mira, troca de arma e faca; a automática atira sozinha com a mira no zumbi; em retrato a página pede para virar o aparelho; nenhum controle fica fora da área visível em 915×412 e 640×360 → e2e com toque emulado e inspeção no S26 Ultra.
- CA5 (desempenho no computador): bancada com 16 zumbis animados à vista, 3 drones e sangue: p95 do quadro ≤ 16,7 ms em 1440×900 no Chromium com GPU; com CPU 4× mais lenta, p95 ≤ 33 ms → `pnpm test:e2e -- jogo-bancada` (local, com GPU).
- CA6 (desempenho no S26 Ultra): mesma bancada no aparelho: p95 ≤ 16,7 ms; em 5 minutos de partida, a média do último minuto fica em até 1,2× a do primeiro → a bancada mostra o resultado na tela e ele vai para o Registro. Bloqueada por P1.
- CA7 (regulador): com p95 > 20 ms por 2 s o jogo reduz, nesta ordem, resolução, sombras e zumbis à vista (mínimo 8); com folga por 10 s, sobe um degrau → `pnpm test -- lib/jogo/regulador` e bancada com CPU lenta.
- CA8 (peso): tudo que baixa antes de jogar soma ≤ 6 MB; cada modelo ≤ 1,5 MB; o zumbi tem ≤ 12 mil triângulos; texturas com lado ≤ 1024 px; abrir `/` não baixa nenhum arquivo de `/jogo` e o LCP da home continua sendo o h1 → `pnpm test -- lib/jogo/orcamento` e e2e de rede.
- CA9 (memória): 3 partidas seguidas sem recarregar: geometrias e texturas vivas (`renderer.info.memory`) iguais às da primeira e heap JS em até 1,1× → e2e na bancada.
- CA10 (caminhos): toda função de `lib/jogo/**` tem complexidade ciclomática ≤ 10 e a cobertura de linhas e de ramos de `lib/jogo/**` é 100% → `pnpm test -- lib/jogo --coverage`.
- CA11 (acesso): sem JavaScript, sem WebGL ou com movimento reduzido a página mostra os 27 problemas em texto com link para o serviço (com movimento reduzido há também "jogar mesmo assim", sem tremor de câmera); aviso de sangue antes de começar; pausa no `Esc` e ao sair da aba; botão de mudo; axe sem violação na tela inicial e no relatório → e2e.
- CA12 (conteúdo e licença): cada problema aponta para um serviço que existe; nenhum texto do jogo cita cliente; cada arquivo de `public/jogo/` consta em `public/jogo/CREDITOS.json` com origem, autor e licença (`CC0-1.0` para o que foi baixado, `propria` para o que foi gerado aqui) → `pnpm test -- lib/jogo/problemas lib/jogo/creditos`.
- CA13: Biome, typecheck, `pnpm test`, build e e2e verdes; `/jogo` e `/jogo/bancada` com `noindex` e fora do sitemap nesta etapa.
## 4. Contexto verificado
- Cena 3D carregada sob demanda: `app/components/cinematic/HeroField.tsx:109` (`await import("./BlocosScene")`). `GLTFLoader` vem de `three/examples/jsm` (`BlocosScene.tsx:25`); `SkeletonUtils`, `PointerLockControls`, `DRACOLoader` e `meshopt_decoder` existem na mesma pasta do three 0.182.
- Orçamento de quadro que já existe: `createFrameBudget(limitMs = 28, frames = 60)` em `lib/blocos.ts:315`. Bloco da marca: `public/3d/bloco.glb` (2,7 KB).
- WhatsApp: `buildWhatsAppUrl(message)` em `lib/whatsapp.ts:17`. Serviços e slugs: `lib/site-content.ts:120-613`. `noindex` de referência: `app/not-found.tsx:21`.
- Jest: `roots` em `app` e `lib`, `coverageProvider: "v8"`, sem `coverageThreshold` (`jest.config.ts:4-6`). O Biome não tem regra de complexidade (`biome.json`). e2e em `e2e/`, sobre a porta 3001 (`playwright.config.ts:10`).
- Blender 5.2.2 instalado; os scripts de modelo ficam em `3d/*.py`.
- Zumbi candidato: "Male City Zombie", de Rikindle3D, CC0, 4,4 MB em `.7z`, baixa sem login (https://opengameart.org/content/male-city-zombie-ready-for-use-in-game-engines); a página lista andar, atacar, morrer, parado, correr e reação. Formato interno e triângulos não conferidos. Não foi achado outro zumbi realista com esqueleto que baixe sem login.
- Texturas e sons CC0 sem conta: Poly Haven, ambientCG, Kenney, OpenGameArt (licenças lidas pelos agentes de pesquisa, não conferidas arquivo a arquivo).
- Não há número medido de quantos personagens animados um celular aguenta no three.js; um relato viu queda acima de uns 20.
## 5. Desenho
- **Lógica separada da tela.** `lib/jogo/` não importa three nem DOM, avança em passo fixo de 1/60 s e sorteia por semente; por isso é testável caminho a caminho e o piloto repete a mesma partida. Interface comum: `criarPartida({ semente, dificuldade, problemaId })`, `avancar(estado, entrada)` e `Entrada = { mover: [x, y], olhar: [dx, dy], atirar: boolean, arma: 0 | 1 | 2 }`.
- **Módulos:** `tipos` e `valores` (contrato e números, abaixo), `mapa` (grade de 1 m escrita em texto, de onde saem as paredes da lógica e as da tela), `partida` (inicio → horda → pausa → chefão → vitória ou derrota), `hordas`, `zumbis` (campo de fluxo em grade, colisão por círculo), `armas` (tiro por raio contra cápsulas), `drones`, `feed`, `dificuldade`, `regulador`, `problemas`, `piloto`, `orcamento`, `creditos`.
- **Contrato da lógica:** plano XZ em metros, Y para cima; `rumo` em radianos, com direção `(sin(rumo), cos(rumo))`. O estado é um objeto só, alterado no lugar por `avancar`; cada passo deixa em `eventos` o que a tela precisa desenhar (tiro, acerto, morte, drone novo, horda resolvida).
- **Valores iniciais:** zumbi comum (vida 100, 1,2 m/s, dano 10), rápido (50, 2,4 m/s, 6), blindado (250, 0,9 m/s, 15), chefão (1500, 1,0 m/s, 25); ataque a 1 m, um por segundo. Pistola (dano 34, 3 tiros/s, munição infinita), automática (20, 10 tiros/s, 90 balas, caixa caída dá 30), faca (100, 2 golpes/s, 1,5 m). Acerto acima de 1,55 m de altura dobra o dano. Drone (dano 25, 2 tiros/s, 15 m, precisa de linha de visão). Pausa de 5 s entre hordas, com +25 de vida.
- **Hordas:** a 1 só tem comuns; a 2, comuns e rápidos; a 3, comuns, rápidos e blindados; o chefão vem com 6 comuns. As três hordas levam o nome de três outros problemas da mesma área do problema escolhido.
- **Feed:** só registra eliminação feita por drone e horda resolvida.
- **Tela:** `app/jogo/` com o canvas three e a interface em HTML por cima. Sem motor de física e sem biblioteca nova.
- **Zumbis:** um GLB clonado com `SkeletonUtils.clone`, com teto de zumbis à vista (16 no nível alto, 8 no baixo); o resto da horda espera na fila. Animação assada em textura com instâncias só entra se a bancada reprovar: não construir antes de medir.
- **Drones:** blocos verdes da marca (`bloco.glb`) agrupados, com o nome da solução por cima. Sem esqueleto, custam pouco.
- **Partida:** hordas de 12, 16 e 20 zumbis e o chefão; o problema escolhido é o chefão e a área dele dá os nomes das hordas. Vida 100.
| Dificuldade | Velocidade | Dano | Quantidade |
|---|---|---|---|
| Fácil (padrão) | 0,6× | 0,4× | 0,7× |
| Normal | 1× | 1× | 1× |
| Difícil | 1,3× | 1,5× | 1,3× |
- **Problemas (27) e o serviço de cada área:**
- Vender e ser encontrado → `criacao-de-sites`: site que ninguém acha no Google; site que não abre direito no celular; site lento; catálogo desatualizado; pedido perdido no WhatsApp; pagamento que falha.
- Atender → `atendimento-whatsapp-ia`: atraso no atendimento; mesma pergunta respondida cem vezes; agenda com horário duplicado; cliente sem retorno.
- Operar → `engenharia-de-software`: planilha duplicada; digitar a mesma coisa em dois lugares; estoque furado; processo que só uma pessoa sabe fazer; controle no papel.
- Fiscal e financeiro → `nota-fiscal-reforma-tributaria`: nota fiscal rejeitada; fechamento do mês que não bate; cobrança esquecida; relatório que demora dias.
- Sistemas e integrações → `arquitetura-de-software` (a máquina vai para `integracao-hardware-software-iot`): sistemas que não conversam; máquina que não conversa com o sistema; sistema antigo em que ninguém mexe; sistema que cai na hora do pico.
- Segurança e dados → `arquitetura-de-software` (o app vai para `/ia`): senha compartilhada; dados sem backup; acesso de ex-funcionário; app feito por IA cheio de falha.
- **Arquivos 3D:** `3d/jogo.py` (Blender sem janela) baixa as fontes para `3d/fontes/` (fora do git), reduz, troca as texturas e exporta para `public/jogo/`. Compressão Draco só se o peso passar do CA8. Variações de textura do zumbi e cartazes do cenário saem da geração de imagem.
- **Bancada:** `/jogo/bancada?zumbis=16&seg=20` roda o piloto, mostra na tela e expõe `window.__bancada = { p50, p95, max, quadros, chamadas, triangulos, heapMB }`.
## 6. Restrições, riscos, suposições
- Restrições: sem dependência nova; nenhum byte do jogo na home; só arquivos CC0 que baixam sem login; sem commit nem push sem pedido.
- R1: só há um zumbi realista sem login, de aparência mediana e formato não conferido. T1 abre e mede antes de tudo; se não servir, o trabalho para e a decisão volta ao Evandro (zumbi simplificado do Quaternius ou liberar login).
- R2: o S26 Ultra é topo de linha; passar nele não garante aparelho médio. Por isso o CA5 mede com CPU 4× mais lenta e o CA7 exige o regulador.
- R3: sangue e zumbi realista num site que vende para dono de empresa. O jogo fica em página própria, com aviso antes de começar e `noindex` nesta etapa.
- R4: Safari no iPhone não será testado (sem aparelho).
- Suposições: S1 no celular o jogo roda em paisagem; S2 nome provisório "Expediente dos Mortos"; S3 o CI não tem GPU, então a bancada roda só local e no aparelho; S4 "testes ciclomáticos" é o CA10; S5 valores da tabela e das hordas são iniciais e se ajustam jogando.
- Perguntas abertas: P1: para o S26 Ultra abrir a bancada é preciso publicar a branch como prévia (push de `feat/jogo-zumbis`, que o CI publica fora de produção). Depende do ok do Evandro.
## 7. Microtasks
### T1 · preparar o zumbi · sênior
- Serve: CA8, CA12 · Depende de: nenhuma · Arquivos: criar `3d/jogo.py`, `public/jogo/zumbi.glb`, `public/jogo/CREDITOS.json`
- Fazer: baixar o candidato, abrir no Blender, relatar formato, triângulos e clipes; reduzir para ≤ 12 mil triângulos e textura de 1024 px; exportar com os clipes `parado`, `andar`, `atacar`, `morrer`. Parar e relatar se o arquivo não servir (R1).
- Pronto quando: `blender -b -P 3d/jogo.py` termina com `OK zumbi`; o GLB tem ≤ 1,5 MB; um render de frente e um de perto estão em `3d/out/`.
- [x] feito
### T2 · bancada de desempenho · sênior
- Serve: CA5, CA6 · Depende de: T1 · Arquivos: criar `app/jogo/bancada/page.tsx`, `app/jogo/Bancada.tsx`, `e2e/jogo-bancada.spec.ts`
- Fazer: cena com N zumbis clonados andando em círculo, 3 drones e luz do mapa; medir o tempo de quadro por `seg` segundos e publicar `window.__bancada`; `noindex`.
- Pronto quando: o e2e lê `p95` com 16 zumbis; o resultado aparece na tela.
- [x] feito
### CP1 · após T1–T2 · direto
- Verificar: CA5 no computador e, com P1 resolvida, CA6 no S26 Ultra. O teto de zumbis à vista sai desta medida e vai para o Registro. Reprovou: parar e rever o desenho com o Evandro.
### T3 · partida, hordas e dificuldade · implementador
- Serve: CA1, CA2 · Depende de: CP1 · Arquivos: criar `lib/jogo/partida.ts`, `lib/jogo/hordas.ts`, `lib/jogo/dificuldade.ts` e os testes
- Fazer: teste primeiro; máquina de estados e tabela da seção 5; `criarPartida` e `avancar` com a interface da seção 5.
- Pronto quando: `pnpm test -- lib/jogo/partida lib/jogo/hordas lib/jogo/dificuldade` verde, com um teste por caminho.
- [x] feito
### T4 · zumbis · implementador [P]
- Serve: CA1 · Depende de: T3 · Arquivos: criar `lib/jogo/zumbis.ts` e o teste
- Fazer: campo de fluxo em grade até o jogador, colisão por círculo, comportamentos comum, rápido, blindado e chefão, teto de zumbis à vista e fila.
- Pronto quando: `pnpm test -- lib/jogo/zumbis` verde.
- [x] feito
### T5 · armas · implementador [P]
- Serve: CA3 · Depende de: T3 · Arquivos: criar `lib/jogo/armas.ts` e o teste
- Fazer: pistola, automática e faca; tiro por raio contra cápsula; munição caída e recolhida.
- Pronto quando: `pnpm test -- lib/jogo/armas` verde.
- [x] feito
### T6 · drones e feed · implementador [P]
- Serve: CA1 · Depende de: T3 · Arquivos: criar `lib/jogo/drones.ts`, `lib/jogo/feed.ts` e os testes
- Fazer: drone nasce quando a horda cai, escolhe o alvo mais próximo, atira em ritmo fixo; o feed guarda as últimas 5 linhas "<solução> eliminou <problema>".
- Pronto quando: `pnpm test -- lib/jogo/drones lib/jogo/feed` verde.
- [x] feito
### T7 · problemas e créditos · implementador [P]
- Serve: CA12 · Depende de: nenhuma · Arquivos: criar `lib/jogo/problemas.ts`, `lib/jogo/creditos.ts` e os testes
- Fazer: os 27 problemas com área, solução e serviço da seção 5; o teste confere cada slug em `lib/site-content.ts` e cada arquivo de `public/jogo/` no `CREDITOS.json`.
- Pronto quando: `pnpm test -- lib/jogo/problemas lib/jogo/creditos` verde.
- [x] feito
### T8 · complexidade e cobertura · implementador
- Serve: CA10 · Depende de: T3–T7 · Arquivos: criar `lib/jogo/complexidade.test.ts`; modificar `jest.config.ts`
- Fazer: teste que percorre `lib/jogo/**` com a API do TypeScript e conta decisões por função (`if`, laços, `case`, `catch`, `?:`, `&&`, `||`, `??`), falhando acima de 10; `coverageThreshold` de 100% em linhas e ramos para `lib/jogo/**`.
- Pronto quando: `pnpm test -- lib/jogo --coverage` verde; uma função de 11 decisões posta de propósito faz o teste falhar.
- [x] feito
### CP2 · após T3–T8 · direto
- Verificar: Biome, typecheck e `pnpm test`; uma partida inteira do piloto termina em vitória no Fácil só com a lógica.
### T9 · cena jogável no computador · sênior
- Serve: CA1 · Depende de: CP2 · Arquivos: criar `app/jogo/page.tsx`, `app/jogo/Jogo.tsx`, `app/jogo/cena.ts`
- Fazer: mapa do escritório, zumbis e drones ligados ao estado da lógica, arma na tela, mira, vida, munição, feed; mouse com `PointerLockControls`, `WASD`, `1 2 3`, `Esc` pausa.
- Pronto quando: uma partida completa é jogável em 1440×900 sem erro no console.
- [x] feito
### T10 · controles de toque · sênior
- Serve: CA4 · Depende de: T9 · Arquivos: criar `app/jogo/toque.ts`, `app/jogo/jogo.css`
- Fazer: direcional à esquerda, arrastar à direita para mirar, tiro automático com a mira no zumbi, botões de arma e faca, aviso de virar o aparelho.
- Pronto quando: o e2e com toque emulado elimina um zumbi; os controles cabem em 915×412 e 640×360.
- [x] feito
### T11 · telas de início e de fim · implementador
- Serve: CA1, CA11 · Depende de: T9 · Arquivos: criar `app/jogo/Inicio.tsx`, `app/jogo/Relatorio.tsx`; modificar `app/jogo/page.tsx`
- Fazer: escolha do problema e da dificuldade, aviso de sangue, mudo; cartão "Resolvido" entre hordas; relatório com links e WhatsApp por `buildWhatsAppUrl`; lista em texto para quem não tem JavaScript, WebGL ou pediu menos movimento.
- Pronto quando: a mensagem do WhatsApp cita o problema escolhido; a lista em texto aparece sem JavaScript.
- [x] feito
### T12 · sangue, som e regulador · sênior
- Serve: CA7, CA9 · Depende de: T9 · Arquivos: criar `lib/jogo/regulador.ts` e o teste; modificar `app/jogo/cena.ts`
- Fazer: partículas e marcas de sangue com teto de 64; sons CC0 liberados no primeiro toque; regulador do CA7; descarte de geometrias e texturas ao fim da partida.
- Pronto quando: `pnpm test -- lib/jogo/regulador` verde; a bancada com CPU lenta desce de nível sozinha.
- [x] feito
### T13 · testes de ponta a ponta · implementador
- Serve: CA1, CA4, CA8, CA9, CA11, CA13 · Depende de: T10–T12 · Arquivos: criar `e2e/jogo.spec.ts`, `lib/jogo/orcamento.test.ts`; modificar `app/sitemap.test.ts`
- Fazer: partida com piloto e semente fixa; toque; rede da home sem arquivo de `/jogo`; 3 partidas e memória; axe; pesos de `public/jogo/`; `/jogo` fora do sitemap.
- Pronto quando: `pnpm test:e2e -- jogo` e `pnpm test -- lib/jogo/orcamento` verdes.
- [x] feito
### CP3 · após T9–T13 · direto
- Verificar: todos os CAs; parecer do `fable-critico` sobre o laço de renderização e o descarte de memória; partida no S26 Ultra pelo Evandro.
## 8. Registro
2026-11-10 — spec escrita a partir das decisões do Evandro: primeira pessoa, jogável no celular desde o início, zumbi realista com sangue, só arquivos sem login, drones como aliados, Fácil por padrão, chefão escolhido pela pessoa, S26 Ultra como aparelho de teste.
2026-11-10 — a licença aceita em `CREDITOS.json` passou a ser `CC0-1.0` ou `propria`, porque texturas e cartazes gerados aqui não têm origem externa.
2026-11-10 — aprovada pelo Evandro ("pode"), incluindo o push da branch como prévia quando a bancada estiver pronta (P1).
2026-11-10 — ordem da lógica revista: ela não depende do resultado da bancada (se a medição reprovar, muda a tela, não as regras), então corre em paralelo com T1 e T2. O contrato (`lib/jogo/tipos.ts`, `lib/jogo/valores.ts`) foi escrito antes; a base (`mapa`, `rng`) e `hordas` com `feed` saem primeiro, depois `zumbis` com `dificuldade`, depois `armas` e `drones`, e por último `partida` com `piloto`. O nascimento dos zumbis fica em `zumbis`, não em `hordas`.
2026-11-10 — T7 entregue: 27 problemas e a conferência de créditos, 17 testes, 100% de linhas e ramos. O teste roda no jsdom (o `jest.setup.ts` não carrega no ambiente node).
2026-11-10 — T1 entregue: `public/jogo/zumbi.glb` com 11.950 triângulos, 1.459.472 bytes, 66 ossos, 1,80 m, clipes `parado`, `andar`, `atacar`, `morrer`, `correr`, `reacao`; texturas de cor e normal em JPEG 1024. Aparência (R1): proporção humana boa, mas a textura original é uma crosta ruidosa igual na pele e na roupa; não passa por realista de perto. Tentativa de conserto dentro do combinado: repintar com texturas geradas pela assinatura GPT (T1b), que também dá as 3 variações. Se não ficar bom, a decisão volta para o Evandro (liberar download com login ou trocar de estilo).
2026-11-10 — os ossos do zumbi têm o prefixo `mixamorig:`, ou seja, o autor montou esqueleto e animações no Mixamo antes de publicar como CC0. A Adobe permite usar personagens e animações do Mixamo em projetos comerciais, jogos incluídos (helpx.adobe.com/creative-cloud/faq/mixamo-faq.html); o uso aqui (embutido no jogo, não redistribuído solto) cabe nisso. O arquivo foi baixado do OpenGameArt sem login.
2026-11-10 — T4, T5, T6 e T8 entregues. `pnpm test:jogo` (script novo, com limite de 100% de cobertura) entrou no job `verify` do CI. O regulador de qualidade (parte de lógica da T12) saiu antes como `lib/jogo/regulador.ts`, com os degraus em `valores.ts`.
2026-11-10 — a partida inteira do piloto pegou um defeito do desenho: a regra "com linha de visão o zumbi anda reto" prendia o zumbi atrás das mesas, que são sólidas mas deixam ver por cima. Correção: andar reto só com caminho livre de sólidos; senão, campo de fluxo mirando o centro da próxima célula, sem cortar quina.
2026-11-10 — contrato da tela escrito em `app/jogo/contrato.ts` (`Escolha`, `Retrato`, `Resultado`, `Controle`); controles e telas correm em paralelo com a bancada. T10 ganhou `lib/jogo/alavanca.ts` e `alvoNaMira` em `armas.ts` (o tiro automático do toque do CA4 precisa saber se a mira está num zumbi). Nomes de arquivo mudaram em relação à seção 7: `app/jogo/controle.ts` e `Toque.tsx` no lugar de `toque.ts`; `Hud.tsx` e `ListaDeProblemas.tsx` a mais.
2026-11-10 — causa real dos zumbis presos: seguindo o campo de fluxo, o corpo (raio 0,35) raspava a quina da mesa. `direcaoNoCampo` passou a mirar o centro da próxima célula e o zumbi só anda reto com `caminhoLivre`. Partida do piloto com semente 1 no Fácil: vitória no passo 6.703 (cerca de 1 min 52 s), placar 23 × 17, vida 100. O piloto mira sem errar, então uma pessoa leva mais tempo; a duração e o dano ficam para ajustar depois de jogar de verdade.
2026-11-10 — o chefão (raio 0,6) nascia encostado na parede e não andava. Decisão: para andar, todo zumbi usa no máximo `RAIO_DE_PASSAGEM` (0,45 m); para levar tiro e atacar, vale o raio do tipo.
2026-11-10 — a cena saiu em duas partes com interface fixa: T9a (`cena.ts`, `ambiente.ts`, `atores.ts`) e T9b (`page.tsx`, `Jogo.tsx`); a parte de sangue e som da T12 virou `efeitos.ts` e `som.ts`. Acréscimo ao desenho: cada zumbi leva o nome do problema acima da cabeça e cada drone o nome da solução, para a ideia ficar legível sem depender só do feed.
2026-11-10 — T2 entregue. Bancada no computador (RTX 5060 Ti, `next dev`, 1440×900, Chromium com GPU): 16 zumbis p50/p95/máx 16,7/16,7/16,8 ms, 50 chamadas, 85 mil triângulos; 24 zumbis 16,7/16,7/16,8 ms; com CPU 4× mais lenta, 16 zumbis 16,7/16,8/33,4 ms e 24 zumbis 16,7/33,4/100 ms. CA5 atendido no computador. O tempo entre quadros trava nos 60 Hz do monitor e não mostra a folga; a bancada vai ganhar a medida do trabalho por quadro. Desvio: o Playwright não aceita `launchOptions` por bloco, então o caso com GPU abre o próprio Chromium.
2026-11-10 — CP2: `pnpm test:jogo` com 253 testes, 100% de linhas e ramos, nenhuma função acima de 10 decisões. Piloto com semente 1: Fácil vitória no passo 5.988 (24 × 16), Normal 5.003 (37 × 18), Difícil 4.662 (46 × 24), sempre com vida 100. O piloto não erra e recua sempre; não serve de medida de dificuldade para gente.
2026-11-10 — sons: 10 arquivos MP3 mono (1,7 a 18 KB cada) de pacotes CC0 da Kenney e do OpenGameArt (kurt, EZduzziteh, AntumDeluge), todos em `CREDITOS.json`. Um pacote foi recusado porque a página dizia CC0 e o zip trazia CC-BY. Ninguém ouviu os sons ainda: foram escolhidos por nome, duração e pico.
2026-11-10 — `efeitos.ts` e `som.ts` entregues (22 testes): arma feita de caixas, clarão, traçantes, 64 gotas e 64 marcas de sangue em reserva fixa, 5 chamadas de desenho. `page.tsx` e `Jogo.tsx` entregues (16 testes), página sem h1 próprio (o h1 é da tela inicial e do relatório).
2026-11-10 — a primeira versão da tela inicial ficou fraca (27 opções numa coluna só, botão "Começar" 1.600 px abaixo, sem imagem). Redesenho em andamento: duas colunas no computador, barra fixa com "Começar" no celular, imagem de abertura renderizada do próprio zumbi (`public/jogo/capa.jpg`).
2026-11-10 — T1b entregue: cor refeita por bake no Blender com as 4 texturas geradas (pele, camisa, calça, máscara de sangue), normal original a 0,3, sem `KHR_materials_specular`, materiais `roupa`, `corpo`, `olhos`; três roupas (A no GLB, `roupa-b.jpg` 111 KB, `roupa-c.jpg` 64 KB); `capa.jpg` 83 KB. `zumbi.glb` com 1.465.268 bytes e 11.950 triângulos. Aparência: passa por zumbi crível a 2–3 m no escuro, não por fotorrealista; o molde original ainda aparece na calça curta, nos pés descalços, na barriga à mostra e na costura do ombro. R1 fica aberto para o Evandro decidir depois de ver no jogo.
2026-11-10 — bancada com a medida de trabalho por quadro: 16 zumbis p95 2,8 ms (10,9 ms com CPU 4× mais lenta); 24 zumbis 4,5 ms (10,5 ms). Memória da bancada: geometrias 5 e texturas 55 iguais na 1ª e na 3ª medição. `capa.jpg` (1600 × 900) ficou fora do limite de 1024 px do CA8, que vale para textura; ganhou limite próprio de 1600 px e 220 KB.
2026-11-10 — T9 entregue: `cena.ts`, `ambiente.ts`, `atores.ts`. No navegador com GPU, o piloto vence no Fácil (passo 5.988) sem erro no console; p95 de 16,7 ms e 26 chamadas de desenho no meio da horda (máximo medido 36 no Fácil e 49 no Difícil). Desvio da interface: `criarAmbiente` recebe `{ camera, movimentoReduzido }` e expõe `atualizar`. Conferência estática do conjunto: tsc rc=0, Biome limpo em 357 arquivos, 363 testes de `app/jogo` e `lib/jogo` passando.
2026-11-10 — primeira olhada na cena: funciona, mas a arma é um bloco preto com faces brancas, os rótulos dos drones tomam a tela de perto, os dos zumbis se empilham e fora da lanterna fica escuro demais. Polimento em andamento, junto com o parecer do `fable-critico` e os testes de ponta a ponta.
2026-11-10 — parecer do `fable-critico` (só leitura, CP3): sem vazamento achado em 3 partidas; passo fixo correto (teto de 5 passos, sem salto na volta da pausa). Um defeito que bloqueia: o regulador media o intervalo entre quadros, que a 60 Hz nunca fica abaixo de 16,7 ms, então nunca voltava a subir, e numa tela limitada a 30 quadros desceria até o fim sem ganho. Decisão: o regulador passa a receber também o tempo de trabalho do quadro; desce pelo intervalo (p95 > 20 ms, como no CA7), sobe quando o intervalo está dentro do limite e o trabalho tem p95 abaixo de 6 ms por 10 s, e duas descidas seguidas sem ganho de 10% fazem voltar e travar. Outros achados em conserto: perda de contexto WebGL sem tratamento, um esqueleto por malha em cada zumbi (3 vezes o custo de ossos), interpolação com 2 passos no mesmo quadro, som que não volta no iOS ao sair da aba, clique rápido perdido entre dois passos, alocação por quadro. O CA9 media `renderer.info.memory` entre renderers diferentes, o que não prova nada: a cena passa a publicar o que sobrou depois do descarte (esperado 0 geometrias e 0 texturas).
2026-11-10 — consertos do parecer, parte 1: as 3 malhas de cada zumbi passaram a dividir um esqueleto (com 16 zumbis e CPU 4× mais lenta, o trabalho por quadro caiu de 20,9 para 12,4 ms no p95 e as texturas de 56 para 24); clone reaproveitado começa do zero; `liberar()` retoma o som sempre que o contexto não está rodando; clique ou toque inteiro entre dois passos vira um tiro. 106 testes de `app/jogo`, tsc e bancada (4 casos) verdes.
2026-11-10 — polimento visual entregue e conferido nas capturas: armas refeitas (0,8% a 3,2% da tela), rótulos de tamanho fixo e só nos 3 zumbis mais próximos e no chefão, drones a 2,3 m, sala visível a 12 m, portas com luz vermelha nos nascedouros. Ainda feio: pilares lisos e armas feitas de caixas. Em 915 × 412 as eliminações cobrem os botões e o aviso cobre a mira (a corrigir no HUD).
2026-11-10 — o Evandro pediu o refino (ferramentas abertas por CLI, mapa novo, itens, golpe sincronizado, ataque mais rápido): virou a spec `2026-11-10-jogo-zumbis-etapa-2-refino.md`, com execução autônoma. Esta etapa fecha antes de a lógica nova entrar.
2026-11-10 — T13b entregue: `e2e/jogo.spec.ts` com 12 casos; 11 passam (CA1, CA4 em 915 × 412, 640 × 360 e retrato, CA8, CA11, CA13). O caso do CA9 falha por dois defeitos do jogo: heap em 1,113× e 1,117× entre a 1ª e a 3ª partida (limite 1,1×) e, com semente aleatória, o piloto fica mais de 5 minutos na horda 3 com "faltam 2". Os dois estão em conserto (o segundo com varredura de 300 sementes por dificuldade). Decisões do teste: no CA8, "arquivo do jogo" é o bloco que contém `__jogo` (a home já baixa o three pelo fundo 3D); no CA11 a pausa é conferida com `exitPointerLock`, porque o `Esc` simulado não solta a trava do mouse.
2026-11-10 — Partida que empacava: achada e consertada. Causa: dois zumbis chegavam juntos a uma passagem de 1 m entre mesas e a separação empurrava cada um metade da sobreposição, um contra cada mesa; nenhum passava e o jogador, sem linha de visão, esperava (Fácil, semente 4, horda 3, "faltam 2" correto). Conserto em `separarPar` (`lib/jogo/zumbis.ts`): só o zumbi mais longe do jogador recua, e recua a sobreposição inteira. Varredura de 300 sementes × 3 dificuldades: 900 partidas terminam, todas em vitória, com vida média do piloto de 100 (o piloto não apanha; não diz nada sobre a dificuldade para uma pessoa, S5). Teste permanente novo com 40 sementes por dificuldade (leva de 19 a 23 s; reduzir na etapa 2). `pnpm test:jogo`: 277 testes, 100% nas quatro colunas.
2026-11-10 — Consertos do parecer crítico, parte 2, feitos: regulador mede intervalo e trabalho (duas descidas sem ganho voltam os degraus e travam; nível inicial pelo `pixelRatio` do aparelho); contexto WebGL perdido pausa a partida e "Continuar" volta a valer depois de restaurar; `guardarPasso` a cada passo; sem vetor novo por passo; carga cancelada não cria renderer; `sobras` = 0 geometrias e 0 texturas nas 3 partidas (faltava soltar a textura `DFG_LUT`, que o three não liberava); "Continuar" chama `som.liberar()`. Heap entre a 1ª e a 3ª partida: 1,096× (limite 1,1×), medido com coleta forçada. Playwright: 16 passaram (bancada e jogo). Observado com CPU 4× mais lenta: o nível desceu, não houve ganho, voltou e travou, como desenhado; não foi visto subir de volta.
2026-11-10 — Ponto de controle antes da lógica da etapa 2: Biome limpo (386 arquivos), typecheck rc=0, `pnpm test` 2071 testes, `pnpm test:jogo` 278 testes com 100% nas quatro colunas, build rc=0. Commit e push da prévia feitos neste estado. Desvio: o `impeccable critique` e o `audit` da etapa 1 passam para o fechamento da etapa 2, sobre a tela final, porque o refino já mudou HUD, sons e clipe de ataque.