Skip to content

feat(duelo-yugioh): modo speed duel estilo Duel Links - #31

Merged
mcsscalabrin merged 6 commits into
mcsscalabrin:mainfrom
benogoulart:feat/modo-yugioh
Aug 15, 2026
Merged

feat(duelo-yugioh): modo speed duel estilo Duel Links#31
mcsscalabrin merged 6 commits into
mcsscalabrin:mainfrom
benogoulart:feat/modo-yugioh

Conversation

@benogoulart

@benogoulart benogoulart commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

What changed, and why

Esta branch traz o Speed Duel — o modo estilo Yu-Gi-Oh que o produto pedia —, a navegação que o liga ao duelo existente e uma reescrita do arrastar-e-soltar da nova arena.

Speed Duel (modo Yu-Gi-Oh)

O produto já tinha dois confrontos: a batalha (um lance único de pontuação) e o duelo (por turnos, dirigido pelo visitante). O Speed Duel é o terceiro e o mais jogável: uma partida estilo Duel Links entre dois logins do GitHub numa mesa 3D, cada lado com LP, um deck de monstros/feitiços/armadilhas sorteado do elenco de perfis (lib/ygo/roster.ts) e fases draw/main/battle/end com janela de armadilha.

A restrição importante é como a partida é decidida. A arena roda o mesmo motor do servidor em lockstep: lib/ygo/engine.ts é executado no navegador do visitante e de novo em POST /api/ygo, na mesma semente e consumindo o PRNG na mesma ordem. O visitante é sempre o lado A; o lado B é a IA (aiAct, mesma heurística e disciplina de RNG do duelo). Ao fim, as ações do visitante são enviadas uma única vez, re-executadas e persistidas no servidor, e a arena redireciona para o resultado estável /ygo/<id> — replay (components/ygo/YgoReplay.tsx) e pôster (lib/og/renderYgo.tsx, app/api/ygo/[ygoId]/image) seguem as convenções do duelo v2, com cache immutable no pôster.

Duel v2

O modo foi construído sobre o sistema de duelo v2, que entra junto neste mesmo PR (motor, árbitro, board, replay e pôster em lib/duel/, components/duel/, app/duel/). O v2 é dirigido: o visitante escolhe a cada turno, a IA responde num timer, e os dois lados são perfis reais do GitHub.

Navegação entre modos

  • Um duelo ao vivo (/duel/<a>/vs/<b>) ganhou o link "Versão Yu-Gi-Oh" para o mesmo confronto; a arena mostra "Versão Duelo" de volta — só quando os dois lados são do elenco (ROSTER_LOGINS), porque a arena ygo sorteia do pool do elenco enquanto o duelo aceita qualquer perfil do GitHub.
  • As páginas de resultado de batalha e de duelo linkam para a versão Yu-Gi-Oh do mesmo confronto.
  • O formulário de batalha da home ganhou um toggle pill (Batalha | Yu-Gi-Oh) que roteia para /ygo/<a>/vs/<b>.
  • Novas chaves i18n em pt e en (home.modeBattle, home.modeYgo, battle.asYgo, ygo.asDuel).

Arrastar-e-soltar da arena (reescrita + duas correções)

A primeira versão da arena usava drag nativo do HTML5, e havia dois problemas: o summon ignorava a zona do drop (o motor pegava a primeira vaga livre) e as cartas eram arrastáveis fora da vez do jogador (nada acendia, parecia morto). A ação summon ganhou um zone opcional, que o motor honra com fallback para a IA/ações legadas; o arrasto passou a ser liberado por canDrag = myTurn && !trapWindow.

A mudança maior: o draggable do HTML5 foi substituído por pointer events. O pointerdown inicia o arrasto com captura de ponteiro, uma carta-fantasma segue o cursor, o alvo é resolvido via elementFromPoint contra .ygo-zone[data-side][data-type][data-zone], e zonas válidas acendem. O pointerup distingue clique (deslocamento < 6px) de arrasto, e o summon abre o seletor de posição já ligado à zona escolhida. Isso também é o que faz a arena funcionar em toque, onde o drag nativo do HTML5 não existe.

What you saw

A arena é 3D e sensível a layout, então segue o que conferir:

  • A arena: campo do oponente espelhado ao fundo, campo do jogador na frente, HUD flutuante (avatares, LP, decks/cemitérios, barra de fases), mão embaixo. [screenshot]
  • O arrasto: a fantasma segue o cursor, zonas válidas acendem, soltar um monstro abre o seletor de posição; um clique ainda abre o painel da carta. [screenshot]
  • O mode-switch: as barras "Versão Yu-Gi-Oh"/"Versão Duelo" nos dois boards ao vivo e o toggle da home. [screenshot]

Para dirigir: npm run dev em /ygo/<a>/vs/<b>, /duel/<a>/vs/<b> e /.

Checks

  • npm run lint
  • npm run typecheck
  • npm test (263 passaram)
  • Alterou o formato da carta? Não — as cartas do elenco são um tipo próprio do ygo; lib/cards/index.ts / CARD_VERSION intactos.
  • Alterou fórmula de pontuação? Não — o scoring de batalha (RFC 6.1/6.2) segue inalterado.
  • Adicionou chave i18n? Sim — em pt e en (o typecheck exige isso).

@benogoulart

Copy link
Copy Markdown
Contributor Author
Captura de tela 2026-08-15 011525

@mcsscalabrin

Copy link
Copy Markdown
Owner

que genial.

@mcsscalabrin
mcsscalabrin merged commit 1802a92 into mcsscalabrin:main Aug 15, 2026
5 checks passed
mcsscalabrin added a commit that referenced this pull request Aug 15, 2026
Conflito semantico de merge, nao conflito textual: o PR #31 entrou na main em
paralelo trazendo dois fixtures de Card, e o campo `axis` desta branch os
invalidou. Arquivos diferentes, entao o git mergeou limpo e so o tsc percebeu.

E so isso mesmo. Nada em lib/duel/ nem em lib/ygo/ le `Card.axis` -- o unico
`.axis` de la e o `AxisRating.axis` do radar, em duel/engine.ts, que e outra
coisa. E as rotas obtem carta por getProfileCard, que popula o campo. Por isso
os 277 testes passavam com o tipo quebrado: o Vitest transpila sem checar tipo,
e em execucao o campo sempre esteve la.
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.

2 participants