@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.
- package/README.md +5 -3
- package/app/css/style.css +22 -5
- package/app/public/vendor/fonts/fondamento-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/fondamento-400.woff2 +0 -0
- package/app/public/vendor/fonts/opendyslexic-400.woff2 +0 -0
- 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/pressstart-400.woff2 +0 -0
- package/app/public/vendor/fonts.css +64 -2
- package/dist-pkg/boot/create-game.d.ts +107 -4
- package/dist-pkg/boot/create-game.js +337 -18
- package/dist-pkg/core/constants.d.ts +0 -28
- package/dist-pkg/core/constants.js +34 -17
- package/dist-pkg/core/contract.d.ts +99 -0
- package/dist-pkg/core/contract.js +78 -0
- package/dist-pkg/core/entity.d.ts +47 -4
- package/dist-pkg/core/layers.d.ts +18 -0
- package/dist-pkg/core/layers.js +18 -0
- package/dist-pkg/core/rng.js +14 -4
- package/dist-pkg/core/route.d.ts +42 -0
- package/dist-pkg/core/route.js +158 -0
- package/dist-pkg/core/state.d.ts +10 -1
- package/dist-pkg/core/state.js +13 -0
- package/dist-pkg/educational/adaptive-engine.d.ts +65 -0
- package/dist-pkg/educational/adaptive-engine.js +117 -0
- package/dist-pkg/educational/segment-bar.d.ts +96 -0
- package/dist-pkg/educational/segment-bar.js +89 -0
- package/dist-pkg/i18n/en.js +44 -0
- package/dist-pkg/i18n/es.js +44 -0
- package/dist-pkg/i18n/pt.js +60 -0
- package/dist-pkg/input/default-bindings.d.ts +40 -0
- package/dist-pkg/input/default-bindings.js +151 -9
- package/dist-pkg/input/gamepad.d.ts +33 -12
- package/dist-pkg/input/gamepad.js +103 -16
- package/dist-pkg/input/keyboard-runtime.d.ts +6 -8
- package/dist-pkg/input/keyboard-runtime.js +24 -5
- package/dist-pkg/input/keyboard.d.ts +42 -0
- package/dist-pkg/input/keyboard.js +87 -17
- package/dist-pkg/input/keydown.d.ts +40 -2
- package/dist-pkg/input/keydown.js +29 -6
- package/dist-pkg/input/latch-edge.d.ts +28 -0
- package/dist-pkg/input/latch-edge.js +45 -0
- package/dist-pkg/input/latch-scope.d.ts +70 -0
- package/dist-pkg/input/latch-scope.js +110 -0
- package/dist-pkg/input/latch-store.d.ts +45 -0
- package/dist-pkg/input/latch-store.js +74 -0
- package/dist-pkg/input/latch-sync.d.ts +47 -0
- package/dist-pkg/input/latch-sync.js +43 -0
- package/dist-pkg/input/origem-sintetica.d.ts +44 -0
- package/dist-pkg/input/origem-sintetica.js +60 -0
- package/dist-pkg/input/pad-defaults.d.ts +22 -0
- package/dist-pkg/input/pad-defaults.js +34 -0
- package/dist-pkg/input/pointer.d.ts +61 -0
- package/dist-pkg/input/pointer.js +71 -0
- package/dist-pkg/input/state.d.ts +86 -1
- package/dist-pkg/input/state.js +128 -1
- package/dist-pkg/input/touch-bindings.d.ts +21 -2
- package/dist-pkg/input/touch-bindings.js +15 -3
- package/dist-pkg/input/touch.js +9 -1
- package/dist-pkg/input/transporte-em-uso.d.ts +101 -0
- package/dist-pkg/input/transporte-em-uso.js +130 -0
- package/dist-pkg/input/transports.d.ts +88 -2
- package/dist-pkg/input/transports.js +60 -5
- package/dist-pkg/input/vocabulary-migration.d.ts +18 -7
- package/dist-pkg/platform/audio-earcons.d.ts +29 -2
- package/dist-pkg/platform/audio-earcons.js +10 -0
- package/dist-pkg/platform/audio-nav.d.ts +1 -1
- package/dist-pkg/platform/audio-sonar.d.ts +149 -9
- package/dist-pkg/platform/audio-sonar.js +232 -21
- package/dist-pkg/platform/guide-intensity.d.ts +40 -0
- package/dist-pkg/platform/guide-intensity.js +73 -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/storage.d.ts +30 -0
- package/dist-pkg/platform/storage.js +35 -0
- package/dist-pkg/platform/tts.js +16 -1
- package/dist-pkg/platform/voice-plan.d.ts +109 -0
- package/dist-pkg/platform/voice-plan.js +156 -0
- package/dist-pkg/platform/vozes-prontas.d.ts +28 -0
- package/dist-pkg/platform/vozes-prontas.js +61 -0
- package/dist-pkg/render/draw.d.ts +1 -1
- package/dist-pkg/render/draw.js +15 -5
- package/dist-pkg/render/viz-axes.d.ts +17 -0
- package/dist-pkg/render/viz-axes.js +19 -0
- package/dist-pkg/render/viz-setters.d.ts +34 -2
- package/dist-pkg/render/viz-setters.js +189 -33
- package/dist-pkg/render/wheelchair-sprites.d.ts +11 -3
- package/dist-pkg/render/wheelchair-sprites.js +10 -4
- package/dist-pkg/ui/dom.js +20 -2
- package/dist-pkg/ui/fonts.d.ts +71 -1
- package/dist-pkg/ui/fonts.js +111 -8
- package/dist-pkg/ui/latch-refusal.d.ts +32 -0
- package/dist-pkg/ui/latch-refusal.js +60 -0
- package/dist-pkg/ui/layout.d.ts +32 -0
- package/dist-pkg/ui/layout.js +64 -1
- package/dist-pkg/ui/motion-scene.d.ts +41 -0
- package/dist-pkg/ui/motion-scene.js +76 -0
- package/dist-pkg/ui/panel-shell.d.ts +53 -0
- package/dist-pkg/ui/panel-shell.js +103 -0
- package/dist-pkg/ui/pause-icons.d.ts +168 -20
- package/dist-pkg/ui/pause-icons.js +315 -45
- package/dist-pkg/ui/reach-notice.js +8 -0
- package/dist-pkg/ui/settings-audio.d.ts +14 -1
- package/dist-pkg/ui/settings-audio.js +43 -3
- package/dist-pkg/ui/settings-controls.d.ts +66 -6
- package/dist-pkg/ui/settings-controls.js +179 -17
- package/dist-pkg/ui/settings-empathy.d.ts +15 -0
- package/dist-pkg/ui/settings-empathy.js +2 -0
- package/dist-pkg/ui/settings-motion.d.ts +25 -19
- package/dist-pkg/ui/settings-motion.js +32 -19
- package/dist-pkg/ui/settings-motor.d.ts +101 -3
- package/dist-pkg/ui/settings-motor.js +136 -9
- package/dist-pkg/ui/settings-typo.d.ts +49 -5
- package/dist-pkg/ui/settings-typo.js +74 -23
- package/dist-pkg/ui/settings-visual.d.ts +21 -1
- package/dist-pkg/ui/settings-visual.js +48 -4
- package/dist-pkg/ui/shell.d.ts +13 -3
- package/dist-pkg/ui/shell.js +3 -4
- package/dist-pkg/ui/simulation-refusal.d.ts +32 -0
- package/dist-pkg/ui/simulation-refusal.js +57 -0
- package/dist-pkg/ui/visual-axes-panel.d.ts +48 -0
- package/dist-pkg/ui/visual-axes-panel.js +95 -0
- package/dist-pkg/ui/webcam.js +6 -1
- package/docs/CREDITS.md +22 -1
- package/docs/LICENSES.md +170 -132
- package/package.json +25 -4
- package/app/public/vendor/fonts/greatvibes-400.woff2 +0 -0
- package/app/public/vendor/fonts/ufcook-700.woff2 +0 -0
|
@@ -32,38 +32,61 @@
|
|
|
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
|
|
41
46
|
// exige seis coisas de plataforma. Não substitui o boot do `main.js`, que tem catorze anos de ordem própria.
|
|
42
47
|
// O que ele cobre é o que o quiz provou ser IDÊNTICO em qualquer jogo: idioma, leitor de tela, mixer, voz,
|
|
43
48
|
// pilha de diálogos, filtros de daltonismo, teclado remapeável e navegação de menu.
|
|
44
|
-
import { initI18n } from '../core/i18n.js';
|
|
49
|
+
import { initI18n, idiomaPronto } from '../core/i18n.js';
|
|
50
|
+
import { entradaDe } from '../input/state.js';
|
|
45
51
|
import { criarAvisoDeQueda } from '../ui/loop-crash.js';
|
|
46
52
|
import { initFocusTrap, focaveisNoDom } from '../ui/focus-trap.js';
|
|
47
53
|
import { mostrarAvisoDeAlcance } from '../ui/reach-notice.js';
|
|
48
54
|
import { alcance, transportesPadrao } from '../input/transports.js';
|
|
49
|
-
import { presetActions } from '../core/actions.js';
|
|
55
|
+
import { presetActions, ACTIONS } from '../core/actions.js';
|
|
50
56
|
import { t } from '../core/i18n.js';
|
|
51
57
|
import { srSay, srAlert } from '../core/a11y-sr.js';
|
|
58
|
+
import { initPauseIcons, iconsMarkup } from '../ui/pause-icons.js';
|
|
59
|
+
// O módulo INTEIRO: o on do barramento de eventos, para a barra montada continuar a dizer a verdade.
|
|
60
|
+
import * as state from '../core/state.js';
|
|
61
|
+
import { vlibrasOpen, toggleLibras } from '../ui/vlibras.js';
|
|
52
62
|
import { conformanceProblems } from '../core/contract.js';
|
|
53
63
|
import { criarPilha } from '../core/scenes.js';
|
|
54
64
|
import { createTts } from '../platform/tts.js';
|
|
55
|
-
import { ensureAC, catNode, audioOut, soundOn, volume, audioCat, initAudioMixer, tonePan, audioCtx } from '../platform/audio.js';
|
|
65
|
+
import { ensureAC, catNode, audioOut, soundOn, volume, audioCat, initAudioMixer, tonePan, audioCtx, setCatGain } from '../platform/audio.js';
|
|
56
66
|
import { createAudioSonar } from '../platform/audio-sonar.js';
|
|
57
|
-
|
|
67
|
+
// A raiz é a camada que PODE conhecer os dois eixos: `render/` está abaixo dela, e é dela a tarefa de
|
|
68
|
+
// responder ao `platform/audio-sonar`, que não pode importar daqui sem inverter uma aresta (#104).
|
|
69
|
+
import { ehCego, ehBaixaVisao, PADRAO } from '../render/viz-axes.js';
|
|
58
70
|
import { OVERLAY_SCOPE_SELECTOR } from '../ui/settings-panel.js';
|
|
59
71
|
import { LOGICAL_W } from '../core/constants.js';
|
|
60
72
|
import { initSettingsPanel } from '../ui/settings-panel.js';
|
|
61
73
|
import { initMenuNav } from '../ui/menu-nav.js';
|
|
62
74
|
import { initKeyboardRuntime } from '../input/keyboard-runtime.js';
|
|
63
|
-
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';
|
|
64
78
|
import { installCvdFilters } from '../render/cvd-matrices.js';
|
|
65
79
|
/** Os ids que os painéis emprestados exigem do documento. Achado 6: sem eles o painel abre VAZIO, sem erro. */
|
|
66
80
|
const MARCACAO_EXIGIDA = ['#game-region', '#sr-status', '#sr-alert'];
|
|
81
|
+
/**
|
|
82
|
+
* Onde a engine procura a barra de acessibilidade quando o jogo não declara `host.a11yBarHost`.
|
|
83
|
+
*
|
|
84
|
+
* ⚠️ NÃO ENTROU NA `MARCACAO_EXIGIDA` de propósito, e a diferença é de mensagem e não de rigor. Aquela lista
|
|
85
|
+
* produz «marcação ausente: #x», que é o que se diz de um id que o jogo esqueceu. Aqui o que falta não é um
|
|
86
|
+
* id — é a barra inteira, e cinco jogos do catálogo não a têm porque ninguém lhes disse que a deviam ter. A
|
|
87
|
+
* frase própria pode explicar O QUE se perde, e é isso que a torna útil a quem a lê pela primeira vez.
|
|
88
|
+
*/
|
|
89
|
+
const SELETOR_BARRA_A11Y = '#title-icons';
|
|
67
90
|
/**
|
|
68
91
|
* Liga a engine para um jogo declarado.
|
|
69
92
|
*
|
|
@@ -108,6 +131,11 @@ export function createGame(o) {
|
|
|
108
131
|
getSoundOn: () => soundOn, getVolume: () => volume, getAudioCat: () => audioCat,
|
|
109
132
|
carregarVozNeural: o.carregarVozNeural,
|
|
110
133
|
});
|
|
134
|
+
if (!o.carregarVozNeural && !declines.semVozNeural) {
|
|
135
|
+
problems.push('sem voz neural: declare `carregarVozNeural` (uma linha — ver ADR-0094) ou `declines.semVozNeural`. '
|
|
136
|
+
+ 'Sem ela a criança que não lê fica com a voz do sistema, que em Chromebook de escola pode não existir '
|
|
137
|
+
+ 'em português');
|
|
138
|
+
}
|
|
111
139
|
/**
|
|
112
140
|
* O FILTRO DE VISÃO, aplicado ao MUNDO QUE O JOGO DECLAROU (ADR-0087).
|
|
113
141
|
*
|
|
@@ -147,25 +175,236 @@ export function createGame(o) {
|
|
|
147
175
|
const cvdFilters = installCvdFilters(o.host.cvdHost ?? null);
|
|
148
176
|
if (!cvdFilters)
|
|
149
177
|
problems.push('sem host de filtros (<svg>): a correção de daltonismo não foi montada');
|
|
178
|
+
// 4c. A BARRA DE ACESSIBILIDADE DA PRIMEIRA TELA. Ver a nota em `EngineHost.a11yBarHost`: cinco dos seis
|
|
179
|
+
// jogos do catálogo não têm nenhuma, e nada o dizia. Isto não a monta — diz que ela falta, que é o
|
|
180
|
+
// passo que tira o silêncio. A frase nomeia a saída, como as outras deste bloco fazem.
|
|
181
|
+
const a11yBar = o.host.a11yBarHost ?? $(SELETOR_BARRA_A11Y);
|
|
182
|
+
if (!a11yBar) {
|
|
183
|
+
problems.push(`sem barra de acessibilidade na primeira tela: declare \`host.a11yBarHost\` ou ponha um ${SELETOR_BARRA_A11Y} no documento. Sem ela a criança não alcança modo cego, TTS, alto contraste nem Libras antes de começar`);
|
|
184
|
+
}
|
|
185
|
+
/*
|
|
186
|
+
* ⚠️ E AGORA A ENGINE MONTA-A (ADR-0106 §4, etapa 2). Até 2026-09-08 esta raiz só REPORTAVA a ausência, e o
|
|
187
|
+
* registo dizia porquê: «reportar não é oferecer — cinco jogos continuam sem barra até alguém agir na
|
|
188
|
+
* linha». A etapa 1 tirou dos sete campos acidentais a obrigação de virem do jogo, e a 3 deu lista padrão
|
|
189
|
+
* ao menu; com isso o `PauseIconsCtx` deixou de exigir seja o que for que esta raiz não saiba responder.
|
|
190
|
+
*
|
|
191
|
+
* ⚠️ NÃO É `buildQuickBar`, e a diferença tem dono: aquele põe `tabIndex = -1` nos botões porque durante a
|
|
192
|
+
* partida dez paradas de tabulação separam a criança do jogo (ADR-0044 item 7). Na primeira tela não se
|
|
193
|
+
* está a jogar, e tirar os ícones da ordem de tabulação ali seria escondê-los de quem navega por teclado —
|
|
194
|
+
* exactamente a pessoa para quem eles existem.
|
|
195
|
+
*/
|
|
196
|
+
/**
|
|
197
|
+
* O LEITOR DO MODO CEGO — e ele tem de ler ONDE O ESCRITOR PADRÃO ESCREVE.
|
|
198
|
+
*
|
|
199
|
+
* 🔴 ESTAS DUAS METADES GANHARAM PADRÃO EM DIAS DIFERENTES E NÃO SE FALAVAM, o que produziu um defeito que
|
|
200
|
+
* nenhum teste podia ver. O escritor recebeu o padrão da engine na etapa 1b do ADR-0106
|
|
201
|
+
* (`ui/pause-icons` → `ctx.setModoCego ?? setModoCegoValue`), que grava no `core/state`. O leitor ficou com
|
|
202
|
+
* o `() => false` que já cá estava — uma CONSTANTE. Num jogo que não injecta `isBlindMode`:
|
|
203
|
+
*
|
|
204
|
+
* 1. a criança carrega no ícone → `setModoCego(!false)` → o modo LIGA de verdade;
|
|
205
|
+
* 2. o reflexo lê `false` → o ícone diz «desligado» e o anúncio diz o mesmo;
|
|
206
|
+
* 3. ela carrega outra vez → `setModoCegoValue(!false)` = `true` OUTRA VEZ → a guarda de igualdade do
|
|
207
|
+
* `core/state` devolve cedo → nada acontece.
|
|
208
|
+
*
|
|
209
|
+
* ⚠️ O modo cego ligava uma vez e NÃO HAVIA COMO DESLIGAR — um jogo que começa a descrever tudo em voz alta
|
|
210
|
+
* e não se cala, sem erro em lado nenhum. Para quem não depende dele, é o jogo a ficar inutilizável.
|
|
211
|
+
*
|
|
212
|
+
* 📌 UMA CONSTANTE E NÃO A EXPRESSÃO REPETIDA NOS DOIS SÍTIOS, porque a repetição É o defeito: duas
|
|
213
|
+
* respostas à mesma pergunta divergem, e foi assim que esta divergiu. `import * as state` dá ligação VIVA,
|
|
214
|
+
* então isto lê o valor de agora e não o do arranque.
|
|
215
|
+
*/
|
|
216
|
+
const lerModoCego = o.isBlindMode ?? (() => state.modoCego);
|
|
217
|
+
const pauseIcons = initPauseIcons({
|
|
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(),
|
|
231
|
+
getPlayers: () => o.players ?? [],
|
|
232
|
+
getNumPlayers: () => (o.players ?? [null]).length,
|
|
233
|
+
srSay, srAlert,
|
|
234
|
+
// ⚠️ NÃO `instanceof HTMLElement`: esse é um GLOBAL DO NAVEGADOR, e lê-lo onde ele não existe LANÇA —
|
|
235
|
+
// não devolve falso. Escrito assim na etapa 2, fazia o `reflectPauseIcons` rebentar em qualquer ambiente
|
|
236
|
+
// sem DOM. É o mesmo erro de forma do ACHADO 15 no cabeçalho deste ficheiro: alcançar o global por baixo
|
|
237
|
+
// de quem injectou o documento. A pergunta certa é a mesma que o `barraUsavel` faz — sabe ser uma barra?
|
|
238
|
+
getA11yBars: () => (barraUsavel && a11yBar ? [a11yBar] : []),
|
|
239
|
+
getModoCego: lerModoCego,
|
|
240
|
+
getAudioCat: () => audioCat,
|
|
241
|
+
setCatGain,
|
|
242
|
+
/*
|
|
243
|
+
* ⚠️ QUEM RESPONDE PELO APARELHO EM USO É A RAIZ, e é aqui que o autómato do ADR-0109 ganha o primeiro
|
|
244
|
+
* leitor. `input/state.entradaDe(i)` devolve `PADRAO` para quem nunca produziu uma aresta, logo isto
|
|
245
|
+
* nunca é `undefined` e o ícone nunca escreve numa chave torta.
|
|
246
|
+
*
|
|
247
|
+
* 📌 E é a RAIZ que o passa, não o ícone que o importa: `ui/` a ler estado de módulo de `input/` seria
|
|
248
|
+
* uma aresta nova entre camadas para poupar um argumento. A composição é o trabalho deste ficheiro.
|
|
249
|
+
*/
|
|
250
|
+
transporteEmUso: (i) => entradaDe(i).emUso,
|
|
251
|
+
reflectTtsPanel: () => { },
|
|
252
|
+
reflectTtsPanelEnabled: false,
|
|
253
|
+
isLibrasOn: vlibrasOpen,
|
|
254
|
+
toggleLibras,
|
|
255
|
+
});
|
|
256
|
+
/*
|
|
257
|
+
* ⚠️ O HOSPEDEIRO TEM DE SABER SER UMA BARRA, e perguntar isso não é zelo: `a11yBarHost` é `Element` no
|
|
258
|
+
* tipo, e um consumidor pode passar um duplo, um nó de outro documento, ou um elemento de um `<svg>`. Sem
|
|
259
|
+
* esta guarda, um objecto sem `addEventListener` derruba o BOOT INTEIRO — e derrubá-lo por causa da barra
|
|
260
|
+
* de acessibilidade seria tirar o jogo a toda a gente para não o dar a ninguém.
|
|
261
|
+
*
|
|
262
|
+
* Não sabendo, é `problems` como qualquer outra lacuna do hospedeiro: o consumidor lê e conserta.
|
|
263
|
+
*/
|
|
264
|
+
const barraUsavel = !!a11yBar
|
|
265
|
+
&& typeof a11yBar.addEventListener === 'function'
|
|
266
|
+
&& 'innerHTML' in a11yBar;
|
|
267
|
+
if (a11yBar && !barraUsavel) {
|
|
268
|
+
problems.push('o elemento da barra de acessibilidade não aceita conteúdo nem clique: os ícones não foram montados');
|
|
269
|
+
}
|
|
270
|
+
if (a11yBar && barraUsavel) {
|
|
271
|
+
a11yBar.innerHTML = iconsMarkup(pauseIcons.iconesMontados);
|
|
272
|
+
a11yBar.addEventListener('click', (e) => {
|
|
273
|
+
const botao = e.target?.closest('.pi-btn');
|
|
274
|
+
if (!botao)
|
|
275
|
+
return;
|
|
276
|
+
pauseIcons.iconAct(botao.dataset.pi ?? '', 0);
|
|
277
|
+
pauseIcons.reflectIconsIn(a11yBar, 0);
|
|
278
|
+
// O anúncio lê o `aria-label` DEPOIS do reflexo, porque é ele que carrega o estado NOVO — anunciar
|
|
279
|
+
// antes diria o estado que a criança acabou de deixar.
|
|
280
|
+
srSay(botao.getAttribute('aria-label') ?? '');
|
|
281
|
+
});
|
|
282
|
+
pauseIcons.reflectIconsIn(a11yBar, 0);
|
|
283
|
+
/*
|
|
284
|
+
* ⚠️ E OUTRA VEZ QUANDO O IDIOMA DO ARRANQUE CHEGAR — sem isto a barra fica no idioma de RECUO.
|
|
285
|
+
*
|
|
286
|
+
* O `initI18n` aplica pt de forma síncrona (para a página nunca ficar em branco) e, se o idioma
|
|
287
|
+
* preferido for outro, PEDE a troca — que é assíncrona, porque en/es são chunks sob demanda. Esta
|
|
288
|
+
* marcação nasce nesse intervalo. 📏 Medido num navegador em 2026-09-08, com `lang="en"`: a barra
|
|
289
|
+
* servia cinco rótulos em inglês e três ainda em português, na mesma linha de ícones.
|
|
290
|
+
*
|
|
291
|
+
* 📌 O `idiomaPronto()` existe exactamente para isto, e o cabeçalho dele já descreve o defeito noutro
|
|
292
|
+
* lugar: «o `applyDom` conserta o markup ESTÁTICO, mas o que o JavaScript monta tinha capturado o
|
|
293
|
+
* texto de pt e ninguém reconstruía». A barra é a instância nova, criada quando a ENGINE passou a
|
|
294
|
+
* montá-la (ADR-0106 etapa 2).
|
|
295
|
+
*
|
|
296
|
+
* ⚠️ É O `idiomaPronto()` E NÃO O EVENTO `i18n:change`, de propósito: o evento é a troca de idioma EM
|
|
297
|
+
* EXECUÇÃO, e o próprio `core/i18n` declara essa pergunta como sendo do Dev («QUANDO a interface se
|
|
298
|
+
* reconstrói ao trocar de idioma em execução»). Isto responde só a pergunta do ARRANQUE, que aquele
|
|
299
|
+
* mesmo comentário diz não ter duas respostas.
|
|
300
|
+
*/
|
|
301
|
+
void idiomaPronto().then(() => { pauseIcons.reflectIconsIn(a11yBar, 0); });
|
|
302
|
+
/*
|
|
303
|
+
* ⚠️ E ELA TEM DE CONTINUAR A DIZER A VERDADE quando o estado muda NOUTRO SÍTIO. O modo cego liga-se
|
|
304
|
+
* também pelo painel de áudio e pela simulação de empatia; sem esta assinatura, o ícone da barra ficaria
|
|
305
|
+
* a dizer «desligado» com `aria-pressed=false` depois de a criança o ter ligado — o controlo a mentir o
|
|
306
|
+
* estado, que é a família de defeito que o `reflectTTS` e o `#opt-modocego` já custaram a este projeto.
|
|
307
|
+
*
|
|
308
|
+
* 📌 SÓ O MODO CEGO, e a limitação é medida e não preguiça: dos ícones que esta raiz monta, ele é o ÚNICO
|
|
309
|
+
* cujo estado tem evento (`EventoDoJogo` tem `modoCego`; TTS, Libras, TEA e alternância não emitem nada).
|
|
310
|
+
* Os outros continuam a refletir-se ao clique, que é o caminho por onde hoje eles mudam.
|
|
311
|
+
*/
|
|
312
|
+
state.on('modoCego', () => { pauseIcons.reflectIconsIn(a11yBar, 0); });
|
|
313
|
+
}
|
|
314
|
+
// 4d. QUEM ABRIU A PAUSA, quando há mais de um assento — o achado 3 da auditoria do `game-soccer`.
|
|
315
|
+
//
|
|
316
|
+
// ⚠️ O PAINEL DE CONTROLE É PARAMETRIZADO PELO ASSENTO: `render(selPlayer)` desenha as posições DAQUELE
|
|
317
|
+
// esquema, e não há selector de assento — o `#ctrl-players` é uma FRASE, não abas. Quem decide o assento é
|
|
318
|
+
// o consumidor, passando o ator da pausa: «edita o controle de quem abriu o menu».
|
|
319
|
+
//
|
|
320
|
+
// ⚠️ E É AQUI QUE ISTO FICA MUDO. O `setPauseActor` desta raiz é `() => {}` — literal, logo abaixo. Um jogo
|
|
321
|
+
// montado por `createGame` com dois assentos deixa a criança do SEGUNDO sem como remapear, e nada o diz.
|
|
322
|
+
// Não é a mesma coisa que declarar `semAtorDePausa`: essa é uma ausência declarada, e uma ausência
|
|
323
|
+
// declarada é uma escolha. Esta era uma ausência por omissão, que é a forma de defeito do ADR-0106 §2.
|
|
324
|
+
const assentos = (o.players ?? []).length;
|
|
325
|
+
if (assentos > 1 && !declines.semAtorDePausa) {
|
|
326
|
+
problems.push(`declarou ${assentos} jogadores e não registra o ator da pausa: o painel de controle edita sempre o `
|
|
327
|
+
+ 'assento 0, então ninguém além do primeiro consegue remapear. Declare `declines.semAtorDePausa` se '
|
|
328
|
+
+ 'for de propósito');
|
|
329
|
+
}
|
|
330
|
+
/*
|
|
331
|
+
* 4e. O CARTÃO DE PAUSA DA PRIMEIRA TELA — e isto fecha um LAÇO QUE ESTAVA ABERTO.
|
|
332
|
+
*
|
|
333
|
+
* 📏 MEDIDO EM 2026-09-08, nos seis jogos do catálogo local: `#vp-pause-0` é procurado por esta raiz (o
|
|
334
|
+
* `getPauseMenu` do `initMenuNav`, mais abaixo) e **NENHUM jogo o cria**. `git grep vp-pause` devolve zero
|
|
335
|
+
* em `game-platformer`, `game-soccer`, `pixi-15-puzzle`, `2048`, `whackwhack` e `game-chess`. Ou seja: a
|
|
336
|
+
* engine inventou uma convenção, procurou-a, não a achou, e concluiu em silêncio que nenhum jogo tem menu
|
|
337
|
+
* de pausa — que é a MESMA forma de defeito do ADR-0106 §2, desta vez cometida pela engine contra si mesma.
|
|
338
|
+
*
|
|
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.
|
|
342
|
+
*/
|
|
343
|
+
const hospedeiroDaPausa = o.host.pauseHost ?? $('#game-region');
|
|
344
|
+
const pausaUsavel = !!hospedeiroDaPausa && typeof hospedeiroDaPausa.appendChild === 'function';
|
|
345
|
+
if (!pausaUsavel) {
|
|
346
|
+
problems.push('sem sítio para o menu de pausa: declare `host.pauseHost` ou tenha um #game-region que aceite filhos. '
|
|
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');
|
|
349
|
+
}
|
|
350
|
+
if (hospedeiroDaPausa && pausaUsavel) {
|
|
351
|
+
const cartao = pauseIcons.buildScreenPause(0);
|
|
352
|
+
// ⚠️ O ID É O QUE A PRÓPRIA ENGINE PROCURA, logo abaixo, no `getPauseMenu`. Montar sem o pôr deixaria o
|
|
353
|
+
// laço tão aberto como estava — o cartão existiria e a navegação de menu continuaria a não o achar.
|
|
354
|
+
cartao.id = 'vp-pause-0';
|
|
355
|
+
hospedeiroDaPausa.appendChild(cartao);
|
|
356
|
+
}
|
|
150
357
|
// 4b. NAVEGAÇÃO SONORA. Só o contrato entra: nada de tile, caixa de colisão ou array de moedas.
|
|
151
358
|
const sonar = createAudioSonar({
|
|
152
359
|
topology: () => o.declaration.topology(),
|
|
153
360
|
targetsOf: (i) => o.declaration.targetsOf(i),
|
|
154
361
|
nameAt: (at) => o.declaration.nameAt(at),
|
|
362
|
+
// Campo 2 + o barramento do mixer: o que o GUIA CONTÍNUO precisa e o bipe não precisava (#84 item 2). O
|
|
363
|
+
// `roleAt` é o que deixa a rota contornar parede; o `catNode`/`audioOut`/`getVolume` são o que põem um
|
|
364
|
+
// grafo PERMANENTE no mesmo cursor de volume que todo o resto do áudio usa.
|
|
365
|
+
roleAt: (at) => o.declaration.roleAt(at),
|
|
155
366
|
tonePan, srSay, narrate: (texto) => tts.narrate(texto),
|
|
156
|
-
|
|
367
|
+
catNode, audioOut, getVolume: () => volume,
|
|
368
|
+
// ⚠️ A RESPOSTA, E NÃO A TABELA (#104). O `platform/audio-sonar` recebia o `VIZ_BY_KEY` e atravessava-o
|
|
369
|
+
// com `pl.viz`; ele deixou de saber o que é um modo visual, e quem responde é aqui — a raiz é a única
|
|
370
|
+
// camada que conhece os dois eixos E pode importar de `render/`.
|
|
371
|
+
visaoComprometida: (pl) => {
|
|
372
|
+
const v = pl.visual;
|
|
373
|
+
return !!v && (ehCego(v) || ehBaixaVisao(v));
|
|
374
|
+
},
|
|
375
|
+
getModoCego: lerModoCego, LOGICAL_W,
|
|
157
376
|
// O jogador DERIVADO do foco: campo 4 respondendo "onde a criança está". Um jogo que não fornece lista
|
|
158
377
|
// ainda tem sonar, e é isso que faz a pilha de acessibilidade não ser acessório.
|
|
159
378
|
getPlayers: o.sonarPlayers ?? (() => {
|
|
160
379
|
const f = o.declaration.focusOf(0);
|
|
161
|
-
return f ? [{ i: 0, x: f.at.x, y: f.at.y,
|
|
380
|
+
return f ? [{ i: 0, x: f.at.x, y: f.at.y, visual: PADRAO }] : [];
|
|
162
381
|
}),
|
|
163
382
|
getNumPlayers: () => (o.players ?? [null]).length,
|
|
164
383
|
getAudioCtx: () => audioCtx, getSoundOn: () => soundOn, getAudioCat: () => audioCat,
|
|
165
384
|
});
|
|
166
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);
|
|
167
401
|
initKB();
|
|
168
|
-
|
|
402
|
+
// ⚠️ O ESQUEMA DE ARRANQUE ALCANÇA NADA, e diz isso com `null` em vez de com um objeto vazio (issue #118).
|
|
403
|
+
// Ele vive um instante — `assignControls()` logo abaixo substitui-o pelo esquema real —, mas enquanto vive
|
|
404
|
+
// é um `KeyScheme` como qualquer outro, e a única forma honesta de um esquema que não alcança nada é
|
|
405
|
+
// catorze ausências declaradas. Um `{}` fazia o tipo mentir sobre estar completo.
|
|
406
|
+
const semAlcance = Object.fromEntries(ACTIONS.map((a) => [a, null]));
|
|
407
|
+
const players = o.players ?? [{ ctrl: semAlcance }];
|
|
169
408
|
const keyboard = initKeyboardRuntime({
|
|
170
409
|
getKB: () => kb, getNumPlayers: () => players.length, getPlayers: () => players,
|
|
171
410
|
});
|
|
@@ -174,7 +413,7 @@ export function createGame(o) {
|
|
|
174
413
|
const nav = initMenuNav({
|
|
175
414
|
$, getActiveElement: () => doc.activeElement,
|
|
176
415
|
topVisibleOverlay: overlays.topVisibleOverlay, closeById: overlays.closeById,
|
|
177
|
-
getPauseMenu:
|
|
416
|
+
getPauseMenu: (i) => $(`#vp-pause-${i}`),
|
|
178
417
|
setPhase: o.setPhase ?? (() => { }),
|
|
179
418
|
setPauseActor: () => { },
|
|
180
419
|
srSay,
|
|
@@ -182,10 +421,25 @@ export function createGame(o) {
|
|
|
182
421
|
// que ele existe se vier desligado (a mesma razão de o modo cego nascer com TTS e sonar).
|
|
183
422
|
comIndice: o.comIndice ?? (() => true),
|
|
184
423
|
isNavigable: o.isNavigable ?? (() => true),
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
424
|
+
/*
|
|
425
|
+
* ⚠️ ESTE PADRÃO ERA `() => false` / `() => {}`, E DESDE HOJE ISSO SERIA UM BURACO QUE EU ABRI. O
|
|
426
|
+
* comentário que estava aqui dizia «um hospedeiro que não tenha barra de acessibilidade responde nunca e
|
|
427
|
+
* nunca chama nada» — verdade até a etapa 2 do ADR-0106, quando esta raiz passou a MONTAR a barra.
|
|
428
|
+
*
|
|
429
|
+
* Com a barra montada e estes dois em no-op, ela existiria e **não se conseguiria navegar por teclado nem
|
|
430
|
+
* por controle**: alcançável só por ponteiro. Para uma criança cega, que navega por teclado, uma barra
|
|
431
|
+
* que ela não alcança é o mesmo que barra nenhuma — e é exactamente o «oferece o caminho e depois
|
|
432
|
+
* recusa-o» que o §5 do ADR-0106 proíbe.
|
|
433
|
+
*
|
|
434
|
+
* A engine responde com a SUA instância, que é a mesma que montou a barra. Quem injecta continua a mandar.
|
|
435
|
+
*
|
|
436
|
+
* ⚠️ E FICA UMA METADE POR LIGAR, dita aqui em vez de descoberta: o `navBar` do `ui/menu-nav` recebe
|
|
437
|
+
* `(i, k)` e não o terceiro argumento `temStart`, que é a borda do botão de pausa — a SEGUNDA saída do
|
|
438
|
+
* modo (ADR-0044 item 7). No cartucho ela chega por outra rota (o encaminhador do gamepad, `main.ts:1470`)
|
|
439
|
+
* que esta raiz ainda não monta. Logo: o direcional navega a barra; sair por START, por enquanto, não.
|
|
440
|
+
*/
|
|
441
|
+
naBarraDe: o.naBarraDe ?? ((i) => pauseIcons.naBarraDe(i)),
|
|
442
|
+
navBar: o.navBar ?? ((i, k) => pauseIcons.navBar(i, k)),
|
|
189
443
|
isCapturing: () => false,
|
|
190
444
|
closePadWiz: () => { },
|
|
191
445
|
whichPlayer: (code) => keyboard.whichPlayer(code),
|
|
@@ -243,9 +497,30 @@ export function createGame(o) {
|
|
|
243
497
|
catch {
|
|
244
498
|
return true;
|
|
245
499
|
} },
|
|
500
|
+
/**
|
|
501
|
+
* O RATO (ADR-0112) — e a sonda é `any-pointer` de propósito, não `pointer`.
|
|
502
|
+
*
|
|
503
|
+
* ⚠️ `(pointer:fine)` descreve o ponteiro PRIMÁRIO, então um tablet com rato ligado responde «coarse» e
|
|
504
|
+
* o rato desaparecia — exactamente o aparelho que esta pergunta existe para achar. `any-pointer:fine` diz
|
|
505
|
+
* «ALGUM dos dispositivos apontadores é fino», que é a pergunta certa: para desenhar basta um.
|
|
506
|
+
*
|
|
507
|
+
* ⚠️ ELA ERRA, e a direcção do erro é a que se aceita: um stylus também responde `fine`, e é um ponteiro
|
|
508
|
+
* a sério — logo isso não é erro. O que pode faltar é um rato ligado depois do arranque, e por isso a
|
|
509
|
+
* sonda é uma FUNÇÃO, avaliada a cada pergunta, como as três acima.
|
|
510
|
+
*/
|
|
511
|
+
rato: () => { try {
|
|
512
|
+
return win.matchMedia('(any-pointer:fine)').matches;
|
|
513
|
+
}
|
|
514
|
+
catch {
|
|
515
|
+
return false;
|
|
516
|
+
} },
|
|
246
517
|
};
|
|
247
518
|
const acoesDoJogo = o.preset ? presetActions(o.preset) : [];
|
|
248
|
-
|
|
519
|
+
// O segundo eixo entra aqui, e vem do jogo (ADR-0104 §A): quantas posições ele segura ao mesmo tempo.
|
|
520
|
+
// ⚠️ O TERCEIRO EIXO ENTRA AQUI (ADR-0112), e vem do jogo tal como os outros dois. `?? false` e não um
|
|
521
|
+
// padrão inventado: o campo é opcional de propósito — ver a nota nele —, e a ausência significa «este jogo
|
|
522
|
+
// não desenha», que é a resposta certa para a esmagadora maioria dos trezentos.
|
|
523
|
+
const alcanceAqui = alcance(transportesPadrao(disponibilidade), acoesDoJogo, o.declaration.holdsAtOnce(), o.declaration.needsPointer?.() ?? false);
|
|
249
524
|
// ⚠️ SÓ APARECE QUANDO HÁ O QUE DIZER. Um aviso que aparece sempre deixa de ser lido, e um jogo cujas ações
|
|
250
525
|
// cabem no toque não tem nada a avisar — que é o caso comum e tem de continuar silencioso.
|
|
251
526
|
if (acoesDoJogo.length) {
|
|
@@ -264,5 +539,49 @@ export function createGame(o) {
|
|
|
264
539
|
criar: (tag) => doc.createElement(tag),
|
|
265
540
|
narrar: (texto) => tts.narrate(texto),
|
|
266
541
|
});
|
|
267
|
-
|
|
542
|
+
/*
|
|
543
|
+
* ⚠️ MOSTRAR REFAZ OS ITENS ANTES DE REVELAR, e a ordem é a regra: o §5 do ADR-0106 diz que a criança nunca
|
|
544
|
+
* vê um item que não acciona, e a tabela de acções deste jogo pode ter mudado desde a montagem. Revelar
|
|
545
|
+
* primeiro e refazer depois deixaria um piscar em que ela vê o que não pode usar.
|
|
546
|
+
*/
|
|
547
|
+
const pausa = {
|
|
548
|
+
mostrar: (i) => {
|
|
549
|
+
pauseIcons.reflectPauseIcons();
|
|
550
|
+
const cartao = $(`#vp-pause-${i}`);
|
|
551
|
+
if (cartao)
|
|
552
|
+
cartao.hidden = false;
|
|
553
|
+
},
|
|
554
|
+
esconder: (i) => {
|
|
555
|
+
const cartao = $(`#vp-pause-${i}`);
|
|
556
|
+
if (cartao)
|
|
557
|
+
cartao.hidden = true;
|
|
558
|
+
},
|
|
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
|
+
}
|
|
586
|
+
return { declaration: o.declaration, pausa, tts, overlays, nav, keyboard, sonar, aplicarFiltroDeVisao, cenas: criarPilha(), cvdFilters, problems, declines, aoFalhar, alcance: alcanceAqui };
|
|
268
587
|
}
|
|
@@ -1,23 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
2
|
export declare const LOGICAL_W = 320, LOGICAL_H = 180, TILE = 16;
|
|
3
|
-
export declare const COIN_TARGET = 10;
|
|
4
|
-
export declare const TUNE: {
|
|
5
|
-
jumpVel: number;
|
|
6
|
-
waterJump: number;
|
|
7
|
-
waterJumpRun: number;
|
|
8
|
-
waterStrokeFrames: number;
|
|
9
|
-
trampBase: number;
|
|
10
|
-
trampMax: number;
|
|
11
|
-
gravity: number;
|
|
12
|
-
hWalk: number;
|
|
13
|
-
hRun: number;
|
|
14
|
-
climbSpeed: number;
|
|
15
|
-
maxFall: number;
|
|
16
|
-
waterMaxFall: number;
|
|
17
|
-
hTurbo: number;
|
|
18
|
-
ultraJumpVel: number;
|
|
19
|
-
};
|
|
20
|
-
export declare const JUMP_BASE: number;
|
|
21
3
|
export declare const ANIM: {
|
|
22
4
|
walkHold: number;
|
|
23
5
|
runHold: number;
|
|
@@ -53,18 +35,8 @@ export type TileType = {
|
|
|
53
35
|
export declare const TILE_TYPES: Record<number, TileType>;
|
|
54
36
|
/** Machuca ao encostar (lava). */
|
|
55
37
|
export declare const ehPerigo: (t: number) => boolean;
|
|
56
|
-
/** Água: nada-se dentro, e o pulo funciona lá. */
|
|
57
|
-
export declare const ehAgua: (t: number) => boolean;
|
|
58
|
-
/** Escada: sobe e desce. */
|
|
59
|
-
export declare const ehEscada: (t: number) => boolean;
|
|
60
38
|
/** Trampolim: arremessa para cima. */
|
|
61
39
|
export declare const ehTrampolim: (t: number) => boolean;
|
|
62
|
-
/** Portão: barra até a chave abrir. */
|
|
63
|
-
export declare const ehPortao: (t: number) => boolean;
|
|
64
|
-
/** A chave que abre o portão. */
|
|
65
|
-
export declare const ehChave: (t: number) => boolean;
|
|
66
|
-
/** Ar de região secreta: o que a escuridão cobre até alguém entrar. */
|
|
67
|
-
export declare const ehSecreto: (t: number) => boolean;
|
|
68
40
|
export declare const TILE_COLOR: {
|
|
69
41
|
0: string;
|
|
70
42
|
1: string;
|
|
@@ -3,13 +3,22 @@
|
|
|
3
3
|
// TUNE é objeto mutável (o painel de debug ajusta propriedades), mas NUNCA reatribuído → o import const
|
|
4
4
|
// funciona (mutar propriedade é ok; reatribuir é que quebraria). Ver docs/plano-modularizacao.md.
|
|
5
5
|
export const LOGICAL_W = 320, LOGICAL_H = 180, TILE = 16;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
// ⚠️ `COIN_TARGET` e `TUNE` MUDARAM DE CASA EM 2026-09-07 — foram para `game/tuning.ts` do `game-platformer`
|
|
7
|
+
// (issue #63, etapa B, decisão do Dev). Tinham ZERO importadores dentro desta árvore e TODOS do outro lado
|
|
8
|
+
// da fronteira do pacote: não eram código morto, eram a superfície pública que o cartucho consumia.
|
|
9
|
+
//
|
|
10
|
+
// Uma engine que exporta a GRAVIDADE e a META DE MOEDAS está a decidir que todo jogo é uma plataforma de
|
|
11
|
+
// coletar coisas. `TUNE` é gravidade, velocidade de nado e curso do trampolim — a física DAQUELE jogo; um
|
|
12
|
+
// quiz não tem gravidade. `COIN_TARGET` é o objetivo, que é conteúdo.
|
|
13
|
+
//
|
|
14
|
+
// A etapa C (o segundo consumidor) é o que autorizou o corte: catorze achados, e nenhum reclama estes dois
|
|
15
|
+
// para a engine. O `ui/debug-panel` continua a afinar o `TUNE` ao vivo — ele sempre o recebeu por INJEÇÃO
|
|
16
|
+
// (`TUNE: Tune` no ctx), nunca por importação, e é por isso que a mudança não lhe custou nada.
|
|
17
|
+
// ⚠️ `JUMP_BASE` SAIU EM 2026-09-07 (issue #63, etapa B, decisão do Dev). Não tinha um único consumidor:
|
|
18
|
+
// nem na engine, nem no `game-platformer`, nem nos testes — fora UM, que verificava que
|
|
19
|
+
// `JUMP_BASE === jumpVel * sqrt(8/5)`, ou seja **reafirmava a própria definição**. Um teste que não pode
|
|
20
|
+
// falhar por motivo que importe, a manter viva uma constante que ninguém usa. A própria issue #63 já o
|
|
21
|
+
// nomeava no seu «achado solto».
|
|
13
22
|
// E15: cadência de animação (ticks por quadro) — regulável ao vivo no painel ?debug=true. Como TUNE, é objeto
|
|
14
23
|
// mutável (o debug ajusta propriedades) mas NUNCA reatribuído → import const funciona. andar 6; correr 8 (~8fps,
|
|
15
24
|
// pedido do José); idle 20; swim 24; cling 10; escada 8; flavor ~6s.
|
|
@@ -46,18 +55,26 @@ export const TILE_TYPES = {
|
|
|
46
55
|
const prop = (t, k) => !!TILE_TYPES[t]?.[k];
|
|
47
56
|
/** Machuca ao encostar (lava). */
|
|
48
57
|
export const ehPerigo = (t) => prop(t, 'hazard');
|
|
49
|
-
/** Água: nada-se dentro, e o pulo funciona lá. */
|
|
50
|
-
export const ehAgua = (t) => prop(t, 'water');
|
|
51
|
-
/** Escada: sobe e desce. */
|
|
52
|
-
export const ehEscada = (t) => prop(t, 'ladder');
|
|
53
58
|
/** Trampolim: arremessa para cima. */
|
|
54
59
|
export const ehTrampolim = (t) => prop(t, 'tramp');
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
60
|
+
// ⚠️ `ehAgua`, `ehEscada`, `ehPortao` e `ehSecreto` MUDARAM DE CASA EM 2026-09-07 — foram para
|
|
61
|
+
// `game/tile-flags.ts` do `game-platformer` (issue #63, etapa B). Tinham zero importadores aqui dentro.
|
|
62
|
+
//
|
|
63
|
+
// E as duas que ficaram — `ehPerigo` e `ehTrampolim` — ficaram por um motivo MEDIDO, não por simetria: a
|
|
64
|
+
// engine lê-as, e não por geometria. O `isSolidType` de `core/collision` torna perigo e trampolim SÓLIDOS
|
|
65
|
+
// no modo cadeira de rodas, e perigo sólido no modo cego. É uma regra de ACESSIBILIDADE — a criança em
|
|
66
|
+
// cadeira de rodas não cai no fosso — e essa é da engine.
|
|
67
|
+
//
|
|
68
|
+
// A engine precisa de saber que um tile é PERIGO; não precisa de saber que ele é ESCADA.
|
|
69
|
+
//
|
|
70
|
+
// ⚠️ O QUE FICA POR DECIDIR, e está registado na #63: a `TILE_TYPES` acima continua a carregar as bandeiras
|
|
71
|
+
// `water`, `ladder`, `gate`, `key` e `secreto`, que **nenhum módulo desta árvore lê**. Enquanto elas aqui
|
|
72
|
+
// estiverem, o jogo faz perguntas sobre uma tabela que a engine publica. O fim honesto é a tabela mudar de
|
|
73
|
+
// casa e o jogo declarar os papéis pelo `core/contract` (`roleOf`) — que é o mecanismo que já existe para
|
|
74
|
+
// isso. Não é execução: é a fronteira seguinte, e ela é decisão do Dev.
|
|
75
|
+
// ⚠️ `ehChave` SAIU EM 2026-09-07, pelo mesmo motivo e no mesmo passo que a `JUMP_BASE`: zero consumidores
|
|
76
|
+
// em lado nenhum — engine, testes e cartucho. Os outros predicados de tile ficam por enquanto porque o
|
|
77
|
+
// `game-platformer` os importa; estes dois não eram fronteira mal cortada, eram peso morto.
|
|
61
78
|
export const TILE_COLOR = {
|
|
62
79
|
0: '#0a0a14', 1: '#241f38', 2: '#6b6480', 3: '#2f6fae', 4: '#8a5a2b', 5: '#34e29b', 6: '#3a3a46',
|
|
63
80
|
7: '#7fdcff', 8: '#ffd23f', 9: '#ff5b3a', 10: '#9a8a6f', 11: '#ffe06a', 12: '#3a86ff', 13: '#8a5cff', 14: '#ff6fae',
|