Skip to content

feat: add medals, music, sound effects and the narrator - #14

Merged
NicolasArthurDev merged 43 commits into
devfrom
feat/medals-music-and-sound
Sep 14, 2026
Merged

NicolasArthurDev merged 43 commits into
devfrom
feat/medals-music-and-sound

Conversation

@NicolasArthurDev

@NicolasArthurDev NicolasArthurDev commented Sep 13, 2026 •

Copy link
Copy Markdown
Member

Fecha o que faltava entre o material pronto e o jogo: as dezessete medalhas
desenhadas, as três músicas, os nove efeitos e as quatro vozes do narrador
estavam no repositório e nenhum deles chegava ao navegador.

docs/medals.md já era a especificação completa, e KillEvent/applyKill já
tinham sido desenhados para recebê-la — o docblock de applyKill previa
literalmente applyKill(...) + awardMedals(...).

medalhas

  • as dezessete, uma função pura por medalha em packages/core/src/medals/,
    com teste de mesa. As cinco que dependem de mecânica ausente (knife,
    backstab, on-the-rope, collateral, 360-no-scope) entram com a regra
    escrita e inerte: o campo que elas leem fica no valor neutro, e
    killEvent.ts diz em cada um qual mecânica o destrava.
  • awardMedals roda antes de applyKill, e a ordem é a decisão que
    importa: as condições leem a sequência da vítima, a contagem da partida e a
    janela de multikill, e applyKill muda as três.
  • a escala é a de match.pointsPerKill, que já era 100. docs/medals.md
    fora escrito supondo 10; o documento foi corrigido, e a frase do GDD que as
    medalhas revogam também.

o hud, em dois lugares

  • a medalha celebra no topo central: só a arte, até 320 px, sem texto e sem
    número. Uma de cada vez — três medalhas na mesma kill entram em fila, e a
    fila é atraso de css, não relógio em javascript.
  • a retícula leva o registro: +350 na diagonal de cima, com o nome do que
    o rendeu logo abaixo. É o que explica um total que não é múltiplo de kill.

uma animação por raridade

Não é o mesmo efeito com número maior — são quatro coisas diferentes:

raridade verbo o que acontece
comum carimbo um objeto sólido é pousado. Sem luz nenhuma, e sai cedo
incomum reflexo o mesmo carimbo, e depois dele a luz pega no metal uma vez
rara impacto acelera para dentro (ease-in), para seco, estoura de luz e solta uma onda de choque
lendária forja a silhueta surge como luz pura, o metal esfria dentro dela, e ela sobe enquanto esfria

A primeira versão disto foi reprovada por parecer igual nos quatro, e a lição
está no documento: o olho trava no pixel mais brilhante e mais rápido, e
esse era idêntico em todos. Escala e halo são canais terciários.

Duas mecânicas caem daí: a luz se recorta na silhueta do escudo
(mask-image sobre o mesmo webp), porque de 49% a 66% da caixa do ícone é arte
e o resto é transparência; e ela soma em vez de pintar por cima
(mix-blend-mode: plus-lighter), o que mantém a filigrana do metal visível
enquanto ele esquenta.

áudio

  • mixer Web Audio com três canais, ganho em curva quadrática, e a música
    abaixando enquanto o narrador fala.
  • música: Chrome_Perimeter no menu, Blackout_Velocity na partida,
    Protocol_Seven no último minuto — casada com a fala match-final-minute —,
    e de volta ao tema do menu no fim.
  • narrador: as quatro vozes escolhíveis, vega no padrão. Fila com cooldown
    e corte por raridade: nunca duas falas sobrepostas.
  • efeitos: por borda entre quadros, sem estado novo na simulação.

a sincronia saiu de medição, não de ouvido

Envelope de energia a cada 20 ms sobre cada wav de origem:

  • shot-reload é tiro e ferrolho no mesmo take — tiro em 0,1 s, cliques do
    ferrolho de 0,9 s a 1,6 s. Como boltCycleS é 1,3 s, o último clique caía
    0,3 s depois de o ferrolho já ter fechado. O tiro passou para o
    shot-sniper, que é seco, e o ferrolho virou disparo à parte recortado a
    partir de 0,9 s e centrado na janela.
  • reload tem a inserção do carregador no pico, a 1,02 s, num arquivo de
    1,39 s dentro de uma recarga de 2,4 s: soava com o carregador ainda saindo.
    Atrasado para cair nos sete décimos do gesto.
  • andar é o take de correr a 0,69×, razão tirada da configuração. O take
    próprio de andar soava mal e saiu da lista de pré-carga.

