@the-inclusionist/engine 7.0.0 → 8.0.0-rc.1
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/pressstart-400.woff2 +0 -0
- package/app/public/vendor/fonts.css +43 -2
- package/dist-pkg/boot/create-game.d.ts +76 -2
- package/dist-pkg/boot/create-game.js +271 -13
- package/dist-pkg/core/constants.d.ts +0 -28
- package/dist-pkg/core/constants.js +34 -17
- package/dist-pkg/core/contract.d.ts +39 -0
- package/dist-pkg/core/contract.js +36 -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 +33 -0
- package/dist-pkg/i18n/es.js +33 -0
- package/dist-pkg/i18n/pt.js +46 -0
- package/dist-pkg/input/default-bindings.d.ts +40 -0
- package/dist-pkg/input/default-bindings.js +143 -9
- package/dist-pkg/input/gamepad.d.ts +18 -11
- package/dist-pkg/input/gamepad.js +75 -9
- 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 +10 -0
- package/dist-pkg/input/keyboard.js +41 -11
- package/dist-pkg/input/keydown.d.ts +27 -2
- package/dist-pkg/input/keydown.js +20 -6
- 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/origem-sintetica.d.ts +44 -0
- package/dist-pkg/input/origem-sintetica.js +60 -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 +11 -2
- package/dist-pkg/input/touch-bindings.js +9 -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/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 +90 -0
- package/dist-pkg/platform/voice-plan.js +137 -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 +47 -1
- package/dist-pkg/ui/fonts.js +47 -9
- 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 +134 -20
- package/dist-pkg/ui/pause-icons.js +307 -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 +81 -3
- package/dist-pkg/ui/settings-motor.js +118 -9
- package/dist-pkg/ui/settings-typo.d.ts +32 -2
- package/dist-pkg/ui/settings-typo.js +64 -18
- 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 +18 -0
- package/docs/LICENSES.md +12 -0
- package/package.json +24 -4
- package/app/public/vendor/fonts/greatvibes-400.woff2 +0 -0
- package/app/public/vendor/fonts/ufcook-700.woff2 +0 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// input/transporte-em-uso — A ALTERNÂNCIA SEGUE O APARELHO EM USO (ADR-0109), na metade pura.
|
|
3
|
+
//
|
|
4
|
+
// ========================= O QUE ESTE MÓDULO É =========================
|
|
5
|
+
// O autómato das quatro regras do ADR-0109 §1, sem DOM, sem armazenamento e sem eventos. Recebe ARESTAS com
|
|
6
|
+
// origem e devolve estado; quem pergunta «há alternância agora?» pergunta ao estado.
|
|
7
|
+
//
|
|
8
|
+
// 1. Por padrão, controle e teclado, ambos SEM alternância.
|
|
9
|
+
// 2. Clique de mouse ou toque na tela → controles de tela COM alternância.
|
|
10
|
+
// 3. Apertar tecla devolve o teclado SEM alternância; usar o controle faz o mesmo.
|
|
11
|
+
// 4. Câmera e microfone precisam ser habilitados; habilitados, ligam a alternância em TODOS os outros
|
|
12
|
+
// controles, SEM possibilidade de desligar. São prioridade.
|
|
13
|
+
//
|
|
14
|
+
// ⚠️ POR QUE ISTO É UM AUTÓMATO E NÃO UM VALOR GUARDADO — e é o que o ADR-0109 supersede do ADR-0104 §C. A
|
|
15
|
+
// alternância era uma ESCOLHA guardada por transporte, e a issue #114 mediu que a fiação dela não era
|
|
16
|
+
// escrevível: a origem da aresta é apagada à porta (`input/state.keys` é `Set<string>` de CÓDIGOS, e o toque
|
|
17
|
+
// e a webcam escrevem lá dentro). O Dev decidiu o COMPORTAMENTO, e o comportamento escolhe o mecanismo.
|
|
18
|
+
//
|
|
19
|
+
// ⚠️ E A REGRA 3 É A RAZÃO DE PRECISAR DE DUAS COISAS, não de uma. «Apertar uma tecla devolve o teclado sem
|
|
20
|
+
// alternância» são DOIS factos: um EVENTO cuja origem tem de ser conhecida, e um MODO que persiste até à
|
|
21
|
+
// troca seguinte. Uma aresta não guarda estado; um modo guardado não detecta a própria troca. Nenhuma metade
|
|
22
|
+
// exprime a regra; juntas exprimem. Este ficheiro é a segunda metade — o MODO.
|
|
23
|
+
//
|
|
24
|
+
// 📌 A imagem do Dev, mantida porque diz a coisa: é um CAPS-LOCK NUM TECLADO COM MEMÓRIA. Modo e não estado
|
|
25
|
+
// momentâneo; lembrado por aparelho; trocar de aparelho não apaga o que o outro lembra.
|
|
26
|
+
/**
|
|
27
|
+
* A UNIÃO COMO VALOR, porque há um sítio onde ela tem de ser verificada em runtime.
|
|
28
|
+
*
|
|
29
|
+
* ⚠️ EXISTE POR CAUSA DE UMA FRONTEIRA, e é a única razão que a justifica: o `input/origem-sintetica` lê o
|
|
30
|
+
* transporte de um EXPANDO pendurado num `KeyboardEvent` — um objecto que este código não construiu e que
|
|
31
|
+
* qualquer script da página pode construir. Um valor que atravessa essa fronteira não é um `Transporte` por
|
|
32
|
+
* o TypeScript o dizer; é uma `string` até alguém a conferir. Sem lista, `'olho'` entrava no mapa de origens
|
|
33
|
+
* como transporte fantasma, e nada o diria.
|
|
34
|
+
*
|
|
35
|
+
* 📌 Não é o defeito da lista-ao-lado-da-união que este repositório já desfez três vezes (o `RM_KEYS`, os
|
|
36
|
+
* rótulos de movimento reduzido, as chaves de armazenamento), porque a guarda abaixo é do COMPILADOR: as duas
|
|
37
|
+
* não podem divergir. Uma cópia que não pode divergir é uma projecção, não uma segunda fonte.
|
|
38
|
+
*/
|
|
39
|
+
export const TRANSPORTES = ['teclado', 'gamepad', 'toque', 'olhos', 'rosto', 'gestos', 'fala'];
|
|
40
|
+
const _COBRE_OS_TRANSPORTES = true;
|
|
41
|
+
void _COBRE_OS_TRANSPORTES;
|
|
42
|
+
/**
|
|
43
|
+
* Isto que veio de fora é mesmo um transporte?
|
|
44
|
+
*
|
|
45
|
+
* ⚠️ A pergunta não é de segurança — os scripts desta página são todos da casa, e quem quisesse mentir usaria
|
|
46
|
+
* um valor VÁLIDO. É de correcção: impede que um carimbo errado ou ausente vire uma entrada silenciosa no
|
|
47
|
+
* `origemDaTecla`, que é a estrutura de que a alternância inteira depende.
|
|
48
|
+
*/
|
|
49
|
+
export function ehTransporte(v) {
|
|
50
|
+
return typeof v === 'string' && TRANSPORTES.includes(v);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* OS QUATRO QUE EXIGEM HABILITAÇÃO EXPLÍCITA e, uma vez habilitados, mandam em todos (regra 4).
|
|
54
|
+
*
|
|
55
|
+
* ⚠️ É a mesma lista do `UM_COMANDO_DE_CADA_VEZ` do `input/latch-scope`, e a coincidência não é acaso: são
|
|
56
|
+
* os transportes de quem NÃO CONSEGUE SEGURAR NADA. O que o ADR-0109 acrescenta é que eles não ligam a
|
|
57
|
+
* alternância só para si — ligam-na para o resto, porque quem usa a webcam pode também tocar na tela, e uma
|
|
58
|
+
* alternância que se desliga ao mudar de aparelho é uma armadilha para exactamente essa pessoa.
|
|
59
|
+
*/
|
|
60
|
+
export const EXIGEM_HABILITACAO = new Set(['olhos', 'rosto', 'gestos', 'fala']);
|
|
61
|
+
/** O transporte que liga a alternância por si só, sem prioridade nenhuma envolvida (regra 2). */
|
|
62
|
+
export const COM_ALTERNANCIA_PROPRIA = new Set(['toque']);
|
|
63
|
+
/**
|
|
64
|
+
* O ESTADO INICIAL: teclado, sem alternância.
|
|
65
|
+
*
|
|
66
|
+
* ⚠️ A regra 1 diz «controle E teclado, ambos sem alternância», e é por isso que o padrão pode nomear um só
|
|
67
|
+
* sem mentir: entre os dois a resposta à única pergunta que este módulo faz — há alternância? — é a MESMA.
|
|
68
|
+
* O `emUso` só passa a distingui-los quando alguém quiser MOSTRAR o aparelho corrente, que é outra questão
|
|
69
|
+
* e o ADR-0109 deixa-a explicitamente por decidir.
|
|
70
|
+
*/
|
|
71
|
+
export const PADRAO = Object.freeze({ emUso: 'teclado', assistidaLigada: false });
|
|
72
|
+
/**
|
|
73
|
+
* HÁ ALTERNÂNCIA AGORA? — ⚠️ **NÃO PERGUNTE ISTO A ESTA FUNÇÃO.** Ver o parágrafo abaixo.
|
|
74
|
+
*
|
|
75
|
+
* ⚠️ A prioridade da assistida vem PRIMEIRO, e a ordem é a regra 4 inteira: enquanto ela estiver ligada,
|
|
76
|
+
* nenhum outro aparelho a desliga — nem o teclado, que noutro caso a desligaria. Inverter estas duas linhas
|
|
77
|
+
* é o defeito que trancaria uma criança fora do próprio jogo, e é silencioso.
|
|
78
|
+
*
|
|
79
|
+
* @deprecated 🔴 **ESTA FUNÇÃO IMPLEMENTA O MODELO QUE O ADR-0113 SUPERSEDEU**, e fica exportada por ser
|
|
80
|
+
* superfície publicada (`./input/*.js`) e por o registo ter valor histórico — não por ser a resposta.
|
|
81
|
+
*
|
|
82
|
+
* O ADR-0109 decidia a alternância **só pelo aparelho**: assistida ligada → sim; toque → sim; todo o resto
|
|
83
|
+
* → não. O ADR-0113 retirou essa cláusula, com a razão do Dev: a alternância é um **caps-lock guardado com
|
|
84
|
+
* o mapeamento do controle**, e o valor que a criança gravou vale.
|
|
85
|
+
*
|
|
86
|
+
* 🔴 A DIVERGÊNCIA TEM UMA CRIANÇA CONCRETA, e é a que motivou o registo: quem tem dificuldade motora, joga
|
|
87
|
+
* no TECLADO e gravou a alternância ligada. Esta função devolve `false` para ela — `teclado` não está em
|
|
88
|
+
* `COM_ALTERNANCIA_PROPRIA` — e é exactamente o controle que lhe seria retirado. O `latch-scope.alternanciaDe`
|
|
89
|
+
* devolve `true`, porque lê o que ela gravou.
|
|
90
|
+
*
|
|
91
|
+
* ⚠️ E A CLÁUSULA DO TOQUE TAMBÉM CAIU: sob o ADR-0113 o toque é um transporte como os outros — o valor dele
|
|
92
|
+
* é escolha e fica guardado. Só olhos, rosto, gestos e fala podem recusar-se a DESLIGAR, e essa metade vive
|
|
93
|
+
* em `latch-scope.alternanciaSempreLigada`, com um conjunto diferente deste e a responder a outra pergunta.
|
|
94
|
+
*
|
|
95
|
+
* **A resposta certa é `latch-scope.alternanciaDe(estado.emUso, leitura)`.** O papel que sobra a este
|
|
96
|
+
* módulo é o que o nome dele diz: QUAL transporte está em uso — que é o que alimenta aquele primeiro
|
|
97
|
+
* argumento. `tests/alternancia-por-transporte.node.test.js` afirma que esta função continua sem consumidor.
|
|
98
|
+
*/
|
|
99
|
+
export function alternanciaAgora(estado) {
|
|
100
|
+
if (estado.assistidaLigada)
|
|
101
|
+
return true;
|
|
102
|
+
return COM_ALTERNANCIA_PROPRIA.has(estado.emUso);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* UMA ARESTA CHEGOU, com a sua origem. Devolve o estado NOVO.
|
|
106
|
+
*
|
|
107
|
+
* ⚠️ Uma aresta de um transporte assistido NÃO o habilita. Habilitar é um acto explícito (regra 4: «precisam
|
|
108
|
+
* ser habilitados»), e deixar uma aresta fazê-lo significaria que um falso positivo da webcam — uma sombra,
|
|
109
|
+
* um segundo rosto a passar — trancava a alternância de toda a gente sem ninguém ter pedido.
|
|
110
|
+
*/
|
|
111
|
+
export function aposAresta(estado, origem) {
|
|
112
|
+
if (estado.emUso === origem)
|
|
113
|
+
return estado; // sem mudança: devolve o MESMO objecto, não uma cópia
|
|
114
|
+
return { emUso: origem, assistidaLigada: estado.assistidaLigada };
|
|
115
|
+
}
|
|
116
|
+
/** A criança (ou quem a acompanha) habilitou câmera/microfone. Daqui em diante a alternância é lei. */
|
|
117
|
+
export function habilitarAssistida(estado) {
|
|
118
|
+
return estado.assistidaLigada ? estado : { emUso: estado.emUso, assistidaLigada: true };
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* DESABILITAR a assistida. Existe, e o ADR-0109 diz de quem é: NÃO é da criança durante a partida.
|
|
122
|
+
*
|
|
123
|
+
* ⚠️ Fica exportada porque desligar a câmera tem de ser possível em algum lugar — trocar de utilizador,
|
|
124
|
+
* fechar o jogo, um adulto a reconfigurar. O que o §4 proíbe é oferecê-la como um botão ao lado do jogo.
|
|
125
|
+
* Uma função que existe e não é oferecida é diferente de uma função que não existe: a primeira diz onde a
|
|
126
|
+
* decisão mora.
|
|
127
|
+
*/
|
|
128
|
+
export function desabilitarAssistida(estado) {
|
|
129
|
+
return estado.assistidaLigada ? { emUso: estado.emUso, assistidaLigada: false } : estado;
|
|
130
|
+
}
|
|
@@ -22,11 +22,38 @@ export declare const LUGARES: Readonly<{
|
|
|
22
22
|
toque: 9;
|
|
23
23
|
teclado: 14;
|
|
24
24
|
}>;
|
|
25
|
+
/**
|
|
26
|
+
* QUANTAS POSIÇÕES O CONTROLE DE TELA SEGURA AO MESMO TEMPO. Dois, e é uma DECLARAÇÃO (ADR-0104 §B).
|
|
27
|
+
*
|
|
28
|
+
* ⚠️ E A DECISÃO É NÃO PERGUNTAR AO APARELHO. O `navigator.maxTouchPoints` existe, responde depressa, e
|
|
29
|
+
* MENTE — mente para cima: muitos aparelhos anunciam cinco e reconhecem dois. Um modelo assente nele falha
|
|
30
|
+
* exactamente no telemóvel barato da escola pública, que é o pilar 1 deste projeto, e falha em silêncio: a
|
|
31
|
+
* criança tenta correr e pular ao mesmo tempo, não acontece nada, e ela conclui que o jogo está partido.
|
|
32
|
+
*
|
|
33
|
+
* Um piso declarado não pode errar para cima. Ele erra para baixo — um aparelho que segurava três fica
|
|
34
|
+
* servido por dois —, e esse erro tem conserto: a opção do terceiro botão, PROVADA POR TESTE, porque um
|
|
35
|
+
* gesto real é a única evidência que um aparelho não consegue falsificar. Nada é recusado e nada é assumido.
|
|
36
|
+
*
|
|
37
|
+
* ⚠️ NÃO É UM REGISTO DE TETOS POR TRANSPORTE, e o ADR diz isso em `more-information`: só o toque tem número
|
|
38
|
+
* declarado. O `holds` do `Transport` é opcional justamente por isso — ausente significa «não há tecto
|
|
39
|
+
* conhecido», não «segura uma». Um registo de tetos para teclado e controle é uma mudança maior, e nada
|
|
40
|
+
* precisa dela hoje.
|
|
41
|
+
*/
|
|
42
|
+
export declare const SEGURA_TOQUE = 2;
|
|
25
43
|
/** Como se descobre que cada transporte está aqui AGORA. Injetado: nenhuma destas perguntas é pura. */
|
|
26
44
|
export interface Disponibilidade {
|
|
27
45
|
gamepad: () => boolean;
|
|
28
46
|
toque: () => boolean;
|
|
29
47
|
teclado: () => boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Há um RATO aqui? (ADR-0112)
|
|
50
|
+
*
|
|
51
|
+
* ⚠️ ELE NÃO É UM TRANSPORTE À PARTE, e é por isso que entra como uma pergunta e não como uma quarta linha
|
|
52
|
+
* do `transportesPadrao`: um rato sozinho não carrega as catorze posições. Ele é o SINAL CONTÍNUO ao lado
|
|
53
|
+
* do teclado — a frase do Dev, «no caso do teclado, o sinal contínuo passa a ser o mouse» —, e é o que dá
|
|
54
|
+
* fundação aos transportes 8 e 9 do ADR-0074, que aquele registo declarava como não tendo nenhuma.
|
|
55
|
+
*/
|
|
56
|
+
rato: () => boolean;
|
|
30
57
|
}
|
|
31
58
|
/**
|
|
32
59
|
* Os três transportes que esta engine sabe oferecer hoje.
|
|
@@ -44,14 +71,43 @@ export interface Transport {
|
|
|
44
71
|
readonly id: string;
|
|
45
72
|
/** Quantos lugares ATRIBUÍVEIS. É o número que a aritmética da garantia usa. */
|
|
46
73
|
readonly slots: number;
|
|
74
|
+
/**
|
|
75
|
+
* Quantas posições ele SEGURA AO MESMO TEMPO, quando isso é conhecido. Eixo diferente dos `slots`: o
|
|
76
|
+
* controle de tela tem nove lugares e segura dois.
|
|
77
|
+
*
|
|
78
|
+
* ⚠️ AUSENTE SIGNIFICA «NÃO HÁ TECTO CONHECIDO», e não «segura uma». Hoje só o toque declara um número
|
|
79
|
+
* (`SEGURA_TOQUE`), porque só sobre ele há decisão — ver a nota lá. Ler a ausência como zero faria todo
|
|
80
|
+
* transporte sem número reprovar de repente, que é o oposto do que um campo opcional deve fazer.
|
|
81
|
+
*/
|
|
82
|
+
readonly holds?: number;
|
|
47
83
|
/**
|
|
48
84
|
* Está disponível NESTE aparelho, AGORA? Função e não valor: um controle é ligado no meio da partida, e
|
|
49
85
|
* uma tela de toque aparece quando a criança gira o tablet.
|
|
50
86
|
*/
|
|
51
87
|
readonly available: () => boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Este transporte oferece um PONTEIRO — posição contínua (ADR-0112)?
|
|
90
|
+
*
|
|
91
|
+
* ⚠️ FUNÇÃO E NÃO BOOLEANO, pela mesma razão escrita no `available` acima: um rato é ligado no meio da
|
|
92
|
+
* partida, tal como um controle. Um valor fixo aqui responderia com o estado do arranque.
|
|
93
|
+
*
|
|
94
|
+
* ⚠️ AUSENTE SIGNIFICA «NÃO OFERECE», e aqui — ao contrário do `holds` — ler a ausência assim é o correcto:
|
|
95
|
+
* o ponteiro é uma capacidade que se DECLARA, e um transporte que não a declara não a tem. O `holds` é o
|
|
96
|
+
* oposto porque lá a ausência é «não há tecto conhecido», e lê-la como zero reprovaria todo o mundo.
|
|
97
|
+
*/
|
|
98
|
+
readonly aponta?: () => boolean;
|
|
52
99
|
}
|
|
53
100
|
/** Este transporte carrega este conjunto de ações? Aritmética, como o ADR-0079 §3 a descreve. */
|
|
54
101
|
export declare function carries(t: Transport, acoes: readonly Action[]): boolean;
|
|
102
|
+
/**
|
|
103
|
+
* Este transporte SEGURA quantas o jogo pede ao mesmo tempo? (ADR-0104 §A.)
|
|
104
|
+
*
|
|
105
|
+
* ⚠️ TRANSPORTE SEM TECTO DECLARADO RESPONDE SIM, e a escolha é deliberada: o `holds` ausente quer dizer «não
|
|
106
|
+
* medimos isto», e recusar por falta de medida transformaria uma ignorância em acusação — o teclado e o
|
|
107
|
+
* controle passariam a reprovar todos os jogos por não terem número nenhum. Onde não há decisão, o modelo
|
|
108
|
+
* cala; é o toque que tem decisão, e é só ele que pode reprovar aqui.
|
|
109
|
+
*/
|
|
110
|
+
export declare function holds(t: Transport, pedidas: number): boolean;
|
|
55
111
|
/**
|
|
56
112
|
* Os transportes DISPONÍVEIS que carregam este conjunto.
|
|
57
113
|
*
|
|
@@ -69,10 +125,20 @@ export declare function carriedBy(lista: readonly Transport[], acoes: readonly A
|
|
|
69
125
|
export declare function reachable(lista: readonly Transport[], acoes: readonly Action[]): boolean;
|
|
70
126
|
/** O que a tela de seleção precisa dizer, e o que ela precisa saber para o dizer. */
|
|
71
127
|
export interface Alcance {
|
|
72
|
-
/**
|
|
128
|
+
/**
|
|
129
|
+
* Verdadeiro = pelo menos um transporte disponível carrega o conjunto **e segura quantas o jogo pede ao
|
|
130
|
+
* mesmo tempo**.
|
|
131
|
+
*
|
|
132
|
+
* ⚠️ A SEGUNDA METADE DESTA FRASE É NOVA (ADR-0104), e é o conserto do ponto cego que ninguém tinha
|
|
133
|
+
* nomeado: a plataforma declara nove ações, o toque tem nove lugares, o `ok` dizia sim — e correr, andar e
|
|
134
|
+
* pular ao mesmo tempo são três dedos que um telemóvel de dois não tem. O `ok` afirmava «dá para jogar»
|
|
135
|
+
* sobre um jogo que não dava, que é a pior coisa que este campo podia fazer.
|
|
136
|
+
*/
|
|
73
137
|
readonly ok: boolean;
|
|
74
138
|
/** Quantas ações o jogo pede. */
|
|
75
139
|
readonly pedidas: number;
|
|
140
|
+
/** Quantas ele pede SEGURAR ao mesmo tempo — o segundo eixo, e o que o `holdsAtOnce` declara. */
|
|
141
|
+
readonly seguraPedidas: number;
|
|
76
142
|
/** Os que serviriam se estivessem ligados — a informação acionável: «ligue um controle». */
|
|
77
143
|
readonly serviriamSeLigados: readonly string[];
|
|
78
144
|
/** Os disponíveis que NÃO cabem, com quantos lugares têm. Para a frase dizer o número. */
|
|
@@ -80,6 +146,26 @@ export interface Alcance {
|
|
|
80
146
|
readonly id: string;
|
|
81
147
|
readonly slots: number;
|
|
82
148
|
}[];
|
|
149
|
+
/**
|
|
150
|
+
* Os disponíveis que CHEGAM às ações mas não seguram quantas o jogo pede de uma vez, com o tecto deles.
|
|
151
|
+
*
|
|
152
|
+
* É a terceira frase do cartão da #112, e ela precisa dos dois números: «o controle de tela segura dois
|
|
153
|
+
* botões de cada vez e este jogo pede três». Sem o tecto, a frase diria que algo falta sem dizer o quê.
|
|
154
|
+
*/
|
|
155
|
+
readonly naoSeguram: readonly {
|
|
156
|
+
readonly id: string;
|
|
157
|
+
readonly holds: number;
|
|
158
|
+
}[];
|
|
159
|
+
/** Este jogo declarou que precisa de PONTEIRO? (ADR-0112) */
|
|
160
|
+
readonly pedePonteiro: boolean;
|
|
161
|
+
/**
|
|
162
|
+
* Os disponíveis que chegam às acções E seguram quantas o jogo pede, e falham SÓ por não apontarem.
|
|
163
|
+
*
|
|
164
|
+
* ⚠️ SÓ QUEM FALHA APENAS NISTO, pela mesma regra que o `naoSeguram` já segue: um transporte em duas listas
|
|
165
|
+
* faria o cartão da #112 dizer dois problemas onde há um, e a criança leria dois motivos para a mesma
|
|
166
|
+
* recusa. A lista é vazia quando o jogo não pede ponteiro — não «todos», porque nenhum falhou.
|
|
167
|
+
*/
|
|
168
|
+
readonly naoApontam: readonly string[];
|
|
83
169
|
}
|
|
84
170
|
/**
|
|
85
171
|
* Mede o alcance ANTES de a criança começar.
|
|
@@ -87,4 +173,4 @@ export interface Alcance {
|
|
|
87
173
|
* ⚠️ Devolve DADO e não texto. A frase é da interface e tem de passar por `t()`; devolver português daqui
|
|
88
174
|
* repetiria o defeito que o `PADWIZ_STEPS` acabou de deixar de cometer.
|
|
89
175
|
*/
|
|
90
|
-
export declare function alcance(lista: readonly Transport[], acoes: readonly Action[]): Alcance;
|
|
176
|
+
export declare function alcance(lista: readonly Transport[], acoes: readonly Action[], seguraPedidas: number, pedePonteiro?: boolean): Alcance;
|
|
@@ -40,6 +40,24 @@ import { ACTIONS } from '../core/actions.js';
|
|
|
40
40
|
* número cresce com ele e nunca passa a mentir.
|
|
41
41
|
*/
|
|
42
42
|
export const LUGARES = Object.freeze({ gamepad: 17, toque: 9, teclado: ACTIONS.length });
|
|
43
|
+
/**
|
|
44
|
+
* QUANTAS POSIÇÕES O CONTROLE DE TELA SEGURA AO MESMO TEMPO. Dois, e é uma DECLARAÇÃO (ADR-0104 §B).
|
|
45
|
+
*
|
|
46
|
+
* ⚠️ E A DECISÃO É NÃO PERGUNTAR AO APARELHO. O `navigator.maxTouchPoints` existe, responde depressa, e
|
|
47
|
+
* MENTE — mente para cima: muitos aparelhos anunciam cinco e reconhecem dois. Um modelo assente nele falha
|
|
48
|
+
* exactamente no telemóvel barato da escola pública, que é o pilar 1 deste projeto, e falha em silêncio: a
|
|
49
|
+
* criança tenta correr e pular ao mesmo tempo, não acontece nada, e ela conclui que o jogo está partido.
|
|
50
|
+
*
|
|
51
|
+
* Um piso declarado não pode errar para cima. Ele erra para baixo — um aparelho que segurava três fica
|
|
52
|
+
* servido por dois —, e esse erro tem conserto: a opção do terceiro botão, PROVADA POR TESTE, porque um
|
|
53
|
+
* gesto real é a única evidência que um aparelho não consegue falsificar. Nada é recusado e nada é assumido.
|
|
54
|
+
*
|
|
55
|
+
* ⚠️ NÃO É UM REGISTO DE TETOS POR TRANSPORTE, e o ADR diz isso em `more-information`: só o toque tem número
|
|
56
|
+
* declarado. O `holds` do `Transport` é opcional justamente por isso — ausente significa «não há tecto
|
|
57
|
+
* conhecido», não «segura uma». Um registo de tetos para teclado e controle é uma mudança maior, e nada
|
|
58
|
+
* precisa dela hoje.
|
|
59
|
+
*/
|
|
60
|
+
export const SEGURA_TOQUE = 2;
|
|
43
61
|
/**
|
|
44
62
|
* Os três transportes que esta engine sabe oferecer hoje.
|
|
45
63
|
*
|
|
@@ -51,15 +69,32 @@ export const LUGARES = Object.freeze({ gamepad: 17, toque: 9, teclado: ACTIONS.l
|
|
|
51
69
|
*/
|
|
52
70
|
export function transportesPadrao(d) {
|
|
53
71
|
return [
|
|
72
|
+
// ⚠️ O GAMEPAD NÃO DECLARA PONTEIRO, e a ausência é medida e não esquecimento: o stick tem o sinal
|
|
73
|
+
// contínuo e a engine deita-o fora na fonte (`PAD_DEAD = 0.5`, `PadState = Record<string, boolean>`).
|
|
74
|
+
// Ligá-lo ao ponteiro é possível e traz de volta uma pergunta que o ADR-0112 já deixou nomeada — metade
|
|
75
|
+
// do curso morta é ergonomia certa para um BOTÃO e errada para um CURSOR.
|
|
54
76
|
{ id: 'gamepad', slots: LUGARES.gamepad, available: d.gamepad },
|
|
55
|
-
|
|
56
|
-
{ id: '
|
|
77
|
+
// O teclado aponta QUANDO HÁ RATO — a cláusula do Dev, e a fundação dos transportes 8 e 9 do ADR-0074.
|
|
78
|
+
{ id: 'teclado', slots: LUGARES.teclado, available: d.teclado, aponta: d.rato },
|
|
79
|
+
// O toque aponta por natureza: a superfície É o ponteiro, e é o mesmo dedo que carrega nos botões.
|
|
80
|
+
{ id: 'toque', slots: LUGARES.toque, holds: SEGURA_TOQUE, available: d.toque, aponta: d.toque },
|
|
57
81
|
];
|
|
58
82
|
}
|
|
59
83
|
/** Este transporte carrega este conjunto de ações? Aritmética, como o ADR-0079 §3 a descreve. */
|
|
60
84
|
export function carries(t, acoes) {
|
|
61
85
|
return t.slots >= acoes.length;
|
|
62
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* Este transporte SEGURA quantas o jogo pede ao mesmo tempo? (ADR-0104 §A.)
|
|
89
|
+
*
|
|
90
|
+
* ⚠️ TRANSPORTE SEM TECTO DECLARADO RESPONDE SIM, e a escolha é deliberada: o `holds` ausente quer dizer «não
|
|
91
|
+
* medimos isto», e recusar por falta de medida transformaria uma ignorância em acusação — o teclado e o
|
|
92
|
+
* controle passariam a reprovar todos os jogos por não terem número nenhum. Onde não há decisão, o modelo
|
|
93
|
+
* cala; é o toque que tem decisão, e é só ele que pode reprovar aqui.
|
|
94
|
+
*/
|
|
95
|
+
export function holds(t, pedidas) {
|
|
96
|
+
return t.holds === undefined || t.holds >= pedidas;
|
|
97
|
+
}
|
|
63
98
|
/**
|
|
64
99
|
* Os transportes DISPONÍVEIS que carregam este conjunto.
|
|
65
100
|
*
|
|
@@ -85,12 +120,32 @@ export function reachable(lista, acoes) {
|
|
|
85
120
|
* ⚠️ Devolve DADO e não texto. A frase é da interface e tem de passar por `t()`; devolver português daqui
|
|
86
121
|
* repetiria o defeito que o `PADWIZ_STEPS` acabou de deixar de cometer.
|
|
87
122
|
*/
|
|
88
|
-
export function alcance(lista, acoes) {
|
|
123
|
+
export function alcance(lista, acoes, seguraPedidas, pedePonteiro = false) {
|
|
89
124
|
const disponiveis = lista.filter((t) => t.available());
|
|
125
|
+
/**
|
|
126
|
+
* ⚠️ PADRÃO `false` E NÃO PARÂMETRO OBRIGATÓRIO: os trezentos jogos que não desenham não podem sentir esta
|
|
127
|
+
* mudança, e um quarto argumento exigido faria cada chamador existente decidir hoje uma coisa que não lhe
|
|
128
|
+
* diz respeito.
|
|
129
|
+
*/
|
|
130
|
+
const apontaSeFor = (t) => !pedePonteiro || (!!t.aponta && t.aponta());
|
|
131
|
+
// ⚠️ «Serve» passou a ser TRÊS coisas. Foi DUAS na #114 (o `ok` dizia sim a quem não segurava três dedos), e
|
|
132
|
+
// é três desde o ADR-0112 — pela mesma razão das duas vezes: um `ok` verdadeiro sobre um jogo que a criança
|
|
133
|
+
// não consegue jogar é a pior coisa que este campo pode fazer.
|
|
134
|
+
const serve = (t) => carries(t, acoes) && holds(t, seguraPedidas) && apontaSeFor(t);
|
|
90
135
|
return {
|
|
91
|
-
ok:
|
|
136
|
+
ok: acoes.length > 0 && disponiveis.some(serve),
|
|
92
137
|
pedidas: acoes.length,
|
|
93
|
-
|
|
138
|
+
seguraPedidas,
|
|
139
|
+
pedePonteiro,
|
|
140
|
+
serviriamSeLigados: lista.filter((t) => !t.available() && serve(t)).map((t) => t.id),
|
|
94
141
|
curtos: disponiveis.filter((t) => !carries(t, acoes)).map((t) => ({ id: t.id, slots: t.slots })),
|
|
142
|
+
naoApontam: disponiveis
|
|
143
|
+
.filter((t) => carries(t, acoes) && holds(t, seguraPedidas) && !apontaSeFor(t))
|
|
144
|
+
.map((t) => t.id),
|
|
145
|
+
// ⚠️ SÓ QUEM CHEGA, e não quem já reprovou por lugares. Um transporte que aparecesse nas duas listas
|
|
146
|
+
// faria o cartão dizer duas coisas sobre o mesmo defeito, e a criança leria dois problemas onde há um.
|
|
147
|
+
naoSeguram: disponiveis
|
|
148
|
+
.filter((t) => carries(t, acoes) && !holds(t, seguraPedidas))
|
|
149
|
+
.map((t) => ({ id: t.id, holds: t.holds })),
|
|
95
150
|
};
|
|
96
151
|
}
|
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* ⚠️ O DADO SALVO NÃO É UM `KeyScheme`, e a issue #118 tornou isso um erro de compilação em vez de uma
|
|
4
|
+
* suposição. Um `KeyScheme` é FECHADO nas quatorze posições e completo; o que está no navegador da criança é
|
|
5
|
+
* uma SOBREPOSIÇÃO — parcial por construção (`loadKB` funde-a sobre os padrões com `Object.assign`) e capaz
|
|
6
|
+
* de carregar chaves que este código não conhece, o que o cabeçalho de `migrarEsquema` já dizia com todas as
|
|
7
|
+
* letras: «chave desconhecida atravessa intacta».
|
|
8
|
+
*
|
|
9
|
+
* Dar-lhe o tipo fechado obrigaria este ficheiro a inventar as posições que faltam no dado antigo — quer
|
|
10
|
+
* dizer, a escrever teclas que a criança nunca escolheu, no exacto módulo que existe para não lhe perder o
|
|
11
|
+
* remapeamento. O tipo aberto é o honesto aqui, e é só aqui.
|
|
12
|
+
*/
|
|
13
|
+
export type EsquemaSalvo = Record<string, readonly string[]>;
|
|
3
14
|
/**
|
|
4
15
|
* Nome de plataforma → posição abstrata. **ADR-0086 §2**, e não o ADR-0074.
|
|
5
16
|
*
|
|
@@ -38,11 +49,11 @@ export declare function migrarMapaDeToque(mapa: Record<string, string> | null |
|
|
|
38
49
|
export declare function migrarMapaDeControle<T>(mapa: Record<string, T> | null | undefined): Record<string, T> | null;
|
|
39
50
|
/** O objeto salvo, tal como `input/keyboard` o persiste. `p34` é o formato mais antigo de todos. */
|
|
40
51
|
export interface SavedKB {
|
|
41
|
-
solo?:
|
|
42
|
-
p2?:
|
|
43
|
-
p3?:
|
|
44
|
-
p4?:
|
|
45
|
-
p34?: (
|
|
52
|
+
solo?: EsquemaSalvo;
|
|
53
|
+
p2?: EsquemaSalvo[];
|
|
54
|
+
p3?: EsquemaSalvo[];
|
|
55
|
+
p4?: EsquemaSalvo[];
|
|
56
|
+
p34?: (EsquemaSalvo | null)[];
|
|
46
57
|
}
|
|
47
58
|
/**
|
|
48
59
|
* Traduz UM esquema salvo do vocabulário antigo para o abstrato.
|
|
@@ -53,6 +64,6 @@ export interface SavedKB {
|
|
|
53
64
|
* sumirem. É também o que torna esta função IDEMPOTENTE: aplicada sobre um esquema já migrado, nenhuma chave
|
|
54
65
|
* casa e o resultado é igual à entrada, o que importa porque `loadKB` pode correr mais de uma vez na sessão.
|
|
55
66
|
*/
|
|
56
|
-
export declare function migrarEsquema(esquema:
|
|
67
|
+
export declare function migrarEsquema(esquema: EsquemaSalvo | null | undefined): EsquemaSalvo | null;
|
|
57
68
|
/** Traduz o objeto salvo inteiro — o esquema solo e as listas por contagem de jogadores. */
|
|
58
69
|
export declare function migrarSalvo(s: SavedKB | null | undefined): SavedKB | null;
|
|
@@ -1,9 +1,37 @@
|
|
|
1
1
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* A definição de UM earcon. A tabela é do JOGO (item 19); esta é a forma que a engine sabe tocar.
|
|
4
|
+
*
|
|
5
|
+
* ⚠️ EXPORTADA DESDE 2026-09-07 (#124), e a falta era um defeito de interface: o `game-platformer` declarava
|
|
6
|
+
* a sua própria cópia deste tipo, palavra por palavra, porque não tinha como o nomear. Uma forma que cada
|
|
7
|
+
* consumidor redescobre por cópia é uma forma que diverge — foi assim que cinco cópias de `Gfx` divergiram
|
|
8
|
+
* nesta árvore, e está escrito noutro módulo.
|
|
9
|
+
*/
|
|
10
|
+
export interface SfxDef {
|
|
11
|
+
/** O timbre. */
|
|
3
12
|
t: OscillatorType;
|
|
13
|
+
/** A frequência inicial, em hertz. */
|
|
4
14
|
f: number;
|
|
15
|
+
/** A duração, em segundos. */
|
|
5
16
|
d: number;
|
|
17
|
+
/** A CHAVE de i18n da legenda (a11y surdez). Quem exibe resolve. */
|
|
6
18
|
cap?: string;
|
|
19
|
+
/**
|
|
20
|
+
* A frequência FINAL, em hertz. Ausente = nota parada, que é o que sempre houve.
|
|
21
|
+
*
|
|
22
|
+
* ⚠️ ELE EXISTE PORQUE UM EARCON PRECISA DE PODER IR PARA ALGUM LADO (#124). Medido ao construir o
|
|
23
|
+
* `game-soccer`: marcar e sofrer golo têm de ser distinguíveis **só de ouvido** — uma criança cega ouve a
|
|
24
|
+
* sala reagir e precisa de saber para que lado antes de a narração chegar. O desenho óbvio é uma figura
|
|
25
|
+
* que SOBE para o golo dela e DESCE para o do outro, e a tabela não o sabia dizer. O que sobrava era
|
|
26
|
+
* agudo-e-longo contra grave-e-curto: distinguível, e menos informação do que o momento carrega.
|
|
27
|
+
*
|
|
28
|
+
* ⚠️ E A CAPACIDADE JÁ ESTAVA NESTE FICHEIRO, sem ser alcançável da tabela: o `doorSound` faz exatamente
|
|
29
|
+
* isto, com `frequency.exponentialRampToValueAtTime`. O conserto não é síntese nova — é abrir a porta.
|
|
30
|
+
*
|
|
31
|
+
* A rampa é EXPONENCIAL e não linear porque a altura é percebida em razão e não em diferença: uma rampa
|
|
32
|
+
* linear de 200 a 800 sobe depressa no início e devagar no fim, e ouve-se torta.
|
|
33
|
+
*/
|
|
34
|
+
f2?: number;
|
|
7
35
|
}
|
|
8
36
|
export interface AudioEarconsCtx {
|
|
9
37
|
SFX: Record<string, SfxDef | undefined>;
|
|
@@ -21,4 +49,3 @@ export interface AudioEarcons {
|
|
|
21
49
|
doorSound: (mat: string) => void;
|
|
22
50
|
}
|
|
23
51
|
export declare function createAudioEarcons(ctx: AudioEarconsCtx): AudioEarcons;
|
|
24
|
-
export {};
|
|
@@ -30,6 +30,16 @@ export function createAudioEarcons(ctx) {
|
|
|
30
30
|
g.gain.value = 0.0001;
|
|
31
31
|
o.connect(g).connect(ctx.catNode('earcons') || ctx.audioOut() || ac.destination);
|
|
32
32
|
const t = ac.currentTime, vol = ctx.getVolume();
|
|
33
|
+
// A FIGURA, quando a tabela pede uma (#124). `setValueAtTime` antes da rampa como no `doorSound`: sem
|
|
34
|
+
// ele o ponto de partida da curva fica por conta da implementação, e o glissando começa onde calhar.
|
|
35
|
+
//
|
|
36
|
+
// ⚠️ AS DUAS GUARDAS SÃO NECESSÁRIAS E NÃO ZELO. `exponentialRampToValueAtTime` LANÇA com alvo zero ou
|
|
37
|
+
// negativo — uma tabela com `f2: 0` mataria o earcon inteiro pelo `catch`, em silêncio. E `f2 === f`
|
|
38
|
+
// não é rampa nenhuma: pedi-la ao navegador seria trabalho para produzir a nota parada que já havia.
|
|
39
|
+
if (typeof c.f2 === 'number' && c.f2 > 0 && c.f2 !== c.f) {
|
|
40
|
+
o.frequency.setValueAtTime(c.f, t);
|
|
41
|
+
o.frequency.exponentialRampToValueAtTime(c.f2, t + c.d);
|
|
42
|
+
}
|
|
33
43
|
g.gain.exponentialRampToValueAtTime(Math.max(0.02, 0.25 * vol), t + 0.01);
|
|
34
44
|
g.gain.exponentialRampToValueAtTime(0.0001, t + c.d);
|
|
35
45
|
o.start(t);
|
|
@@ -14,7 +14,7 @@ export type { PlayerCtxOut };
|
|
|
14
14
|
* globais ambientes em qualquer arquivo desta árvore. O motivo verdadeiro é de CAMADA: `_ac` e `_acOut` não
|
|
15
15
|
* são da entidade da engine, e por isso o `core/entity` deixou de mencioná-los (ADR-0039, opção A1).
|
|
16
16
|
*/
|
|
17
|
-
type Player = PlayerView<'x' | 'y' | 'facing' | 'viz' | 'i' | 'audioSink' | 'wnT' | '
|
|
17
|
+
type Player = PlayerView<'x' | 'y' | 'facing' | 'viz' | 'i' | 'audioSink' | 'wnT' | 'pad'> & PlayerAudioOut & Pick<ControlledPlayer, 'ctrl'>;
|
|
18
18
|
export interface AudioNavCtx {
|
|
19
19
|
tileAt: (x: number, y: number) => number;
|
|
20
20
|
solidAt: (x: number, y: number) => boolean;
|