@the-inclusionist/engine 8.0.0-rc.1 → 8.0.0
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.
- package/app/public/vendor/fonts/playwrite-ar.woff2 +0 -0
- package/app/public/vendor/fonts/playwrite-br.woff2 +0 -0
- package/app/public/vendor/fonts/playwrite-ca.woff2 +0 -0
- package/app/public/vendor/fonts/playwrite-cl.woff2 +0 -0
- package/app/public/vendor/fonts/playwrite-co.woff2 +0 -0
- package/app/public/vendor/fonts/playwrite-mx.woff2 +0 -0
- package/app/public/vendor/fonts/playwrite-us-modern.woff2 +0 -0
- package/app/public/vendor/fonts/playwrite-us-trad.woff2 +0 -0
- package/app/public/vendor/fonts.css +21 -0
- package/dist-pkg/boot/create-game.d.ts +33 -4
- package/dist-pkg/boot/create-game.js +72 -11
- package/dist-pkg/core/contract.d.ts +60 -0
- package/dist-pkg/core/contract.js +42 -0
- package/dist-pkg/i18n/en.js +11 -0
- package/dist-pkg/i18n/es.js +11 -0
- package/dist-pkg/i18n/pt.js +14 -0
- package/dist-pkg/input/default-bindings.js +20 -12
- package/dist-pkg/input/gamepad.d.ts +15 -1
- package/dist-pkg/input/gamepad.js +30 -9
- package/dist-pkg/input/keyboard.d.ts +32 -0
- package/dist-pkg/input/keyboard.js +46 -6
- package/dist-pkg/input/keydown.d.ts +13 -0
- package/dist-pkg/input/keydown.js +9 -0
- package/dist-pkg/input/latch-edge.d.ts +28 -0
- package/dist-pkg/input/latch-edge.js +45 -0
- package/dist-pkg/input/latch-sync.d.ts +47 -0
- package/dist-pkg/input/latch-sync.js +43 -0
- package/dist-pkg/input/pad-defaults.d.ts +22 -0
- package/dist-pkg/input/pad-defaults.js +34 -0
- package/dist-pkg/input/touch-bindings.d.ts +10 -0
- package/dist-pkg/input/touch-bindings.js +6 -0
- package/dist-pkg/platform/pesados-catalogo.d.ts +14 -0
- package/dist-pkg/platform/pesados-catalogo.js +177 -0
- package/dist-pkg/platform/pesados.d.ts +31 -0
- package/dist-pkg/platform/pesados.js +75 -0
- package/dist-pkg/platform/voice-plan.d.ts +20 -1
- package/dist-pkg/platform/voice-plan.js +20 -1
- package/dist-pkg/platform/vozes-prontas.d.ts +28 -0
- package/dist-pkg/platform/vozes-prontas.js +61 -0
- package/dist-pkg/ui/fonts.d.ts +24 -0
- package/dist-pkg/ui/fonts.js +66 -1
- package/dist-pkg/ui/pause-icons.d.ts +36 -2
- package/dist-pkg/ui/pause-icons.js +9 -1
- package/dist-pkg/ui/settings-motor.d.ts +21 -1
- package/dist-pkg/ui/settings-motor.js +22 -4
- package/dist-pkg/ui/settings-typo.d.ts +18 -4
- package/dist-pkg/ui/settings-typo.js +16 -11
- package/docs/CREDITS.md +15 -12
- package/docs/LICENSES.md +170 -144
- package/package.json +2 -1
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -127,3 +127,24 @@
|
|
|
127
127
|
@font-face{font-family:'Fondamento';font-style:normal;font-weight:400;font-display:swap;
|
|
128
128
|
src:url('fonts/fondamento-400-ext.woff2') format('woff2');
|
|
129
129
|
unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;}
|
|
130
|
+
|
|
131
|
+
/* ===== Playwrite — as OITO das Américas (ADR-0108 §2, issue #87 item 3) =====
|
|
132
|
+
* A mão que cada país ensina a escrever. Os EUA entram com AS DUAS que ensinam — escolher uma seria
|
|
133
|
+
* escolher pela criança. São variáveis no peso (100–400) e cada família é UM ficheiro: a Google não as
|
|
134
|
+
* corta por unicode-range, então não há metade `-ext` para carregar depois. SIL OFL 1.1, como as outras. */
|
|
135
|
+
@font-face{font-family:'Playwrite BR';font-style:normal;font-weight:100 400;font-display:swap;
|
|
136
|
+
src:url('fonts/playwrite-br.woff2') format('woff2');}
|
|
137
|
+
@font-face{font-family:'Playwrite US Trad';font-style:normal;font-weight:100 400;font-display:swap;
|
|
138
|
+
src:url('fonts/playwrite-us-trad.woff2') format('woff2');}
|
|
139
|
+
@font-face{font-family:'Playwrite US Modern';font-style:normal;font-weight:100 400;font-display:swap;
|
|
140
|
+
src:url('fonts/playwrite-us-modern.woff2') format('woff2');}
|
|
141
|
+
@font-face{font-family:'Playwrite CA';font-style:normal;font-weight:100 400;font-display:swap;
|
|
142
|
+
src:url('fonts/playwrite-ca.woff2') format('woff2');}
|
|
143
|
+
@font-face{font-family:'Playwrite MX';font-style:normal;font-weight:100 400;font-display:swap;
|
|
144
|
+
src:url('fonts/playwrite-mx.woff2') format('woff2');}
|
|
145
|
+
@font-face{font-family:'Playwrite AR';font-style:normal;font-weight:100 400;font-display:swap;
|
|
146
|
+
src:url('fonts/playwrite-ar.woff2') format('woff2');}
|
|
147
|
+
@font-face{font-family:'Playwrite CL';font-style:normal;font-weight:100 400;font-display:swap;
|
|
148
|
+
src:url('fonts/playwrite-cl.woff2') format('woff2');}
|
|
149
|
+
@font-face{font-family:'Playwrite CO';font-style:normal;font-weight:100 400;font-display:swap;
|
|
150
|
+
src:url('fonts/playwrite-co.woff2') format('woff2');}
|
|
@@ -11,6 +11,7 @@ import { type SettingsPanelApi } from '../ui/settings-panel.js';
|
|
|
11
11
|
import { type MenuNavApi } from '../ui/menu-nav.js';
|
|
12
12
|
import type { NavKeys } from '../input/edges.js';
|
|
13
13
|
import { type KeyboardRuntime } from '../input/keyboard-runtime.js';
|
|
14
|
+
import { type RelatorioPesado } from '../platform/pesados.js';
|
|
14
15
|
/** O que o jogo empresta do documento. Tudo opcional menos `doc`/`win`: o que faltar vira `problems`. */
|
|
15
16
|
export interface EngineHost {
|
|
16
17
|
readonly doc: Document;
|
|
@@ -48,8 +49,9 @@ export interface EngineHost {
|
|
|
48
49
|
* ONDE O CARTÃO DE PAUSA da primeira tela é pendurado. Ausente, a engine usa `#game-region`.
|
|
49
50
|
*
|
|
50
51
|
* ⚠️ Existe pela mesma razão do `a11yBarHost`: a engine pode OFERECER a pausa, mas não sabe onde ela cabe
|
|
51
|
-
* no desenho de um jogo alheio.
|
|
52
|
-
*
|
|
52
|
+
* no desenho de um jogo alheio. 📌 E desde o ADR-0120 ONDE já não é SE: a pausa deixou de ser declinável, e
|
|
53
|
+
* o que sobra deste campo é o lugar. Um hospedeiro que não aceite filhos vira linha de `problems`, que é a
|
|
54
|
+
* diferença entre a engine não saber e a engine calar-se.
|
|
53
55
|
*/
|
|
54
56
|
readonly pauseHost?: Element | null;
|
|
55
57
|
}
|
|
@@ -60,8 +62,6 @@ export interface EngineHost {
|
|
|
60
62
|
* "pausado" para navegar os próprios menus, porque a engine não tinha por onde ouvir "eu não tenho fases".
|
|
61
63
|
*/
|
|
62
64
|
export interface Declinios {
|
|
63
|
-
/** Sem menu de pausa por tela (um quiz não tem). */
|
|
64
|
-
readonly semMenuDePausa?: boolean;
|
|
65
65
|
/** Sem assistente de mapeamento de controle. */
|
|
66
66
|
readonly semAssistenteDePad?: boolean;
|
|
67
67
|
/** Sem "ator da pausa" — quem apertou o botão que abriu o menu. */
|
|
@@ -144,6 +144,35 @@ export interface CreateGameOptions {
|
|
|
144
144
|
* painel de áudio deixa de OFERECER o motor neural, em vez de o oferecer e nunca o carregar.
|
|
145
145
|
*/
|
|
146
146
|
readonly carregarVozNeural?: CarregarVozNeural;
|
|
147
|
+
/**
|
|
148
|
+
* BAIXAR AS COISAS PESADAS NO PRIMEIRO CARREGAMENTO? Padrão **sim** (ADR-0110 (b), ADR-0116, ADR-0119).
|
|
149
|
+
*
|
|
150
|
+
* As quatro vozes neurais são ~241 MB e descem em SEGUNDO PLANO, uma de cada vez, sem bloquear o jogo: a
|
|
151
|
+
* criança joga enquanto elas chegam, e o que não pode acontecer é ela voltar no segundo dia, sem rede, e
|
|
152
|
+
* descobrir que a voz nunca foi buscada. O pilar 8 é «primeiro dia ONLINE, depois offline-first», e o
|
|
153
|
+
* ADR-0116 tirou a contradição que travava isto — instalar já é um acto de rede.
|
|
154
|
+
*
|
|
155
|
+
* ⚠️ PÔR `false` É PARA QUEM TEM RAZÃO PARA O FAZER, e a razão que já existe é um TESTE: um caso que monte
|
|
156
|
+
* o arranque num navegador de verdade não pode disparar 241 MB contra o Hugging Face. Um jogo em produção
|
|
157
|
+
* que o desligue está a decidir que a criança dele fica sem voz neural offline.
|
|
158
|
+
*
|
|
159
|
+
* 📌 E o ADR-0117 diz que quem devia pagar isto uma vez é a PLATAFORMA, não cada cartucho — a Cache Storage
|
|
160
|
+
* é particionada por origem, e num site só os 241 MB descem uma vez para todos os jogos. Enquanto a
|
|
161
|
+
* plataforma não os pede, é o jogo que os pede: melhor descer duas vezes do que nunca.
|
|
162
|
+
*/
|
|
163
|
+
readonly baixarPesados?: boolean;
|
|
164
|
+
/**
|
|
165
|
+
* O QUE ACONTECEU COM CADA COISA PESADA, à medida que acontece. Ausente = ninguém está a ver.
|
|
166
|
+
*
|
|
167
|
+
* ⚠️ É AQUI E NÃO EM `problems` porque a descarga é de FUNDO: `problems` é devolvido sincronamente pelo
|
|
168
|
+
* `createGame`, e uma linha que chegue depois disso entra num vector que o leitor já leu. O ADR-0110 pede
|
|
169
|
+
* que uma busca falhada seja REPORTADA — reportar é ter um canal que existe quando a notícia chega, e não
|
|
170
|
+
* empurrar para uma lista que já foi entregue.
|
|
171
|
+
*
|
|
172
|
+
* 📌 A engine não inventa superfície nenhuma com isto: quem sabe onde cabe «faltam 241 MB» na tela de um
|
|
173
|
+
* jogo é o jogo. `pesoPorBaixar(relatorio)` dá o número para a frase.
|
|
174
|
+
*/
|
|
175
|
+
readonly aoProgredirPesados?: (r: RelatorioPesado) => void;
|
|
147
176
|
/**
|
|
148
177
|
* Como se descobre que cada transporte está aqui. Ausente = a engine pergunta ao aparelho.
|
|
149
178
|
*
|
|
@@ -32,9 +32,14 @@
|
|
|
32
32
|
//
|
|
33
33
|
// ========================= DECLINAR NÃO É MENTIR =========================
|
|
34
34
|
// O quiz recusou o sonar e o pad em vez de inventar tiles e uma caixa de colisão falsos, e essa distinção é o
|
|
35
|
-
// que separa um achado de um verde falso. Aqui ela vira TIPO: um jogo
|
|
36
|
-
//
|
|
37
|
-
//
|
|
35
|
+
// que separa um achado de um verde falso. Aqui ela vira TIPO: um jogo que não tem uma peça declara-o num
|
|
36
|
+
// campo em vez de devolver `null` de um getter e torcer. O que se declina fica registrado no objeto devolvido,
|
|
37
|
+
// e um consumidor pode ser auditado pelo que recusou.
|
|
38
|
+
// ⚠️ E ESTE PARÁGRAFO NÃO NOMEIA NENHUM DOS CAMPOS, o que parece esquisito e é medido: o
|
|
39
|
+
// `tests/declinio-morto` conta MENÇÕES, incluindo as de comentário, e fá-lo de propósito — «falhar para o lado
|
|
40
|
+
// de vivo é a direcção certa deste erro», porque uma acusação falsa desliga um gate. A consequência é que usar
|
|
41
|
+
// um declínio como EXEMPLO em prosa o faz parecer lido, e um campo genuinamente morto deixa de ser acusado.
|
|
42
|
+
// 📌 O primeiro rascunho desta nota fez exactamente isso, e o crivo apanhou-o no mesmo minuto.
|
|
38
43
|
//
|
|
39
44
|
// ========================= O QUE ISTO AINDA NÃO FAZ, DITO AQUI E NÃO ESCONDIDO =========================
|
|
40
45
|
// Não liga render, física, tiles nem o sonar — nada disso é de todo jogo, e o achado 9 mostra que o sonar hoje
|
|
@@ -67,7 +72,9 @@ import { LOGICAL_W } from '../core/constants.js';
|
|
|
67
72
|
import { initSettingsPanel } from '../ui/settings-panel.js';
|
|
68
73
|
import { initMenuNav } from '../ui/menu-nav.js';
|
|
69
74
|
import { initKeyboardRuntime } from '../input/keyboard-runtime.js';
|
|
70
|
-
import { kb, initKB } from '../input/keyboard.js';
|
|
75
|
+
import { kb, initKB, registrarMapeamentoDoTeclado } from '../input/keyboard.js';
|
|
76
|
+
import { registrarMapeamentoDoPad } from '../input/pad-defaults.js';
|
|
77
|
+
import { baixarPesados } from '../platform/pesados.js';
|
|
71
78
|
import { installCvdFilters } from '../render/cvd-matrices.js';
|
|
72
79
|
/** Os ids que os painéis emprestados exigem do documento. Achado 6: sem eles o painel abre VAZIO, sem erro. */
|
|
73
80
|
const MARCACAO_EXIGIDA = ['#game-region', '#sr-status', '#sr-alert'];
|
|
@@ -209,6 +216,18 @@ export function createGame(o) {
|
|
|
209
216
|
const lerModoCego = o.isBlindMode ?? (() => state.modoCego);
|
|
210
217
|
const pauseIcons = initPauseIcons({
|
|
211
218
|
doc,
|
|
219
|
+
/*
|
|
220
|
+
* A RESPOSTA DO JOGO, lida da declaração (ADR-0115). Sem ela o ícone `altmove` não é montado.
|
|
221
|
+
*
|
|
222
|
+
* ⚠️ LIDA UMA VEZ, NO ARRANQUE, e a razão não é economia — é a criança. O campo é uma FUNÇÃO porque o
|
|
223
|
+
* ADR-0084 diz que um jogo muda de exigência entre fases, mas a COMPOSIÇÃO DA BARRA não pode mudar
|
|
224
|
+
* debaixo da mão de quem está a usá-la: um ícone que aparece e some entre fases é pior do que um que
|
|
225
|
+
* nunca esteve lá, e para quem navega por teclado desloca a ordem de tabulação a meio.
|
|
226
|
+
* 📌 Logo a leitura correcta do contrato é «este jogo segura teclas em ALGUMA fase» — um jogo que segura
|
|
227
|
+
* a pé e nada dentro de um veículo declara `true`, e o registo não disse isto porque a pergunta só
|
|
228
|
+
* aparece quando se monta a barra.
|
|
229
|
+
*/
|
|
230
|
+
seguraTeclas: o.declaration.seguraTeclas(),
|
|
212
231
|
getPlayers: () => o.players ?? [],
|
|
213
232
|
getNumPlayers: () => (o.players ?? [null]).length,
|
|
214
233
|
srSay, srAlert,
|
|
@@ -317,15 +336,16 @@ export function createGame(o) {
|
|
|
317
336
|
* engine inventou uma convenção, procurou-a, não a achou, e concluiu em silêncio que nenhum jogo tem menu
|
|
318
337
|
* de pausa — que é a MESMA forma de defeito do ADR-0106 §2, desta vez cometida pela engine contra si mesma.
|
|
319
338
|
*
|
|
320
|
-
* Agora ela cria o que procura
|
|
321
|
-
*
|
|
339
|
+
* Agora ela cria o que procura — e para TODO jogo, desde o ADR-0120 e outra vez desde o ADR-0122: o
|
|
340
|
+
* declínio saiu, porque a razão de ele existir foi construída fora por este mesmo ADR-0106, e porque a
|
|
341
|
+
* regra é do Dev — a pausa e os ícones de acessibilidade estão em todo jogo, logo são da engine.
|
|
322
342
|
*/
|
|
323
|
-
const hospedeiroDaPausa =
|
|
343
|
+
const hospedeiroDaPausa = o.host.pauseHost ?? $('#game-region');
|
|
324
344
|
const pausaUsavel = !!hospedeiroDaPausa && typeof hospedeiroDaPausa.appendChild === 'function';
|
|
325
|
-
if (!
|
|
345
|
+
if (!pausaUsavel) {
|
|
326
346
|
problems.push('sem sítio para o menu de pausa: declare `host.pauseHost` ou tenha um #game-region que aceite filhos. '
|
|
327
|
-
+ 'Sem ele a criança não alcança os ajustes durante a partida
|
|
328
|
-
+ 'que
|
|
347
|
+
+ 'Sem ele a criança não alcança os ajustes durante a partida — e NÃO há como declinar: desde o '
|
|
348
|
+
+ 'ADR-0122 a pausa é da engine em todo jogo, e o que este jogo declara é só ONDE ela cabe');
|
|
329
349
|
}
|
|
330
350
|
if (hospedeiroDaPausa && pausaUsavel) {
|
|
331
351
|
const cartao = pauseIcons.buildScreenPause(0);
|
|
@@ -363,6 +383,21 @@ export function createGame(o) {
|
|
|
363
383
|
getAudioCtx: () => audioCtx, getSoundOn: () => soundOn, getAudioCat: () => audioCat,
|
|
364
384
|
});
|
|
365
385
|
// 5. Teclado remapeável — o melhor recorte da base (achado 11): esquema de teclas, sem mundo.
|
|
386
|
+
//
|
|
387
|
+
// ⚠️ O PADRÃO DO JOGO REGISTA-SE ANTES DO `initKB()`, e a ordem é a regra: quem lê o disco já tem de saber
|
|
388
|
+
// qual é a fábrica sobre a qual o dado da criança se sobrepõe (ADR-0115). Registar depois deixaria o
|
|
389
|
+
// primeiro arranque com a fábrica da ENGINE e o segundo com a do jogo — a pior espécie de defeito, porque
|
|
390
|
+
// desaparece quando alguém vai ver.
|
|
391
|
+
// 📌 E o registo aceita `null`, que é o que um jogo sem opinião produz: fica a fábrica da engine.
|
|
392
|
+
registrarMapeamentoDoTeclado(o.declaration.mapeamentoDoTeclado
|
|
393
|
+
? (jogadores, assento) => o.declaration.mapeamentoDoTeclado(jogadores, assento)
|
|
394
|
+
: null);
|
|
395
|
+
// ⚠️ E O DO CONTROLE REGISTA-SE AQUI AINDA QUE ESTA RAIZ NÃO MONTE GAMEPAD NENHUM. Não é descuido: quem
|
|
396
|
+
// chama `initGamepad` é o cartucho, e é exactamente por isso que o registo não pode viver lá — seria mais
|
|
397
|
+
// um campo que um jogo pode esquecer, e esquecê-lo devolve o mapa da ENGINE a quem declarou outro, calado.
|
|
398
|
+
registrarMapeamentoDoPad(o.declaration.mapeamentoDoPad
|
|
399
|
+
? (jogadores, assento) => o.declaration.mapeamentoDoPad(jogadores, assento)
|
|
400
|
+
: null);
|
|
366
401
|
initKB();
|
|
367
402
|
// ⚠️ O ESQUEMA DE ARRANQUE ALCANÇA NADA, e diz isso com `null` em vez de com um objeto vazio (issue #118).
|
|
368
403
|
// Ele vive um instante — `assignControls()` logo abaixo substitui-o pelo esquema real —, mas enquanto vive
|
|
@@ -378,7 +413,7 @@ export function createGame(o) {
|
|
|
378
413
|
const nav = initMenuNav({
|
|
379
414
|
$, getActiveElement: () => doc.activeElement,
|
|
380
415
|
topVisibleOverlay: overlays.topVisibleOverlay, closeById: overlays.closeById,
|
|
381
|
-
getPauseMenu:
|
|
416
|
+
getPauseMenu: (i) => $(`#vp-pause-${i}`),
|
|
382
417
|
setPhase: o.setPhase ?? (() => { }),
|
|
383
418
|
setPauseActor: () => { },
|
|
384
419
|
srSay,
|
|
@@ -522,5 +557,31 @@ export function createGame(o) {
|
|
|
522
557
|
cartao.hidden = true;
|
|
523
558
|
},
|
|
524
559
|
};
|
|
560
|
+
/*
|
|
561
|
+
* AS COISAS PESADAS COMEÇAM A DESCER AQUI, e a linha é deliberadamente a ÚLTIMA coisa do arranque.
|
|
562
|
+
*
|
|
563
|
+
* ⚠️ SEM `await`. O arranque não espera por 241 MB — se esperasse, a primeira tela de uma escola com 3G
|
|
564
|
+
* ficaria em branco durante minutos e a criança concluiria que o jogo não abre. O `catch` vazio é a mesma
|
|
565
|
+
* regra escrita duas vezes: uma falha de rede aqui não pode derrubar um jogo que hoje nem usa a voz.
|
|
566
|
+
*
|
|
567
|
+
* 🔴 E O RELATÓRIO NÃO VAI PARA `problems`, embora a primeira versão o fizesse. Duas razões medidas, e a
|
|
568
|
+
* primeira é a que importa:
|
|
569
|
+
*
|
|
570
|
+
* 1. **CHEGA DEPOIS DE O LEITOR SE IR EMBORA.** `problems` é devolvido na linha abaixo, sincronamente; a
|
|
571
|
+
* descarga é de fundo, logo TODA linha dela entraria num vector que o consumidor já leu. Quem faz
|
|
572
|
+
* `if (motor.problems.length) …` não veria nada, e quem o lesse mais tarde veria uma lista que cresceu
|
|
573
|
+
* depois do arranque. Um relatório que chega depois do leitor não é um relatório — é a forma exacta do
|
|
574
|
+
* `srSay` a escrever onde não havia `#sr-status`.
|
|
575
|
+
* 2. **AFOGAVA O QUE SE PODE RESOLVER.** Sem rede — uma escola sem rede, que é o alvo e não a excepção —
|
|
576
|
+
* são OITO falhas a empurrar para uma lista que o ADR-0106 §2 construiu para dizer o que FALTA NO
|
|
577
|
+
* HOSPEDEIRO. A criança perde a barra de acessibilidade e a linha que o diz fica em nono lugar.
|
|
578
|
+
*
|
|
579
|
+
* 📌 O canal certo é o que a própria função já tem: `aoProgredir`, entregue a quem chama. Um consumidor que
|
|
580
|
+
* queira mostrar «faltam 241 MB» ou «a voz não desceu» tem por onde; a engine não inventa uma superfície.
|
|
581
|
+
*/
|
|
582
|
+
if (o.baixarPesados !== false) {
|
|
583
|
+
void baixarPesados({ aoProgredir: o.aoProgredirPesados })
|
|
584
|
+
.catch(() => { });
|
|
585
|
+
}
|
|
525
586
|
return { declaration: o.declaration, pausa, tts, overlays, nav, keyboard, sonar, aplicarFiltroDeVisao, cenas: criarPilha(), cvdFilters, problems, declines, aoFalhar, alcance: alcanceAqui };
|
|
526
587
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
import type { Action } from './actions.js';
|
|
2
3
|
/** Gênero gramatical do nome. `n` = neutro/indefinido (o pt-BR usa o masculino como default nesse caso). */
|
|
3
4
|
export type Gender = 'm' | 'f' | 'n';
|
|
4
5
|
/** Um nome que pode ser FALADO (leitor de tela) e SINALIZADO (Libras, que traduz o mesmo texto). */
|
|
@@ -194,6 +195,28 @@ export interface GameDeclaration {
|
|
|
194
195
|
* em silêncio, que é o defeito que o ADR-0084 nomeou.
|
|
195
196
|
*/
|
|
196
197
|
holdsAtOnce(): number;
|
|
198
|
+
/**
|
|
199
|
+
* ESTE JOGO SEGURA ALGUMA TECLA? — e a resposta não é derivável de mais nada. (ADR-0115.)
|
|
200
|
+
*
|
|
201
|
+
* 🔴 A ALTERNÂNCIA EXISTE PARA UMA CRIANÇA CONCRETA: quem não consegue MANTER uma tecla premida carrega uma
|
|
202
|
+
* vez para andar e outra para parar. Num jogo onde nada se segura — um quiz, um tabuleiro, um puzzle de
|
|
203
|
+
* peças — não há nada a travar, e o controle passa a ser uma opção que **não faz nada**. A criança abre o
|
|
204
|
+
* menu de acessibilidade, liga o ajuste de que depende, e não acontece nada: ela aprende que o ajuste está
|
|
205
|
+
* partido. É o botão morto que o ADR-0106 §5 proíbe.
|
|
206
|
+
*
|
|
207
|
+
* ⚠️ E O `holdsAtOnce` ACIMA NÃO RESPONDE ISTO, o que foi o achado que obrigou a este campo: ele conta
|
|
208
|
+
* POSIÇÕES SIMULTÂNEAS e recusa zero, porque zero faria a aritmética do alcance passar por vacuidade. O
|
|
209
|
+
* `consumer-quiz` declara **1 sem segurar coisa nenhuma**. «Um de cada vez» e «um SEGURADO» são o mesmo
|
|
210
|
+
* número, e toda decisão a jusante vinha a ler um número que responde a outra pergunta.
|
|
211
|
+
*
|
|
212
|
+
* ⚠️ OBRIGATÓRIO, e a obrigatoriedade É a decisão — a mesma do `holdsAtOnce`, pela mesma frase do Dev:
|
|
213
|
+
* «não declarar é ter a acessibilidade programada no controle pro sorte». Um campo opcional faria um jogo
|
|
214
|
+
* que ESQUECE a linha perder a alternância em silêncio, e quem paga é a criança com dificuldade motora.
|
|
215
|
+
*
|
|
216
|
+
* 📌 FUNÇÃO e não valor, pelo ADR-0084: um jogo muda de exigência entre fases. A pé segura-se uma direcção;
|
|
217
|
+
* o mesmo jogo dentro de um veículo pode não segurar nada.
|
|
218
|
+
*/
|
|
219
|
+
seguraTeclas(): boolean;
|
|
197
220
|
/**
|
|
198
221
|
* ESTE JOGO PRECISA DE UM PONTEIRO — posição contínua? (ADR-0112.)
|
|
199
222
|
*
|
|
@@ -213,6 +236,43 @@ export interface GameDeclaration {
|
|
|
213
236
|
* FUNÇÃO e não valor, pela mesma razão das outras: uma actividade pode desenhar numa fase e não noutra.
|
|
214
237
|
*/
|
|
215
238
|
needsPointer?(): boolean;
|
|
239
|
+
/**
|
|
240
|
+
* O MAPEAMENTO DE TECLADO QUE ESTE JOGO QUER — por número de jogadores e por assento (ADR-0115).
|
|
241
|
+
*
|
|
242
|
+
* A precedência é a que o registo pede, e ela cabe entre duas linhas que já existiam no `input/keyboard`:
|
|
243
|
+
* **fábrica da engine → padrão do JOGO → remapeamento da CRIANÇA.** Devolver `null` (ou não declarar) deixa
|
|
244
|
+
* a fábrica da engine intacta, que é o comportamento de sempre.
|
|
245
|
+
*
|
|
246
|
+
* PARCIAL de propósito: um jogo que só queira trocar o `action1` troca o `action1`. A fusão já existe — é o
|
|
247
|
+
* `Object.assign` que sobrepõe o dado guardado —, então esta é mais uma camada no mesmo sítio e não uma
|
|
248
|
+
* segunda forma de fundir.
|
|
249
|
+
*
|
|
250
|
+
* ⚠️ OPCIONAL, e aqui, ao contrário do `seguraTeclas`, o silêncio tem um lado seguro: sem declaração o jogo
|
|
251
|
+
* fica com a fábrica da engine, que é jogável e é o que ele já tem hoje. Não há lado errado na ausência.
|
|
252
|
+
*
|
|
253
|
+
* ⚠️ E LEVA O ASSENTO porque o teclado de dois jogadores não é o de um: as setas mudam de dono, e um padrão
|
|
254
|
+
* que não soubesse o assento daria as mesmas teclas a duas crianças. `jogadores` é 1, 2, 3 ou 4; `assento` é
|
|
255
|
+
* o índice dentro desse arranjo.
|
|
256
|
+
*
|
|
257
|
+
* 📌 O irmão do CONTROLE é o campo logo abaixo, e chegou um commit depois: o obstáculo era que o assento
|
|
258
|
+
* ainda não se conhecia no ponto em que a tabela de botões é lida, e a saída foi subi-lo no laço.
|
|
259
|
+
*/
|
|
260
|
+
mapeamentoDoTeclado?(jogadores: number, assento: number): Partial<Record<Action, readonly string[] | null>> | null;
|
|
261
|
+
/**
|
|
262
|
+
* O MAPEAMENTO DE BOTÕES QUE ESTE JOGO QUER NO CONTROLE — mesma pergunta, outro aparelho (ADR-0115).
|
|
263
|
+
*
|
|
264
|
+
* Índices de botão da Gamepad API «standard», parciais: `{ action1: 3 }` troca só essa. `null` num botão diz
|
|
265
|
+
* «esta posição não existe neste jogo», que é diferente de a deixar na fábrica.
|
|
266
|
+
*
|
|
267
|
+
* ⚠️ A PRECEDÊNCIA TEM UMA DIFERENÇA DE SÍTIO QUE VALE SABER: no teclado, o que a criança remapeou é uma
|
|
268
|
+
* camada POR CIMA desta; no controle, o mapa que ela gravou no assistente é um RAMO inteiro — se ele existe,
|
|
269
|
+
* este padrão não é consultado. Nos dois casos ela ganha, que é o que importa.
|
|
270
|
+
*
|
|
271
|
+
* 📌 E leva o assento pela mesma razão do teclado, ainda que por um caminho diferente: dois controles são
|
|
272
|
+
* dois aparelhos, mas o JOGO pode querer arranjos distintos por assento (o guarda-redes e o atacante não
|
|
273
|
+
* fazem o mesmo).
|
|
274
|
+
*/
|
|
275
|
+
mapeamentoDoPad?(jogadores: number, assento: number): Partial<Record<Action, number | null>> | null;
|
|
216
276
|
readonly tick: TickOwner;
|
|
217
277
|
/** O papel do que está em `at`. É o campo 2, e é o que substitui `roleOf`. */
|
|
218
278
|
roleAt(at: Spot): Role;
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
2
|
// core/contract — OS SETE CAMPOS. A interface que o ADR-0030 escolheu como o eixo da engine.
|
|
3
3
|
//
|
|
4
|
+
// ⚠️ ESTE FICHEIRO NÃO TINHA IMPORT NENHUM, e o único que ganhou é `import type` — apagado na compilação, logo
|
|
5
|
+
// o módulo publicado continua sem dependência de execução. O que entra é o VOCABULÁRIO das catorze posições
|
|
6
|
+
// (ADR-0074), e ele tinha de entrar: um campo que fala de mapeamento e escrevesse as suas próprias chaves
|
|
7
|
+
// seria a segunda cópia da união que o `core/actions` existe para ser a primeira.
|
|
8
|
+
//
|
|
4
9
|
// ========================= O QUE ISTO É, E O QUE NÃO É =========================
|
|
5
10
|
// Não é um framework nem uma classe-base. É a ÚNICA coisa que a pilha de acessibilidade sabe sobre um jogo.
|
|
6
11
|
// O ADR-0027 mediu que as três funções que MAIS pareciam genéricas eram as três mais amarradas à plataforma —
|
|
@@ -126,6 +131,17 @@ export function conformanceProblems(d) {
|
|
|
126
131
|
p.push('holdsAtOnce: must be an integer >= 1 - a game that holds nothing cannot be played');
|
|
127
132
|
}
|
|
128
133
|
}
|
|
134
|
+
// ⚠️ E ESTA É A OUTRA PERGUNTA, que o número acima parecia responder e não responde (ADR-0115). A mensagem
|
|
135
|
+
// diz o que a ausência CUSTA, e não só o que falta: sem ela, um jogo que nada segura oferece um controle de
|
|
136
|
+
// acessibilidade que não faz nada, e um que segura tudo pode não o oferecer a quem depende dele.
|
|
137
|
+
if (typeof d.seguraTeclas !== 'function') {
|
|
138
|
+
p.push('seguraTeclas: missing - declare whether any key is HELD in this game (latching is offered only where something can be held, and a game that holds nothing must not show a control that does nothing)');
|
|
139
|
+
}
|
|
140
|
+
else if (typeof d.seguraTeclas() !== 'boolean') {
|
|
141
|
+
// Um valor não-booleano seria truthy e ofereceria a alternância a toda a gente — o mesmo defeito
|
|
142
|
+
// silencioso que o `needsPointer` recusa logo abaixo, e pela mesma razão.
|
|
143
|
+
p.push('seguraTeclas: must return a boolean - a non-boolean is truthy and would offer latching in a game where nothing is held');
|
|
144
|
+
}
|
|
129
145
|
// ⚠️ OPCIONAL, MAS NÃO IMPUNE. Ausente é a resposta `false` e não é problema — ver a nota no campo. O que
|
|
130
146
|
// se recusa é declará-lo MAL: um `needsPointer: true` (valor em vez de função) seria sempre verdadeiro por
|
|
131
147
|
// ser um objecto, e um que devolvesse `'sim'` também. Nos dois casos o jogo julgaria ter declarado, o
|
|
@@ -144,6 +160,32 @@ export function conformanceProblems(d) {
|
|
|
144
160
|
p.push('needsPointer: must return a boolean - a non-boolean would be truthy and refuse devices this game can actually use');
|
|
145
161
|
}
|
|
146
162
|
}
|
|
163
|
+
// ⚠️ E O MESMO PARA O MAPEAMENTO, com uma razão própria: aqui um valor em vez de uma função não seria um
|
|
164
|
+
// erro barulhento, seria um mapeamento SILENCIOSAMENTE ignorado — a fábrica da engine ficava, e a criança
|
|
165
|
+
// jogava com um teclado que o autor do jogo julga ter mudado. E devolver algo que não é objecto nem `null`
|
|
166
|
+
// atravessaria o `Object.assign` sem escrever nada, que é a mesma ausência com outra roupa.
|
|
167
|
+
if (d.mapeamentoDoTeclado !== undefined) {
|
|
168
|
+
if (typeof d.mapeamentoDoTeclado !== 'function') {
|
|
169
|
+
p.push('mapeamentoDoTeclado: must be a function - write `mapeamentoDoTeclado: (jogadores, assento) => ({ action1: ["KeyQ"] })`, because the keyboard of two players is not the keyboard of one');
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
const m = d.mapeamentoDoTeclado(1, 0);
|
|
173
|
+
if (m !== null && (typeof m !== 'object' || Array.isArray(m))) {
|
|
174
|
+
p.push('mapeamentoDoTeclado: must return an object or null - anything else is merged into nothing, and this game keeps the engine factory while its author believes otherwise');
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (d.mapeamentoDoPad !== undefined) {
|
|
179
|
+
if (typeof d.mapeamentoDoPad !== 'function') {
|
|
180
|
+
p.push('mapeamentoDoPad: must be a function - write `mapeamentoDoPad: (jogadores, assento) => ({ action1: 3 })`, because two seats may want different arrangements');
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
const m = d.mapeamentoDoPad(1, 0);
|
|
184
|
+
if (m !== null && (typeof m !== 'object' || Array.isArray(m))) {
|
|
185
|
+
p.push('mapeamentoDoPad: must return an object or null - anything else is merged into nothing, and this game keeps the engine factory while its author believes otherwise');
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
147
189
|
if (d.tick !== 'player' && d.tick !== 'clock')
|
|
148
190
|
p.push('tick: must be "player" or "clock"');
|
|
149
191
|
for (const f of ['roleAt', 'nameAt', 'focusOf', 'objectiveOf', 'targetsOf']) {
|
package/dist-pkg/i18n/en.js
CHANGED
|
@@ -580,6 +580,17 @@ const en = {
|
|
|
580
580
|
'font.desc.andika': 'based on Sassoon; the fruit of research into how children read and write',
|
|
581
581
|
'font.desc.opendyslexic': 'Letters weighted at the bottom, so they do not flip upside down as you read.',
|
|
582
582
|
'font.desc.fondamento': 'Pen calligraphy, for the handwriting activities.',
|
|
583
|
+
'font.desc.pw.br': 'School cursive taught in Brazil.',
|
|
584
|
+
'font.desc.pw.ustrad': 'School cursive taught in the USA — traditional.',
|
|
585
|
+
'font.desc.pw.usmod': 'School cursive taught in the USA — modern.',
|
|
586
|
+
'font.desc.pw.ca': 'School cursive taught in Canada.',
|
|
587
|
+
'font.desc.pw.mx': 'School cursive taught in Mexico.',
|
|
588
|
+
'font.desc.pw.ar': 'School cursive taught in Argentina.',
|
|
589
|
+
'font.desc.pw.cl': 'School cursive taught in Chile.',
|
|
590
|
+
'font.desc.pw.co': 'School cursive taught in Colombia.',
|
|
591
|
+
'font.desc.ronde': 'French ronde, the joined handwriting taught at school.',
|
|
592
|
+
'font.off.ronde': 'Install one of these three on the device: Ronde Script, OPTIFrench-Script or Merveille. '
|
|
593
|
+
+ 'They are free for personal use, which is why they cannot ship inside the game.',
|
|
583
594
|
'font.desc.greatvibes': 'English calligraphy',
|
|
584
595
|
'font.desc.pinyon': 'English calligraphy',
|
|
585
596
|
'font.desc.ufcook': 'German blackletter',
|
package/dist-pkg/i18n/es.js
CHANGED
|
@@ -578,6 +578,17 @@ const es = {
|
|
|
578
578
|
'font.desc.andika': 'basada en la Sassoon; fruto de la investigación sobre cómo leen y escriben los niños',
|
|
579
579
|
'font.desc.opendyslexic': 'Letras con la base más pesada, para que no se den vuelta al leer.',
|
|
580
580
|
'font.desc.fondamento': 'Caligráfica de pluma, para las actividades de escritura a mano.',
|
|
581
|
+
'font.desc.pw.br': 'Cursiva escolar de Brasil.',
|
|
582
|
+
'font.desc.pw.ustrad': 'Cursiva escolar de EE. UU. — tradicional.',
|
|
583
|
+
'font.desc.pw.usmod': 'Cursiva escolar de EE. UU. — moderna.',
|
|
584
|
+
'font.desc.pw.ca': 'Cursiva escolar de Canadá.',
|
|
585
|
+
'font.desc.pw.mx': 'Cursiva escolar de México.',
|
|
586
|
+
'font.desc.pw.ar': 'Cursiva escolar de Argentina.',
|
|
587
|
+
'font.desc.pw.cl': 'Cursiva escolar de Chile.',
|
|
588
|
+
'font.desc.pw.co': 'Cursiva escolar de Colombia.',
|
|
589
|
+
'font.desc.ronde': 'Ronde francesa, la letra ligada que se enseña en la escuela.',
|
|
590
|
+
'font.off.ronde': 'Instale en el dispositivo una de estas tres: Ronde Script, OPTIFrench-Script o Merveille. '
|
|
591
|
+
+ 'Son gratuitas para uso personal, y por eso no pueden venir dentro del juego.',
|
|
581
592
|
'font.desc.greatvibes': 'caligráfica inglesa',
|
|
582
593
|
'font.desc.pinyon': 'caligráfica inglesa',
|
|
583
594
|
'font.desc.ufcook': 'blackletter alemana',
|
package/dist-pkg/i18n/pt.js
CHANGED
|
@@ -653,6 +653,20 @@ const pt = {
|
|
|
653
653
|
'font.desc.andika': 'baseada na Sassoon; fruto de pesquisa sobre como crianças leem e escrevem',
|
|
654
654
|
'font.desc.opendyslexic': 'Letras com a base mais pesada, para não virarem de cabeça para baixo ao ler.',
|
|
655
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.',
|
|
656
670
|
'font.desc.greatvibes': 'caligráfica inglesa',
|
|
657
671
|
'font.desc.pinyon': 'caligráfica inglesa',
|
|
658
672
|
'font.desc.ufcook': 'blackletter alemã',
|
|
@@ -6,20 +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
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
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:
|
|
12
13
|
//
|
|
13
|
-
// · `GAMEPAD_STANDARD` —
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
// · `KEYBOARD_SOLO` / `KEYBOARD_DUO` —
|
|
17
|
-
// `
|
|
18
|
-
//
|
|
19
|
-
// moram as seis posições que faltam num teclado dividido por quatro.
|
|
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
20
|
//
|
|
21
|
-
// ⚠️
|
|
22
|
-
//
|
|
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.
|
|
23
31
|
//
|
|
24
32
|
// ========================= A SIMETRIA DO TECLADO, QUE NÃO É DECORAÇÃO =========================
|
|
25
33
|
// O padrão que o Dev especificou apoia-se num bloco do QWERTY:
|
|
@@ -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,7 +55,7 @@ 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
60
|
* O MODO DE UM BOTÃO, APLICADO AO CONTROLE — a metade que faltava da empatia motora (issue #120).
|
|
60
61
|
*
|
|
@@ -170,6 +171,19 @@ export interface GamepadCtx {
|
|
|
170
171
|
navPause: (menu: HTMLElement, playerIndex: number, k: NavKeys) => void;
|
|
171
172
|
/** Qual jogador abre o submenu de a11y em seguida (game.js's `pauseActor`). */
|
|
172
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;
|
|
173
187
|
/**
|
|
174
188
|
* MODAL do PRÓPRIO jogador: a engine entrega a INTENÇÃO, o jogo decide (ADR-0033).
|
|
175
189
|
*
|