Os atrasos derivam de boltCycleS e reloadS: afinar qualquer um deles na
configuração reacompanha o som sozinho (ADR 0005), e há teste travando que os
cliques cabem na janela do ferrolho e que a inserção nunca cai depois do fim da
recarga. O agendamento usa o relógio do AudioContext e não setTimeout —
um timer de javascript erra dezenas de milissegundos quando o quadro atrasa, e
é disso que o alinhamento depende.

configurações

Volume de música, efeitos e narrador, mais a escolha de voz. A voz é o primeiro
ajuste de escolha do jogo, e coube num campo opcional choices no
PlayerSettingSpec — faixa, passo e "para nas pontas" continuam valendo sem
nenhum caso especial.

assets

Dois scripts novos no molde dos que já existiam, e o registro de licença que a
ADR 0004 exige:

conjunto de para
17 ícones 35 MB de png a ~1250 px 860 kB de webp a 384 px
3 músicas 7,0 MB de mp3 192 kbps 3,5 MB de opus 96 kbps
9 efeitos 4,0 MB de wav 180 kB de opus mono 64 kbps

Os ícones foram 128 px primeiro, e estava errado: aquele número vinha de um
toast de 48 px, e com o toast em 236 virou upscale de 1,7×.

orçamento do pilar 2

Nada de áudio nem de ícone entra no caminho crítico: a música toca por
streaming, os efeitos são pré-carregados no primeiro gesto do jogador, só a voz
escolhida é baixada, e os ícones entram depois que o jogador ganha o controle.
Medido contra o dev:

caminho crítico    387,9 kB  ->  392,4 kB gzip   (+4,5 kB, e é código)

scripts/measure-bundle.mjs não enxergava .webm nem .opus; passa a
enxergar, senão o áudio cresceria invisível para a medição de que depende o
gatilho de revisão da ADR 0001.

verificação

  • npm run verify verde, 100% de cobertura mantida (1068 testes).
  • Revisão de código sobre o diff inteiro; os dez achados estão tratados e
    listados em comentário neste PR.
  • Conferido no navegador: menu, ajustes, destravamento do áudio no primeiro
    gesto, pré-carga dos efeitos, manifesto do narrador, música tocando, as
    quatro animações de medalha e o registro da retícula.
  • Os efeitos sonoros foram ouvidos e ajustados a partir do retorno de quem
    jogou.

fora de escopo, de propósito

Tela de fim de partida, placar do tab com pontos, e as mecânicas que destravam
as cinco medalhas inertes.

@NicolasArthurDev

Copy link
Copy Markdown
Member Author

Revisão de código passada sobre o diff inteiro, com o verify verde antes e depois. Dez achados, todos tratados:

# achado commit
1 a escada de multikill pagava errado quando um degrau era pulado. As janelas não são encaixadas (5, 8, 12, 15 s), então kills em 0, 1, 2, 14 e 15 s alcançam o kill-chain sem nunca terem alcançado o overkill — e descontar o degrau imediatamente anterior pagava 250 pelo que a docs/medals.md fixa em 400. Passa a descontar o que a sequência já pagou, guardado em PlayerScore.multiKillPaid ca141ac
2 payback nunca era baixada: lastKilledById só é escrita na morte, então dois jogadores trocando kills levavam o bônus em todas elas 834570a
3 matchTimeS andava desde o primeiro quadro renderizado, que é o boot. Cinco minutos parado no menu e o jogador entrava numa partida que o relógio já dava por encerrada — com a música do menu e sem o aviso de último minuto bb62f83
4 a apresentação do narrador era descartada quando o primeiro gesto do jogador chegava antes do manifesto da voz 414d4ef
5 falha ao carregar o manifesto era engolida sem sinal: pasta de voz renomeada virava narrador mudo sem explicação 414d4ef
6 new AudioContext() lançando dentro do tratador de teclado travava o menu inteiro. Jogo mudo é aceitável; jogo que não entra não é edabf1a
7 o feed reescrevia a pilha a cada medalha, então um prêmio antigo voltava ao centro da tela replicando a animação — e era reanunciado pelo leitor de tela 6c46c8d
8 medalIconUrl duplicada em dois módulos 278465f
9 docblock do specFor apagado sem querer na troca da tabela de ajustes 9479904
10 nome de teste com os marcadores trocados 9479904

Os dois que a revisão achou enquanto o branch andava — o relógio de cruzamento da música calando a faixa que entrava, e stop() num laço de passo que nunca começou — já tinham sido corrigidos em 3cf2ad4 e fdf78d9.

@NicolasArthurDev
NicolasArthurDev force-pushed the feat/medals-music-and-sound branch from c9ecd20 to b3207aa Compare September 14, 2026 00:02
@NicolasArthurDev
NicolasArthurDev merged commit 2a4acbd into dev Sep 14, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant