@the-inclusionist/engine 7.0.1 → 8.0.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/README.md +5 -3
  2. package/app/css/style.css +22 -5
  3. package/app/public/vendor/fonts/fondamento-400-ext.woff2 +0 -0
  4. package/app/public/vendor/fonts/fondamento-400.woff2 +0 -0
  5. package/app/public/vendor/fonts/opendyslexic-400.woff2 +0 -0
  6. package/app/public/vendor/fonts/playwrite-ar.woff2 +0 -0
  7. package/app/public/vendor/fonts/playwrite-br.woff2 +0 -0
  8. package/app/public/vendor/fonts/playwrite-ca.woff2 +0 -0
  9. package/app/public/vendor/fonts/playwrite-cl.woff2 +0 -0
  10. package/app/public/vendor/fonts/playwrite-co.woff2 +0 -0
  11. package/app/public/vendor/fonts/playwrite-mx.woff2 +0 -0
  12. package/app/public/vendor/fonts/playwrite-us-modern.woff2 +0 -0
  13. package/app/public/vendor/fonts/playwrite-us-trad.woff2 +0 -0
  14. package/app/public/vendor/fonts/pressstart-400.woff2 +0 -0
  15. package/app/public/vendor/fonts.css +64 -2
  16. package/dist-pkg/boot/create-game.d.ts +107 -4
  17. package/dist-pkg/boot/create-game.js +337 -18
  18. package/dist-pkg/core/constants.d.ts +0 -28
  19. package/dist-pkg/core/constants.js +34 -17
  20. package/dist-pkg/core/contract.d.ts +99 -0
  21. package/dist-pkg/core/contract.js +78 -0
  22. package/dist-pkg/core/entity.d.ts +47 -4
  23. package/dist-pkg/core/layers.d.ts +18 -0
  24. package/dist-pkg/core/layers.js +18 -0
  25. package/dist-pkg/core/rng.js +14 -4
  26. package/dist-pkg/core/route.d.ts +42 -0
  27. package/dist-pkg/core/route.js +158 -0
  28. package/dist-pkg/core/state.d.ts +10 -1
  29. package/dist-pkg/core/state.js +13 -0
  30. package/dist-pkg/educational/adaptive-engine.d.ts +65 -0
  31. package/dist-pkg/educational/adaptive-engine.js +117 -0
  32. package/dist-pkg/educational/segment-bar.d.ts +96 -0
  33. package/dist-pkg/educational/segment-bar.js +89 -0
  34. package/dist-pkg/i18n/en.js +44 -0
  35. package/dist-pkg/i18n/es.js +44 -0
  36. package/dist-pkg/i18n/pt.js +60 -0
  37. package/dist-pkg/input/default-bindings.d.ts +40 -0
  38. package/dist-pkg/input/default-bindings.js +151 -9
  39. package/dist-pkg/input/gamepad.d.ts +33 -12
  40. package/dist-pkg/input/gamepad.js +103 -16
  41. package/dist-pkg/input/keyboard-runtime.d.ts +6 -8
  42. package/dist-pkg/input/keyboard-runtime.js +24 -5
  43. package/dist-pkg/input/keyboard.d.ts +42 -0
  44. package/dist-pkg/input/keyboard.js +87 -17
  45. package/dist-pkg/input/keydown.d.ts +40 -2
  46. package/dist-pkg/input/keydown.js +29 -6
  47. package/dist-pkg/input/latch-edge.d.ts +28 -0
  48. package/dist-pkg/input/latch-edge.js +45 -0
  49. package/dist-pkg/input/latch-scope.d.ts +70 -0
  50. package/dist-pkg/input/latch-scope.js +110 -0
  51. package/dist-pkg/input/latch-store.d.ts +45 -0
  52. package/dist-pkg/input/latch-store.js +74 -0
  53. package/dist-pkg/input/latch-sync.d.ts +47 -0
  54. package/dist-pkg/input/latch-sync.js +43 -0
  55. package/dist-pkg/input/origem-sintetica.d.ts +44 -0
  56. package/dist-pkg/input/origem-sintetica.js +60 -0
  57. package/dist-pkg/input/pad-defaults.d.ts +22 -0
  58. package/dist-pkg/input/pad-defaults.js +34 -0
  59. package/dist-pkg/input/pointer.d.ts +61 -0
  60. package/dist-pkg/input/pointer.js +71 -0
  61. package/dist-pkg/input/state.d.ts +86 -1
  62. package/dist-pkg/input/state.js +128 -1
  63. package/dist-pkg/input/touch-bindings.d.ts +21 -2
  64. package/dist-pkg/input/touch-bindings.js +15 -3
  65. package/dist-pkg/input/touch.js +9 -1
  66. package/dist-pkg/input/transporte-em-uso.d.ts +101 -0
  67. package/dist-pkg/input/transporte-em-uso.js +130 -0
  68. package/dist-pkg/input/transports.d.ts +88 -2
  69. package/dist-pkg/input/transports.js +60 -5
  70. package/dist-pkg/input/vocabulary-migration.d.ts +18 -7
  71. package/dist-pkg/platform/audio-earcons.d.ts +29 -2
  72. package/dist-pkg/platform/audio-earcons.js +10 -0
  73. package/dist-pkg/platform/audio-nav.d.ts +1 -1
  74. package/dist-pkg/platform/audio-sonar.d.ts +149 -9
  75. package/dist-pkg/platform/audio-sonar.js +232 -21
  76. package/dist-pkg/platform/guide-intensity.d.ts +40 -0
  77. package/dist-pkg/platform/guide-intensity.js +73 -0
  78. package/dist-pkg/platform/pesados-catalogo.d.ts +14 -0
  79. package/dist-pkg/platform/pesados-catalogo.js +177 -0
  80. package/dist-pkg/platform/pesados.d.ts +31 -0
  81. package/dist-pkg/platform/pesados.js +75 -0
  82. package/dist-pkg/platform/storage.d.ts +30 -0
  83. package/dist-pkg/platform/storage.js +35 -0
  84. package/dist-pkg/platform/tts.js +16 -1
  85. package/dist-pkg/platform/voice-plan.d.ts +109 -0
  86. package/dist-pkg/platform/voice-plan.js +156 -0
  87. package/dist-pkg/platform/vozes-prontas.d.ts +28 -0
  88. package/dist-pkg/platform/vozes-prontas.js +61 -0
  89. package/dist-pkg/render/draw.d.ts +1 -1
  90. package/dist-pkg/render/draw.js +15 -5
  91. package/dist-pkg/render/viz-axes.d.ts +17 -0
  92. package/dist-pkg/render/viz-axes.js +19 -0
  93. package/dist-pkg/render/viz-setters.d.ts +34 -2
  94. package/dist-pkg/render/viz-setters.js +189 -33
  95. package/dist-pkg/render/wheelchair-sprites.d.ts +11 -3
  96. package/dist-pkg/render/wheelchair-sprites.js +10 -4
  97. package/dist-pkg/ui/dom.js +20 -2
  98. package/dist-pkg/ui/fonts.d.ts +71 -1
  99. package/dist-pkg/ui/fonts.js +111 -8
  100. package/dist-pkg/ui/latch-refusal.d.ts +32 -0
  101. package/dist-pkg/ui/latch-refusal.js +60 -0
  102. package/dist-pkg/ui/layout.d.ts +32 -0
  103. package/dist-pkg/ui/layout.js +64 -1
  104. package/dist-pkg/ui/motion-scene.d.ts +41 -0
  105. package/dist-pkg/ui/motion-scene.js +76 -0
  106. package/dist-pkg/ui/panel-shell.d.ts +53 -0
  107. package/dist-pkg/ui/panel-shell.js +103 -0
  108. package/dist-pkg/ui/pause-icons.d.ts +168 -20
  109. package/dist-pkg/ui/pause-icons.js +315 -45
  110. package/dist-pkg/ui/reach-notice.js +8 -0
  111. package/dist-pkg/ui/settings-audio.d.ts +14 -1
  112. package/dist-pkg/ui/settings-audio.js +43 -3
  113. package/dist-pkg/ui/settings-controls.d.ts +66 -6
  114. package/dist-pkg/ui/settings-controls.js +179 -17
  115. package/dist-pkg/ui/settings-empathy.d.ts +15 -0
  116. package/dist-pkg/ui/settings-empathy.js +2 -0
  117. package/dist-pkg/ui/settings-motion.d.ts +25 -19
  118. package/dist-pkg/ui/settings-motion.js +32 -19
  119. package/dist-pkg/ui/settings-motor.d.ts +101 -3
  120. package/dist-pkg/ui/settings-motor.js +136 -9
  121. package/dist-pkg/ui/settings-typo.d.ts +49 -5
  122. package/dist-pkg/ui/settings-typo.js +74 -23
  123. package/dist-pkg/ui/settings-visual.d.ts +21 -1
  124. package/dist-pkg/ui/settings-visual.js +48 -4
  125. package/dist-pkg/ui/shell.d.ts +13 -3
  126. package/dist-pkg/ui/shell.js +3 -4
  127. package/dist-pkg/ui/simulation-refusal.d.ts +32 -0
  128. package/dist-pkg/ui/simulation-refusal.js +57 -0
  129. package/dist-pkg/ui/visual-axes-panel.d.ts +48 -0
  130. package/dist-pkg/ui/visual-axes-panel.js +95 -0
  131. package/dist-pkg/ui/webcam.js +6 -1
  132. package/docs/CREDITS.md +22 -1
  133. package/docs/LICENSES.md +170 -132
  134. package/package.json +25 -4
  135. package/app/public/vendor/fonts/greatvibes-400.woff2 +0 -0
  136. package/app/public/vendor/fonts/ufcook-700.woff2 +0 -0
@@ -168,6 +168,10 @@ const pt = {
168
168
  'touch.dir.cross': 'cruz (D-pad)',
169
169
  'touch.dir.stick': 'analógico',
170
170
  'sr.touch.slotSet': '{slot}: {acao}.',
171
+ // ⚠️ SEM O NOME DA POSIÇÃO, de propósito: é uma que este jogo não nomeia, e o id interno (`action3`)
172
+ // não pode chegar a uma criança (ADR-0074). O anúncio EXISTE na mesma — quem navega por ouvido precisa de
173
+ // saber que a escolha aterrou. Gémeo do `sr.ctrl.keyTakenHereUnnamed`, que resolveu o mesmo em `7742ac0`.
174
+ 'sr.touch.slotSetUnnamed': '{slot}: definido.',
171
175
  'sr.touch.dirSet': 'Direcional: {tipo}.',
172
176
  'sr.touch.presetChild': 'Controles no tamanho de mão de criança (6 a 12 anos).',
173
177
  'sr.touch.presetAdult': 'Controles no tamanho de mão de adulto.',
@@ -182,9 +186,17 @@ const pt = {
182
186
  'act.especial': 'Especial',
183
187
  'key.space': 'Espaço',
184
188
  'ctrl.change': 'Alterar',
189
+ 'ctrl.pressing': 'Pressione…',
190
+ 'ctrl.editingYours': 'Editando o seu controle — modo {modo}.',
191
+ 'ctrl.mode.one': '1 jogador',
192
+ 'ctrl.mode.many': '{n} jogadores',
185
193
  'ctrl.changeKeyAria': 'Alterar tecla de {acao} do Jogador {n}',
186
194
  'sr.ctrl.pressNewKey': 'Pressione a nova tecla para {acao} do Jogador {n}, ou Esc para cancelar.',
187
195
  'sr.ctrl.keyTaken': 'Essa tecla já é do Jogador {n}. Escolha outra, ou Esc para cancelar.',
196
+ 'sr.ctrl.keyTakenHere': 'Essa tecla já é de {acao}. Escolha outra, ou Esc para cancelar.',
197
+ // ⚠️ SEM O NOME DA POSIÇÃO, de propósito: ela é uma que este jogo não nomeia, e o id interno (`action2`)
198
+ // não pode chegar a uma criança (ADR-0074). A frase diz a verdade que interessa — a tecla está ocupada.
199
+ 'sr.ctrl.keyTakenHereUnnamed': 'Essa tecla já está em uso neste controle. Escolha outra, ou Esc para cancelar.',
188
200
  'sr.ctrl.reset': 'Controles restaurados ao padrão.',
189
201
  // Painel de áudio (#audio) — saídas por jogador, som/narração, bengala, motores de voz.
190
202
  'audio.playerN': 'Jogador {n}',
@@ -274,6 +286,11 @@ const pt = {
274
286
  'pad.glyph.circle': 'bola',
275
287
  'pad.glyph.square': 'quadrado',
276
288
  'pad.glyph.triangle': 'triângulo',
289
+ 'pad.wiz.step': '{n} de {total} — aperte: {acao}',
290
+ 'pad.wiz.mapped': 'Mapeados: {lista}',
291
+ 'pad.wiz.pressAny': 'Aperte QUALQUER botão no controle que deseja mapear.',
292
+ 'pad.wiz.detected': 'Controle novo detectado: {id}. O jogo pausou para você configurá-lo. SOLTE tudo para começar.',
293
+ 'pad.wiz.releaseAll': 'Controle: {id}. Agora SOLTE tudo.',
277
294
  'menu.legendSpoken': 'Botão {sim} para confirmar, botão {nao} para voltar.',
278
295
  'sr.menu.fractionsIntro': 'Soma e subtração de frações: escolha a notação e o tipo.',
279
296
  'sr.menu.keepOneNotation': 'Deixe ao menos uma notação ligada.',
@@ -302,6 +319,21 @@ const pt = {
302
319
  // conclui que o jogo está partido. Estas frases existem para ela saber ANTES, e o que fazer.
303
320
  'reach.titulo': 'Este jogo usa {pedidas} ações.',
304
321
  'reach.curto': 'O {transporte} tem {lugares} lugares — não chegam para todas.',
322
+ // ⚠️ Diz os DOIS números, porque é a diferença entre eles que a criança (ou quem a acompanha) pode
323
+ // resolver. «Falta alguma coisa» manda procurar sem dizer o quê; «segura 2 e este jogo pede 3» diz que a
324
+ // saída é outro controle, e diz porquê.
325
+ 'reach.naoSegura': 'O {transporte} segura {segura} botões de cada vez, e este jogo pede {pedidas} ao mesmo tempo.',
326
+ // ⚠️ A RECUSA DA SIMULAÇÃO (#104 / ADR-0076). Ela diz um FACTO SOBRE A DEMONSTRAÇÃO e o caminho de volta —
327
+ // nunca «desligue isso». Quem ligou o alto contraste ligou-o porque precisa dele para ver.
328
+ 'eixo.tema.titulo': 'Tema (contraste)',
329
+ 'eixo.tema.padrao': 'Tema padrão',
330
+ 'eixo.correcao.titulo': 'Correção de cor',
331
+ 'eixo.correcao.tricro': 'Visão tricromática',
332
+ 'viz.escolher': 'Selecionar',
333
+ 'viz.escolhido': '✓ Selecionado',
334
+ 'sim.indisponivel.tema': 'Para ver a simulação, o tema precisa estar no padrão: por cima do alto contraste ela mostraria o que o tema faz, e não o que a deficiência faz.',
335
+ 'sim.indisponivel.correcao': 'Para ver a simulação, a correção de cor precisa estar em visão tricromática: de uma tela já corrigida ela não mostraria nem a deficiência nem a correção.',
336
+ 'sim.indisponivel.ambos': 'Para ver a simulação, o tema e a correção de cor precisam estar no padrão: por cima de um ajuste, a demonstração mostra o ajuste e não a deficiência.',
305
337
  'reach.ligue': 'Ligue {saida} e você joga com todas.',
306
338
  // Quando NADA resolveria: mandar ligar um controle seria mandar procurar o que não conserta.
307
339
  'reach.semSaida': 'Nenhum controle deste aparelho alcança todas. Dá para jogar assim mesmo, mas algumas ações vão ficar sem lugar.',
@@ -379,6 +411,8 @@ const pt = {
379
411
  'sr.quiz.wellDone': 'Muito bem! {palavra}. {n} de 3.',
380
412
  'sr.quiz.correctSoFar': 'Acertou! {n} de 3 para ganhar a moeda.',
381
413
  'menu.restoreDefaults': 'Restaurar padrões deste menu',
414
+ 'menu.close': 'Fechar',
415
+ 'menu.typo': 'Tipografia',
382
416
  'sr.audio.reset': 'Acessibilidade auditiva restaurada aos padrões. Os outros menus não mudaram.',
383
417
  'sr.empathy.reset': 'Modo empatia restaurado aos padrões: simulações desligadas. Correções de daltonismo e os outros menus não mudaram.',
384
418
  'sr.motor.reset': 'Modo Fácil e movimento por alternância restaurados aos padrões. O controle pelos olhos e o mapeamento de teclas continuam como estavam.',
@@ -488,6 +522,13 @@ const pt = {
488
522
  'sr.motor.easyOff': 'Modo Fácil desligado.',
489
523
  'sr.motor.toggleMoveOn': 'Movimento por alternância ligado: toque a direção para andar sem segurar; toque de novo para parar; segure para ir mais rápido. O pulo não interrompe a caminhada.',
490
524
  'sr.motor.toggleMoveOff': 'Movimento por alternância desligado.',
525
+ // ⚠️ O MOTIVO É UM FACTO SOBRE O APARELHO, NUNCA UMA REPREENSÃO (ADR-0113 cláusula 3). Estas frases
526
+ // dizem por que o botão não responde; «não desligue isto» repreenderia uma criança por mexer num ajuste
527
+ // de que ela depende. QUATRO chaves e não uma com `{aparelho}`: «os gestos» é plural e «o olhar» não.
528
+ 'alt.exigida.olhos': 'O controle por olhar precisa das teclas de alternância para funcionar.',
529
+ 'alt.exigida.rosto': 'O controle por rosto precisa das teclas de alternância para funcionar.',
530
+ 'alt.exigida.gestos': 'Os gestos precisam das teclas de alternância para funcionar.',
531
+ 'alt.exigida.fala': 'O comando de voz precisa das teclas de alternância para funcionar.',
491
532
  'sr.motor.toggleRunOn': 'Alternância do correr ligada.',
492
533
  'sr.motor.toggleRunOff': 'Alternância do correr desligada.',
493
534
  'sr.physics.runLatchOn': 'Correndo.',
@@ -601,18 +642,37 @@ const pt = {
601
642
  // Os NOMES das fontes (Atkinson Hyperlegible, Lexend, …) NÃO entram aqui: são nomes próprios e ficam no
602
643
  // catálogo, como os nomes dos motores de voz. O que traduz é o nome do GRUPO e a descrição — que é o texto
603
644
  // que explica à criança (ou a quem a acompanha) POR QUE aquela fonte existe na lista.
645
+ 'font.grupo.rotulo': 'Família de letra',
604
646
  'font.group.sans': 'Sem serifa',
605
647
  'font.group.serif': 'Serifada',
606
648
  'font.group.hand': 'Manuscrita',
649
+ 'font.group.arcade': 'Arcade',
607
650
  'font.desc.atkinson': 'feita pelo Braille Institute para pessoas com baixa visão (padrão do jogo)',
608
651
  'font.desc.lexend': 'feita para reduzir stress visual e atender pessoas disléxicas (ativa o espaçamento extra)',
609
652
  'font.desc.quattro': 'criada para diminuir a fadiga visual de quem passa muito tempo na tela',
610
653
  'font.desc.andika': 'baseada na Sassoon; fruto de pesquisa sobre como crianças leem e escrevem',
654
+ 'font.desc.opendyslexic': 'Letras com a base mais pesada, para não virarem de cabeça para baixo ao ler.',
655
+ 'font.desc.fondamento': 'Caligráfica de pena, para as atividades de escrita à mão.',
656
+ 'font.desc.pw.br': 'Cursiva escolar do Brasil.',
657
+ 'font.desc.pw.ustrad': 'Cursiva escolar dos EUA — tradicional.',
658
+ 'font.desc.pw.usmod': 'Cursiva escolar dos EUA — moderna.',
659
+ 'font.desc.pw.ca': 'Cursiva escolar do Canadá.',
660
+ 'font.desc.pw.mx': 'Cursiva escolar do México.',
661
+ 'font.desc.pw.ar': 'Cursiva escolar da Argentina.',
662
+ 'font.desc.pw.cl': 'Cursiva escolar do Chile.',
663
+ 'font.desc.pw.co': 'Cursiva escolar da Colômbia.',
664
+ 'font.desc.ronde': 'Ronde francesa, a letra de mão que se ensina na escola.',
665
+ // ⚠️ AS TRÊS PELO NOME, e não «uma fonte ronde» (ADR-0108 §4): um adulto não consegue agir sobre uma
666
+ // categoria. A frase existe para ser executável — abrir o navegador, procurar UM destes três nomes,
667
+ // instalar. Nomear a categoria seria a mesma linha morta que este catálogo já removeu duas vezes.
668
+ 'font.off.ronde': 'Instale no aparelho uma destas três: Ronde Script, OPTIFrench-Script ou Merveille. '
669
+ + 'Elas são gratuitas para uso pessoal, e por isso não podem vir dentro do jogo.',
611
670
  'font.desc.greatvibes': 'caligráfica inglesa',
612
671
  'font.desc.pinyon': 'caligráfica inglesa',
613
672
  'font.desc.ufcook': 'blackletter alemã',
614
673
  'font.desc.ufmag': 'blackletter alemã',
615
674
  'font.desc.comicneue': 'bola e bastão (alfabetização)',
675
+ 'font.desc.pressstart': 'pixel de 8 bits (HUD e título do jogo)',
616
676
  'font.desc.learningcurve': 'cursiva inglesa',
617
677
  'font.desc.kindergarten': 'cursiva brasileira',
618
678
  'font.off.pending': 'licença a confirmar — ainda não embarcada',
@@ -7,6 +7,35 @@ export type Binding<T> = T | null;
7
7
  * layout ABNT2/US e é a razão de nunca se usar `key` aqui.
8
8
  */
9
9
  export declare const KEYBOARD_SOLO: Readonly<Record<Action, Binding<readonly string[]>>>;
10
+ /**
11
+ * Teclado, esquema de DOIS JOGADORES — as quatorze posições para cada um (ADR-0096).
12
+ *
13
+ * ⚠️ O JOGADOR 1 AQUI NÃO É O `KEYBOARD_SOLO`, e a diferença é UMA e obrigatória: as SETAS saem dele. No solo
14
+ * elas são um segundo caminho para o direcional; em dupla são o direcional DO OUTRO. Deixá-las nos dois faria
15
+ * os dois bonecos andarem juntos — um defeito que não dá erro em lado nenhum e que só se vê jogando a dois.
16
+ * `conflitosEntreTabelas` existe por causa desta linha.
17
+ *
18
+ * ⚠️ E A GEOMETRIA DO JOGADOR 2 É A MESMA DO JOGADOR 1, TRANSPOSTA PARA O TECLADO NUMÉRICO — o que faz a
19
+ * memória muscular atravessar de um lado da mesa para o outro:
20
+ *
21
+ * 7 8 / * ombros na linha DE CIMA
22
+ * Y U I O 7 8 9 + gatilhos nas PONTAS da linha das ações
23
+ * J K 5 6
24
+ *
25
+ * action1 U ↔ Numpad8 (cima-esquerda) action4 I ↔ Numpad9 (cima-direita)
26
+ * action2 J ↔ Numpad5 (baixo-esquerda) action3 K ↔ Numpad6 (baixo-direita)
27
+ *
28
+ * O bloco `U I / J K` e o bloco `8 9 / 5 6` têm a MESMA forma, então a rotação de 45° que o cabeçalho deste
29
+ * ficheiro descreve para o Xbox vale igual para o jogador 2. Não é coincidência de teclado: é o que torna o
30
+ * padrão ensinável uma vez só.
31
+ *
32
+ * ⚠️ `Digit7`/`Digit8` (jogador 1) e `Numpad7` (jogador 2) SÃO TECLAS DIFERENTES, e é por isso que a
33
+ * especificação do Dev diz «alphanumeric» e «numeric» em tantas palavras. `KeyboardEvent.code` distingue-as
34
+ * sempre; `key` não — com Num Lock desligado o numérico chega como `ArrowUp`/`Home`, e um esquema lido por
35
+ * `key` juntaria o direcional do jogador 2 com as ações dele. Mais uma razão de este ficheiro só falar
36
+ * `code`.
37
+ */
38
+ export declare const KEYBOARD_DUO: readonly Readonly<Record<Action, Binding<readonly string[]>>>[];
10
39
  /**
11
40
  * Gamepad, mapa PADRÃO da Gamepad API (`mapping: "standard"`), que é o que um controle de Xbox reporta.
12
41
  * O número é o índice em `gamepad.buttons`.
@@ -20,5 +49,16 @@ export declare const GAMEPAD_STANDARD: Readonly<Record<Action, Binding<number>>>
20
49
  * ação dupla intermitente que ninguém consegue reproduzir de propósito.
21
50
  */
22
51
  export declare function bindingProblems<T>(tabela: Readonly<Record<Action, Binding<T | readonly T[]>>>): string[];
52
+ /**
53
+ * As teclas que DUAS OU MAIS tabelas reclamam para si. VAZIA quer dizer que os jogadores não se atropelam.
54
+ *
55
+ * ⚠️ ISTO É UM PROBLEMA DIFERENTE DO `bindingProblems`, E FOI POR ISSO QUE PRECISOU DE FUNÇÃO PRÓPRIA: aquele
56
+ * olha UMA tabela e apanha a mesma tecla em duas ações; este olha DUAS tabelas e apanha a mesma tecla em dois
57
+ * JOGADORES. Cada esquema do `KEYBOARD_DUO` passa no primeiro sozinho — e as setas, se ficassem nos dois,
58
+ * fariam os dois bonecos andarem juntos sem que nada reprovasse.
59
+ *
60
+ * A mensagem nomeia os dois donos, porque «tecla repetida» manda procurar o que a função já sabe.
61
+ */
62
+ export declare function conflitosEntreTabelas<T>(tabelas: readonly Readonly<Record<Action, Binding<T | readonly T[]>>>[]): string[];
23
63
  /** As ações que este transporte NÃO alcança. É o que uma tela de seleção precisa dizer ANTES de a criança começar. */
24
64
  export declare function unreachable<T>(tabela: Readonly<Record<Action, Binding<T | readonly T[]>>>): Action[];
@@ -6,10 +6,28 @@
6
6
  // físicos — L1, R2, X, A — são BINDING e vivem no transporte, não no vocabulário. Este é o ficheiro onde eles
7
7
  // vivem. Um transporte novo (fala, olhar, toque) traz a sua tabela e não toca em `core/actions`.
8
8
  //
9
- // ⚠️ NADA AQUI ESTÁ LIGADO AINDA. `input/gamepad` e `input/keyboard` continuam a falar os oito nomes de
10
- // plataforma (`jump`, `run`, `swap`, `especial`); a migração é a issue #103. Esta tabela é o destino dela,
11
- // escrita agora porque o Dev especificou o padrão — e uma especificação que fica no chat é uma decisão
12
- // perdida.
9
+ // ✅ AS TRÊS ESTÃO LIGADAS, e esta nota já mentiu duas vezes — cada versão dela era verdade no dia em que foi
10
+ // escrita e deixou de ser sem ninguém a corrigir. A primeira dizia «NADA AQUI ESTÁ LIGADO AINDA» (morreu com a
11
+ // issue #103, 06/09); a segunda dizia que só o gamepad estava, e morreu com a issue #118, que uniu as duas
12
+ // tabelas de teclado. 📏 Medido em 2026-09-09:
13
+ //
14
+ // · `GAMEPAD_STANDARD` — `input/gamepad.ts:129` lê os índices desta tabela em vez de literais, e a ligação
15
+ // apanhou uma discordância real: `action1` corria em X, R1 e R2 enquanto a tabela declarava R1 e R2 como
16
+ // ombro e gatilho.
17
+ // · `KEYBOARD_SOLO` / `KEYBOARD_DUO` — `input/keyboard.ts:73` constrói o `KB_DEFAULTS` a partir delas
18
+ // (`vivo(KEYBOARD_SOLO)` e `KEYBOARD_DUO.map(vivo)`). Deixaram de ser duas listas paralelas: são a MESMA
19
+ // decisão, derivada, e por isso a divergência de antes não pode voltar por esquecimento.
20
+ //
21
+ // ⚠️ E A DERIVAÇÃO É POR CÓPIA PROFUNDA (`vivo` faz JSON round-trip), em DUAS camadas — verificado antes de
22
+ // escrito, porque a primeira versão desta linha dizia mais do que é verdade. `input/keyboard.ts` copia estas
23
+ // tabelas para o `KB_DEFAULTS`, e copia OUTRA VEZ para o `kb` mutável. O remapeamento da criança muta o `kb`
24
+ // («remapear uma tecla MUTA o objeto», diz o `setKB`), logo ele já não alcançaria isto nem sem a primeira
25
+ // cópia. O que a primeira camada compra é o resto: `KB_DEFAULTS` é exportado, e sem ela qualquer consumidor
26
+ // que lhe escrevesse dentro alterava o padrão de fábrica desta tabela para toda a gente.
27
+ //
28
+ // 📌 A LIÇÃO QUE ESTA NOTA CARREGA AGORA É SOBRE SI PRÓPRIA: um comentário que descreve estado de ligação
29
+ // apodrece a cada entrega. Este diz a DATA da medição, para que a próxima pessoa saiba contra o que a
30
+ // comparar em vez de acreditar.
13
31
  //
14
32
  // ========================= A SIMETRIA DO TECLADO, QUE NÃO É DECORAÇÃO =========================
15
33
  // O padrão que o Dev especificou apoia-se num bloco do QWERTY:
@@ -28,11 +46,18 @@
28
46
  // O gate deste ficheiro reprova tecla repetida, então o erro não teria passado de qualquer forma; o que a
29
47
  // simetria deu foi a tecla CERTA em vez de só a notícia de que havia uma errada.
30
48
  import { ACTIONS } from '../core/actions.js';
31
- // ⚠️ UMA TECLA QUE EXISTE HOJE E NÃO TEM LUGAR NESTA TABELA: `Space`. O esquema solo de `input/keyboard.ts`
32
- // tem `jump:['KeyJ','Space']` — a barra é um segundo atalho para o pulo desde sempre. A especificação do
33
- // padrão não a menciona, e pô-la em `action2` por conta própria seria decidir que o pulo mora ali: se o
34
- // preset da plataforma puser o pulo noutra posição, a barra segue o verbo errado, que é pior do que ela não
35
- // existir. Fica de fora, dita aqui, e é decisão do Dev — não de quem escreve a tabela.
49
+ // ⚠️ A `Space` ESTÁ EM `action2`, E ISTO É A HISTÓRIA DE COMO LÁ FOI PARAR — porque este bloco dizia o
50
+ // contrário e sobreviveu à decisão que o revogou.
51
+ //
52
+ // Ela ficou de fora da primeira versão desta tabela, e a razão estava certa: a barra é o segundo atalho do
53
+ // pulo desde sempre, a especificação do padrão não a mencionava, e pô-la em `action2` por conta própria
54
+ // seria decidir que o pulo mora ali. Se o preset da plataforma puser o pulo noutra posição, a barra segue o
55
+ // verbo errado — pior do que ela não existir.
56
+ //
57
+ // O Dev decidiu (ADR-0086 §2) que o pulo mora em `action2`, e a barra voltou. O texto acima continuou a
58
+ // dizer «fica de fora» durante todo esse tempo, ao lado de duas linhas que a declaram. Corrigido em
59
+ // 2026-09-07, na mesma passagem da issue #118 — que trata exactamente desta classe de erro: um comentário
60
+ // que descreve um estado que já passou.
36
61
  /**
37
62
  * Teclado, esquema SOLO. Códigos de `KeyboardEvent.code` — físicos, não a letra impressa, que muda com o
38
63
  * layout ABNT2/US e é a razão de nunca se usar `key` aqui.
@@ -58,6 +83,92 @@ export const KEYBOARD_SOLO = {
58
83
  start: ['KeyH', 'Enter'],
59
84
  select: ['KeyF'],
60
85
  };
86
+ /**
87
+ * Teclado, esquema de DOIS JOGADORES — as quatorze posições para cada um (ADR-0096).
88
+ *
89
+ * ⚠️ O JOGADOR 1 AQUI NÃO É O `KEYBOARD_SOLO`, e a diferença é UMA e obrigatória: as SETAS saem dele. No solo
90
+ * elas são um segundo caminho para o direcional; em dupla são o direcional DO OUTRO. Deixá-las nos dois faria
91
+ * os dois bonecos andarem juntos — um defeito que não dá erro em lado nenhum e que só se vê jogando a dois.
92
+ * `conflitosEntreTabelas` existe por causa desta linha.
93
+ *
94
+ * ⚠️ E A GEOMETRIA DO JOGADOR 2 É A MESMA DO JOGADOR 1, TRANSPOSTA PARA O TECLADO NUMÉRICO — o que faz a
95
+ * memória muscular atravessar de um lado da mesa para o outro:
96
+ *
97
+ * 7 8 / * ombros na linha DE CIMA
98
+ * Y U I O 7 8 9 + gatilhos nas PONTAS da linha das ações
99
+ * J K 5 6
100
+ *
101
+ * action1 U ↔ Numpad8 (cima-esquerda) action4 I ↔ Numpad9 (cima-direita)
102
+ * action2 J ↔ Numpad5 (baixo-esquerda) action3 K ↔ Numpad6 (baixo-direita)
103
+ *
104
+ * O bloco `U I / J K` e o bloco `8 9 / 5 6` têm a MESMA forma, então a rotação de 45° que o cabeçalho deste
105
+ * ficheiro descreve para o Xbox vale igual para o jogador 2. Não é coincidência de teclado: é o que torna o
106
+ * padrão ensinável uma vez só.
107
+ *
108
+ * ⚠️ `Digit7`/`Digit8` (jogador 1) e `Numpad7` (jogador 2) SÃO TECLAS DIFERENTES, e é por isso que a
109
+ * especificação do Dev diz «alphanumeric» e «numeric» em tantas palavras. `KeyboardEvent.code` distingue-as
110
+ * sempre; `key` não — com Num Lock desligado o numérico chega como `ArrowUp`/`Home`, e um esquema lido por
111
+ * `key` juntaria o direcional do jogador 2 com as ações dele. Mais uma razão de este ficheiro só falar
112
+ * `code`.
113
+ */
114
+ export const KEYBOARD_DUO = Object.freeze([
115
+ {
116
+ // JOGADOR 1 — a mão esquerda anda (WASD), a direita age (UIJK). Sem as setas: são do jogador 2.
117
+ up: ['KeyW'],
118
+ down: ['KeyS'],
119
+ left: ['KeyA'],
120
+ right: ['KeyD'],
121
+ action1: ['KeyU'],
122
+ action2: ['KeyJ', 'Space'],
123
+ action3: ['KeyK'],
124
+ action4: ['KeyI'],
125
+ leftShoulder: ['Digit7'],
126
+ leftTrigger: ['KeyY'],
127
+ rightShoulder: ['Digit8'],
128
+ rightTrigger: ['KeyO'],
129
+ start: ['KeyH', 'Enter'],
130
+ select: ['KeyF'],
131
+ },
132
+ {
133
+ // JOGADOR 2 — as setas andam, o teclado numérico age. `Enter` fica com o jogador 1; o `NumpadEnter` não
134
+ // entra aqui de propósito, porque `ui/menu-nav` já o usa como CONFIRMAR em qualquer menu (KEY_YES) e
135
+ // `input/keydown` registra, verbatim, que ele NÃO pausa. Dar-lhe um terceiro trabalho seria sobrepor.
136
+ up: ['ArrowUp'],
137
+ down: ['ArrowDown'],
138
+ left: ['ArrowLeft'],
139
+ right: ['ArrowRight'],
140
+ action1: ['Numpad8'],
141
+ action2: ['Numpad5'],
142
+ action3: ['Numpad6'],
143
+ action4: ['Numpad9'],
144
+ leftShoulder: ['NumpadDivide'], // a tecla `/` do bloco numérico
145
+ leftTrigger: ['Numpad7'],
146
+ rightShoulder: ['NumpadMultiply'], // a tecla `*`
147
+ rightTrigger: ['NumpadAdd'], // a tecla `+`
148
+ /**
149
+ * ⚠️ `ShiftRight` É A PORTA DE SAÍDA, E ELA FALTAVA (#122).
150
+ *
151
+ * Um Chromebook não tem bloco numérico, e um Chromebook é o hardware que o pilar 1 nomeia. Medido em
152
+ * 2026-09-07: das catorze posições deste assento, **DEZ só se alcançam pelo numpad** — as oito ações
153
+ * mais o `start` e o `select`. A criança da segunda cadeira anda pelas setas, não age em nada, e **não
154
+ * consegue abrir o menu para consertar**, porque a tecla que abre o menu está no mesmo bloco que falta.
155
+ *
156
+ * Isso é o que a auditoria chamou de «um padrão do qual a criança não escapa», e o conserto que ela pede
157
+ * é textual: *«tornar o próprio padrão remapeável, e não trocar as teclas que ele escolheu».* As teclas
158
+ * ficam — o layout do numpad é melhor onde ele existe, é um bloco físico sob uma mão, e não tira nada ao
159
+ * primeiro jogador. O que entra é UMA porta que todo teclado tem, para o remapeamento ser alcançável.
160
+ *
161
+ * ⚠️ E É SÓ NO `start`, de propósito: da pausa alcança-se a tela de remapeamento, e de lá TODAS as outras
162
+ * treze posições. Uma porta chega para escapar; cada padrão a mais é uma tecla tirada do bolo comum, e
163
+ * este teclado já é repartido por duas crianças.
164
+ *
165
+ * `ShiftRight` porque existe em todo o teclado, fica ao lado do bloco de setas (a mesma mão que já as
166
+ * usa), não é tecla de texto — e está LIVRE nas quatro tabelas, o que foi medido e não suposto.
167
+ */
168
+ start: ['Numpad1', 'ShiftRight'],
169
+ select: ['Numpad0'],
170
+ },
171
+ ]);
61
172
  /**
62
173
  * Gamepad, mapa PADRÃO da Gamepad API (`mapping: "standard"`), que é o que um controle de Xbox reporta.
63
174
  * O número é o índice em `gamepad.buttons`.
@@ -116,6 +227,37 @@ export function bindingProblems(tabela) {
116
227
  }
117
228
  return p;
118
229
  }
230
+ /**
231
+ * As teclas que DUAS OU MAIS tabelas reclamam para si. VAZIA quer dizer que os jogadores não se atropelam.
232
+ *
233
+ * ⚠️ ISTO É UM PROBLEMA DIFERENTE DO `bindingProblems`, E FOI POR ISSO QUE PRECISOU DE FUNÇÃO PRÓPRIA: aquele
234
+ * olha UMA tabela e apanha a mesma tecla em duas ações; este olha DUAS tabelas e apanha a mesma tecla em dois
235
+ * JOGADORES. Cada esquema do `KEYBOARD_DUO` passa no primeiro sozinho — e as setas, se ficassem nos dois,
236
+ * fariam os dois bonecos andarem juntos sem que nada reprovasse.
237
+ *
238
+ * A mensagem nomeia os dois donos, porque «tecla repetida» manda procurar o que a função já sabe.
239
+ */
240
+ export function conflitosEntreTabelas(tabelas) {
241
+ const p = [];
242
+ const dono = new Map();
243
+ tabelas.forEach((tabela, i) => {
244
+ for (const acao of ACTIONS) {
245
+ const v = tabela[acao];
246
+ if (v === null || v === undefined)
247
+ continue;
248
+ for (const item of (Array.isArray(v) ? v : [v])) {
249
+ const chave = String(item);
250
+ const aqui = `p${i + 1}.${acao}`;
251
+ const anterior = dono.get(chave);
252
+ if (anterior)
253
+ p.push(`cross: ${chave} is claimed by both ${anterior} and ${aqui}`);
254
+ else
255
+ dono.set(chave, aqui);
256
+ }
257
+ }
258
+ });
259
+ return p;
260
+ }
119
261
  /** As ações que este transporte NÃO alcança. É o que uma tela de seleção precisa dizer ANTES de a criança começar. */
120
262
  export function unreachable(tabela) {
121
263
  return ACTIONS.filter((a) => tabela[a] === null || tabela[a] === undefined);
@@ -1,5 +1,6 @@
1
1
  // SPDX-License-Identifier: AGPL-3.0-or-later
2
2
  import type { PlayerView } from '../core/entity.js';
3
+ import { type TabelaDoPad } from './pad-defaults.js';
3
4
  export interface PadButtonLike {
4
5
  pressed: boolean;
5
6
  }
@@ -54,23 +55,30 @@ export declare function bindActive(gp: PadLike, bd: PadBinding | null | undefine
54
55
  /** Ações do frame para este gamepad. `custom` = mapa salvo pelo wizard para este `gp.id` (null/`_skip` = usa o
55
56
  * mapa PADRÃO da Gamepad API "standard": 0=pulo · 1=especial · 2/5/7=correr · 3=troca · 9=START). Direções
56
57
  * custom caem de volta em stdDirs quando o binding do usuário não está ativo (D-pad/stick continuam vivos). */
57
- export declare function padActions(gp: PadLike, custom: PadMap | null): PadActions;
58
+ export declare function padActions(gp: PadLike, custom: PadMap | null, tabela?: TabelaDoPad): PadActions;
58
59
  /**
59
- * A ORDEM em que o assistente pergunta. SÓ a ordem.
60
+ * O MODO DE UM BOTÃO, APLICADO AO CONTROLE — a metade que faltava da empatia motora (issue #120).
60
61
  *
61
- * ⚠️ ELA CARREGAVA AS PALAVRAS, EM PORTUGUÊS CRU, DENTRO DA ENGINE: `['action2', 'PULAR']`,
62
- * `['action1', 'CORRER / INTERAGIR']`. Era o defeito do ADR-0074 na sua forma mais visível — não só
63
- * vocabulário de plataforma dentro do motor, mas vocabulário de plataforma NUM IDIOMA SÓ, à frente de uma
64
- * criança, num ficheiro que o pilar 3 obriga a ser localizável.
62
+ * ⚠️ ELE VALIA SÓ NO TECLADO. `input/keydown.ts:407` solta todas as outras teclas de jogo quando uma nova
63
+ * chega com o modo ligado; `pollPads` não tinha equivalente nenhum, e `grep oneButton` neste ficheiro
64
+ * devolvia zero. Uma criança que ligasse o modo e tivesse um controle na mão **não estava no modo** — sem
65
+ * erro, sem aviso, sem sintoma, porque as definições continuavam a dizer que estava ligado.
65
66
  *
66
- * A palavra vem agora de `ctx.rotuloDaAcao`, que o jogo fornece pelo seu preset. Aqui fica o que é mesmo da
67
- * engine: a sequência em que se pergunta — direções primeiro, ação depois, sistema no fim —, que é uma
68
- * decisão de ergonomia do assistente e não do jogo.
67
+ * ⚠️ AS DIREÇÕES CONTAM, e é isso que torna a regra fiel ao teclado: lá, `isGameKeyCode` inclui as teclas
68
+ * de `p.ctrl`, que são as quatro direções — andar e pular não coexistem. Um filtro que poupasse as
69
+ * direções seria mais confortável e estaria a simular outra deficiência.
69
70
  *
70
- * ⚠️ E UMA AÇÃO QUE O JOGO NÃO NOMEIA NÃO É PERGUNTADA. `wizPrompt` salta-a, porque um jogo que não usa a
71
- * posição não tem o que mapear nela — e perguntar produziria um passo mudo, ou pior, um passo a dizer
72
- * `action7` em voz alta.
71
+ * ⚠️ E A ESCOLHA DE QUEM SOBREVIVE É DIFERENTE DA DO TECLADO, POR NECESSIDADE. No teclado a chegada nova
72
+ * ganha, porque HÁ uma chegada: o evento diz qual é. Um controle é lido por SONDAGEM — o que chega é um
73
+ * retrato, sem ordem. Então mantém-se a que já valia, e só quando ela solta é que a próxima assume. É o
74
+ * que impede o botão de correr de ser cortado porque o polegar encostou noutro, e é a mesma leitura de
75
+ * «segurar» que o ADR-0077 dá.
76
+ *
77
+ * A POLÍTICA é a mesma do `keydown`; a IMPLEMENTAÇÃO não pode ser partilhada hoje porque as formas do
78
+ * estado diferem — lá é um `Set` de códigos de tecla, aqui é um retrato de booleanos por posição. Unificar
79
+ * as duas é trabalho à parte, e escrevê-lo aqui é a alternativa a fingir que não há duas.
73
80
  */
81
+ export declare function umBotaoPorVez(anterior: Readonly<Record<string, boolean | undefined>>, atual: PadActions, ligado: boolean): PadActions;
74
82
  export declare const PADWIZ_ORDER: readonly string[];
75
83
  export interface WizAnimDef {
76
84
  seq?: string[];
@@ -163,6 +171,19 @@ export interface GamepadCtx {
163
171
  navPause: (menu: HTMLElement, playerIndex: number, k: NavKeys) => void;
164
172
  /** Qual jogador abre o submenu de a11y em seguida (game.js's `pauseActor`). */
165
173
  setPauseActor: (playerIndex: number) => void;
174
+ /**
175
+ * ESTA ARESTA É DESTE JOGADOR, E VEIO DO CONTROLE (ADR-0113 cláusula 4, issue #127).
176
+ *
177
+ * 🔴 OBRIGATÓRIO, e a razão foi medida em 2026-09-09: `input/state.arestaDoJogador` tinha ZERO chamadores
178
+ * em produção, logo `entradaDe(i).emUso` respondia `teclado` a toda a gente — e a alternância lida era a do
179
+ * teclado mesmo com o controle na mão. 📌 Passe `criarArestaComAlternancia(() => players)` de
180
+ * `input/latch-edge`, e não o cru: é ela que também resolve a alternância deste aparelho no jogador.
181
+ *
182
+ * ⚠️ O gamepad era o ÚNICO transporte que sobrevivia identificável sem isto — ele nunca passou pelo
183
+ * conjunto de teclas, passa por `padCur` —, e é exactamente por isso que a falta aqui era invisível: o
184
+ * módulo sabe de que controle veio a aresta, e o autómato não.
185
+ */
186
+ arestaDoJogador: (jogador: number, origem: 'gamepad') => void;
166
187
  /**
167
188
  * MODAL do PRÓPRIO jogador: a engine entrega a INTENÇÃO, o jogo decide (ADR-0033).
168
189
  *