@the-inclusionist/engine 6.36.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/LICENSE +661 -0
- package/README.md +107 -0
- package/app/css/style.css +622 -0
- package/app/public/vendor/fonts/andika-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/andika-400.woff2 +0 -0
- package/app/public/vendor/fonts/andika-700-ext.woff2 +0 -0
- package/app/public/vendor/fonts/andika-700.woff2 +0 -0
- package/app/public/vendor/fonts/atkinson-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/atkinson-400.woff2 +0 -0
- package/app/public/vendor/fonts/atkinson-700-ext.woff2 +0 -0
- package/app/public/vendor/fonts/atkinson-700.woff2 +0 -0
- package/app/public/vendor/fonts/atkinson-mono-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/atkinson-mono-400.woff2 +0 -0
- package/app/public/vendor/fonts/comicneue-400.woff2 +0 -0
- package/app/public/vendor/fonts/comicneue-700.woff2 +0 -0
- package/app/public/vendor/fonts/greatvibes-400.woff2 +0 -0
- package/app/public/vendor/fonts/inter-400.woff2 +0 -0
- package/app/public/vendor/fonts/inter-700.woff2 +0 -0
- package/app/public/vendor/fonts/lato-400.woff2 +0 -0
- package/app/public/vendor/fonts/lato-700.woff2 +0 -0
- package/app/public/vendor/fonts/lexend-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/lexend-400.woff2 +0 -0
- package/app/public/vendor/fonts/lexend-700-ext.woff2 +0 -0
- package/app/public/vendor/fonts/lexend-700.woff2 +0 -0
- package/app/public/vendor/fonts/literata-400.woff2 +0 -0
- package/app/public/vendor/fonts/literata-700.woff2 +0 -0
- package/app/public/vendor/fonts/newsreader-400.woff2 +0 -0
- package/app/public/vendor/fonts/newsreader-700.woff2 +0 -0
- package/app/public/vendor/fonts/opensans-400.woff2 +0 -0
- package/app/public/vendor/fonts/opensans-700.woff2 +0 -0
- package/app/public/vendor/fonts/pinyon-400.woff2 +0 -0
- package/app/public/vendor/fonts/quattro-400.woff2 +0 -0
- package/app/public/vendor/fonts/quattro-700.woff2 +0 -0
- package/app/public/vendor/fonts/sourcesans-400.woff2 +0 -0
- package/app/public/vendor/fonts/sourcesans-700.woff2 +0 -0
- package/app/public/vendor/fonts/sourceserif-400.woff2 +0 -0
- package/app/public/vendor/fonts/sourceserif-700.woff2 +0 -0
- package/app/public/vendor/fonts/ufcook-700.woff2 +0 -0
- package/app/public/vendor/fonts/ufmag-400.woff2 +0 -0
- package/app/public/vendor/fonts.css +88 -0
- package/dist-pkg/boot/create-game.d.ts +159 -0
- package/dist-pkg/boot/create-game.js +266 -0
- package/dist-pkg/core/a11y-sr.d.ts +4 -0
- package/dist-pkg/core/a11y-sr.js +20 -0
- package/dist-pkg/core/actions.d.ts +100 -0
- package/dist-pkg/core/actions.js +149 -0
- package/dist-pkg/core/anel.d.ts +17 -0
- package/dist-pkg/core/anel.js +31 -0
- package/dist-pkg/core/collision.d.ts +21 -0
- package/dist-pkg/core/collision.js +62 -0
- package/dist-pkg/core/constants.d.ts +84 -0
- package/dist-pkg/core/constants.js +64 -0
- package/dist-pkg/core/contract.d.ts +227 -0
- package/dist-pkg/core/contract.js +184 -0
- package/dist-pkg/core/dom-query.d.ts +9 -0
- package/dist-pkg/core/dom-query.js +25 -0
- package/dist-pkg/core/entity.d.ts +150 -0
- package/dist-pkg/core/entity.js +38 -0
- package/dist-pkg/core/escape-html.d.ts +16 -0
- package/dist-pkg/core/escape-html.js +32 -0
- package/dist-pkg/core/i18n.d.ts +71 -0
- package/dist-pkg/core/i18n.js +236 -0
- package/dist-pkg/core/layers.d.ts +39 -0
- package/dist-pkg/core/layers.js +71 -0
- package/dist-pkg/core/letter-grid.d.ts +55 -0
- package/dist-pkg/core/letter-grid.js +116 -0
- package/dist-pkg/core/loop.d.ts +17 -0
- package/dist-pkg/core/loop.js +36 -0
- package/dist-pkg/core/password.d.ts +34 -0
- package/dist-pkg/core/password.js +205 -0
- package/dist-pkg/core/rng.d.ts +20 -0
- package/dist-pkg/core/rng.js +61 -0
- package/dist-pkg/core/rotulo-acessivel.d.ts +17 -0
- package/dist-pkg/core/rotulo-acessivel.js +41 -0
- package/dist-pkg/core/run-state.d.ts +115 -0
- package/dist-pkg/core/run-state.js +40 -0
- package/dist-pkg/core/scenes.d.ts +68 -0
- package/dist-pkg/core/scenes.js +64 -0
- package/dist-pkg/core/screens.d.ts +16 -0
- package/dist-pkg/core/screens.js +28 -0
- package/dist-pkg/core/state.d.ts +138 -0
- package/dist-pkg/core/state.js +290 -0
- package/dist-pkg/core/tiles.d.ts +12 -0
- package/dist-pkg/core/tiles.js +61 -0
- package/dist-pkg/core/world.d.ts +4 -0
- package/dist-pkg/core/world.js +58 -0
- package/dist-pkg/educational/activities-registry.d.ts +47 -0
- package/dist-pkg/educational/activities-registry.js +95 -0
- package/dist-pkg/i18n/en.d.ts +3 -0
- package/dist-pkg/i18n/en.js +560 -0
- package/dist-pkg/i18n/es.d.ts +3 -0
- package/dist-pkg/i18n/es.js +558 -0
- package/dist-pkg/i18n/pt.d.ts +3 -0
- package/dist-pkg/i18n/pt.js +618 -0
- package/dist-pkg/input/default-bindings.d.ts +24 -0
- package/dist-pkg/input/default-bindings.js +122 -0
- package/dist-pkg/input/devices.d.ts +15 -0
- package/dist-pkg/input/devices.js +31 -0
- package/dist-pkg/input/edges.d.ts +58 -0
- package/dist-pkg/input/edges.js +49 -0
- package/dist-pkg/input/gamepad.d.ts +203 -0
- package/dist-pkg/input/gamepad.js +531 -0
- package/dist-pkg/input/keyboard-runtime.d.ts +61 -0
- package/dist-pkg/input/keyboard-runtime.js +83 -0
- package/dist-pkg/input/keyboard.d.ts +45 -0
- package/dist-pkg/input/keyboard.js +80 -0
- package/dist-pkg/input/keydown.d.ts +270 -0
- package/dist-pkg/input/keydown.js +381 -0
- package/dist-pkg/input/latch.d.ts +30 -0
- package/dist-pkg/input/latch.js +63 -0
- package/dist-pkg/input/pointer-space.d.ts +40 -0
- package/dist-pkg/input/pointer-space.js +51 -0
- package/dist-pkg/input/state.d.ts +11 -0
- package/dist-pkg/input/state.js +12 -0
- package/dist-pkg/input/touch-bindings.d.ts +210 -0
- package/dist-pkg/input/touch-bindings.js +365 -0
- package/dist-pkg/input/touch.d.ts +173 -0
- package/dist-pkg/input/touch.js +314 -0
- package/dist-pkg/input/transports.d.ts +90 -0
- package/dist-pkg/input/transports.js +96 -0
- package/dist-pkg/input/vocabulary-migration.d.ts +58 -0
- package/dist-pkg/input/vocabulary-migration.js +125 -0
- package/dist-pkg/platform/audio-ambient.d.ts +27 -0
- package/dist-pkg/platform/audio-ambient.js +89 -0
- package/dist-pkg/platform/audio-earcons.d.ts +24 -0
- package/dist-pkg/platform/audio-earcons.js +62 -0
- package/dist-pkg/platform/audio-jingles.d.ts +17 -0
- package/dist-pkg/platform/audio-jingles.js +54 -0
- package/dist-pkg/platform/audio-mixer.d.ts +14 -0
- package/dist-pkg/platform/audio-mixer.js +62 -0
- package/dist-pkg/platform/audio-nav.d.ts +51 -0
- package/dist-pkg/platform/audio-nav.js +99 -0
- package/dist-pkg/platform/audio-sonar.d.ts +71 -0
- package/dist-pkg/platform/audio-sonar.js +173 -0
- package/dist-pkg/platform/audio.d.ts +32 -0
- package/dist-pkg/platform/audio.js +191 -0
- package/dist-pkg/platform/interruptible-speech.d.ts +19 -0
- package/dist-pkg/platform/interruptible-speech.js +79 -0
- package/dist-pkg/platform/speech.d.ts +2 -0
- package/dist-pkg/platform/speech.js +35 -0
- package/dist-pkg/platform/storage.d.ts +87 -0
- package/dist-pkg/platform/storage.js +148 -0
- package/dist-pkg/platform/tts.d.ts +33 -0
- package/dist-pkg/platform/tts.js +142 -0
- package/dist-pkg/render/camera.d.ts +58 -0
- package/dist-pkg/render/camera.js +105 -0
- package/dist-pkg/render/canvas.d.ts +13 -0
- package/dist-pkg/render/canvas.js +26 -0
- package/dist-pkg/render/cenario-data.d.ts +127 -0
- package/dist-pkg/render/cenario-data.js +145 -0
- package/dist-pkg/render/city-tex.d.ts +63 -0
- package/dist-pkg/render/city-tex.js +228 -0
- package/dist-pkg/render/city-tiles.d.ts +11 -0
- package/dist-pkg/render/city-tiles.js +140 -0
- package/dist-pkg/render/crt.d.ts +19 -0
- package/dist-pkg/render/crt.js +92 -0
- package/dist-pkg/render/cvd-matrices.d.ts +35 -0
- package/dist-pkg/render/cvd-matrices.js +91 -0
- package/dist-pkg/render/draw.d.ts +149 -0
- package/dist-pkg/render/draw.js +221 -0
- package/dist-pkg/render/fx.d.ts +63 -0
- package/dist-pkg/render/fx.js +123 -0
- package/dist-pkg/render/hc-role-data.d.ts +14 -0
- package/dist-pkg/render/hc-role-data.js +24 -0
- package/dist-pkg/render/high-contrast.d.ts +109 -0
- package/dist-pkg/render/high-contrast.js +254 -0
- package/dist-pkg/render/lq-filter.d.ts +37 -0
- package/dist-pkg/render/lq-filter.js +94 -0
- package/dist-pkg/render/minimap.d.ts +11 -0
- package/dist-pkg/render/minimap.js +94 -0
- package/dist-pkg/render/parallax.d.ts +85 -0
- package/dist-pkg/render/parallax.js +158 -0
- package/dist-pkg/render/player-anim.d.ts +71 -0
- package/dist-pkg/render/player-anim.js +152 -0
- package/dist-pkg/render/port.d.ts +143 -0
- package/dist-pkg/render/port.js +29 -0
- package/dist-pkg/render/recycling-tex.d.ts +48 -0
- package/dist-pkg/render/recycling-tex.js +164 -0
- package/dist-pkg/render/scene-city.d.ts +77 -0
- package/dist-pkg/render/scene-city.js +181 -0
- package/dist-pkg/render/scene-parallax.d.ts +72 -0
- package/dist-pkg/render/scene-parallax.js +426 -0
- package/dist-pkg/render/scene-sky.d.ts +126 -0
- package/dist-pkg/render/scene-sky.js +295 -0
- package/dist-pkg/render/screen-pipeline.d.ts +134 -0
- package/dist-pkg/render/screen-pipeline.js +177 -0
- package/dist-pkg/render/set-cenario.d.ts +54 -0
- package/dist-pkg/render/set-cenario.js +103 -0
- package/dist-pkg/render/sprite-fx.d.ts +3 -0
- package/dist-pkg/render/sprite-fx.js +40 -0
- package/dist-pkg/render/textures.d.ts +75 -0
- package/dist-pkg/render/textures.js +240 -0
- package/dist-pkg/render/title-scene.d.ts +46 -0
- package/dist-pkg/render/title-scene.js +75 -0
- package/dist-pkg/render/viewports.d.ts +52 -0
- package/dist-pkg/render/viewports.js +185 -0
- package/dist-pkg/render/viz-axes.d.ts +98 -0
- package/dist-pkg/render/viz-axes.js +184 -0
- package/dist-pkg/render/viz-modes.d.ts +61 -0
- package/dist-pkg/render/viz-modes.js +74 -0
- package/dist-pkg/render/viz-setters.d.ts +130 -0
- package/dist-pkg/render/viz-setters.js +244 -0
- package/dist-pkg/render/weather.d.ts +96 -0
- package/dist-pkg/render/weather.js +171 -0
- package/dist-pkg/render/wheelchair-sprites.d.ts +24 -0
- package/dist-pkg/render/wheelchair-sprites.js +63 -0
- package/dist-pkg/render/world-tex.d.ts +18 -0
- package/dist-pkg/render/world-tex.js +87 -0
- package/dist-pkg/ui/activities-menu.d.ts +258 -0
- package/dist-pkg/ui/activities-menu.js +648 -0
- package/dist-pkg/ui/caa-sets.d.ts +39 -0
- package/dist-pkg/ui/caa-sets.js +65 -0
- package/dist-pkg/ui/changed-mark.d.ts +15 -0
- package/dist-pkg/ui/changed-mark.js +96 -0
- package/dist-pkg/ui/debug-panel.d.ts +67 -0
- package/dist-pkg/ui/debug-panel.js +173 -0
- package/dist-pkg/ui/dom.d.ts +24 -0
- package/dist-pkg/ui/dom.js +47 -0
- package/dist-pkg/ui/focus-trap.d.ts +71 -0
- package/dist-pkg/ui/focus-trap.js +99 -0
- package/dist-pkg/ui/fonts.d.ts +41 -0
- package/dist-pkg/ui/fonts.js +59 -0
- package/dist-pkg/ui/hud.d.ts +137 -0
- package/dist-pkg/ui/hud.js +229 -0
- package/dist-pkg/ui/item-announcement.d.ts +19 -0
- package/dist-pkg/ui/item-announcement.js +36 -0
- package/dist-pkg/ui/layout.d.ts +6 -0
- package/dist-pkg/ui/layout.js +54 -0
- package/dist-pkg/ui/loop-crash.d.ts +19 -0
- package/dist-pkg/ui/loop-crash.js +60 -0
- package/dist-pkg/ui/map-hub.d.ts +56 -0
- package/dist-pkg/ui/map-hub.js +138 -0
- package/dist-pkg/ui/menu-nav.d.ts +141 -0
- package/dist-pkg/ui/menu-nav.js +390 -0
- package/dist-pkg/ui/pause-icons.d.ts +327 -0
- package/dist-pkg/ui/pause-icons.js +620 -0
- package/dist-pkg/ui/reach-notice.d.ts +34 -0
- package/dist-pkg/ui/reach-notice.js +71 -0
- package/dist-pkg/ui/settings-audio.d.ts +139 -0
- package/dist-pkg/ui/settings-audio.js +544 -0
- package/dist-pkg/ui/settings-caa.d.ts +45 -0
- package/dist-pkg/ui/settings-caa.js +137 -0
- package/dist-pkg/ui/settings-controls.d.ts +100 -0
- package/dist-pkg/ui/settings-controls.js +152 -0
- package/dist-pkg/ui/settings-empathy.d.ts +62 -0
- package/dist-pkg/ui/settings-empathy.js +137 -0
- package/dist-pkg/ui/settings-motion.d.ts +95 -0
- package/dist-pkg/ui/settings-motion.js +282 -0
- package/dist-pkg/ui/settings-motor.d.ts +77 -0
- package/dist-pkg/ui/settings-motor.js +215 -0
- package/dist-pkg/ui/settings-panel.d.ts +76 -0
- package/dist-pkg/ui/settings-panel.js +187 -0
- package/dist-pkg/ui/settings-typo.d.ts +74 -0
- package/dist-pkg/ui/settings-typo.js +150 -0
- package/dist-pkg/ui/settings-visual.d.ts +111 -0
- package/dist-pkg/ui/settings-visual.js +252 -0
- package/dist-pkg/ui/shell.d.ts +239 -0
- package/dist-pkg/ui/shell.js +430 -0
- package/dist-pkg/ui/title.d.ts +36 -0
- package/dist-pkg/ui/title.js +60 -0
- package/dist-pkg/ui/vlibras.d.ts +11 -0
- package/dist-pkg/ui/vlibras.js +87 -0
- package/dist-pkg/ui/webcam.d.ts +7 -0
- package/dist-pkg/ui/webcam.js +88 -0
- package/docs/CREDITS.md +64 -0
- package/docs/LICENSES.md +132 -0
- package/package.json +115 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// core/a11y-sr.ts — anúncios para LEITOR DE TELA (Estágio 4, Tier 1). srSay = região aria-live "polite" (status);
|
|
3
|
+
// srAlert = região "assertive" (alertas). O padrão limpar→requestAnimationFrame→escrever força o leitor a
|
|
4
|
+
// reanunciar mesmo texto repetido. Também espelha a fala em LIBRAS: como ui/vlibras ainda não foi extraído, o
|
|
5
|
+
// vlibrasSay entra por INJEÇÃO (setVlibrasSay) — no-op até o game.js registrá-lo no boot (evita inverter a ordem
|
|
6
|
+
// do Tier 1). Depende só de ui/dom ($). As regiões #sr-status/#sr-alert vivem no index.html.
|
|
7
|
+
import { $ } from '../ui/dom.js';
|
|
8
|
+
let _vlibrasSay = () => { };
|
|
9
|
+
// Registra a fala em Libras (chamado no boot do game.js, quando vlibrasSay existe). Ver ui/vlibras (futuro).
|
|
10
|
+
export function setVlibrasSay(fn) { _vlibrasSay = fn; }
|
|
11
|
+
// Anúncio "polite" (status): não interrompe o que o leitor está falando.
|
|
12
|
+
export const srSay = (t) => { const el = $('#sr-status'); if (el) {
|
|
13
|
+
el.textContent = '';
|
|
14
|
+
requestAnimationFrame(() => { el.textContent = t; });
|
|
15
|
+
} _vlibrasSay(t); };
|
|
16
|
+
// Anúncio "assertive" (alerta): interrompe e fala já (erros/avisos importantes).
|
|
17
|
+
export const srAlert = (t) => { const el = $('#sr-alert'); if (el) {
|
|
18
|
+
el.textContent = '';
|
|
19
|
+
requestAnimationFrame(() => { el.textContent = t; });
|
|
20
|
+
} _vlibrasSay(t); };
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
/** As quatorze posições que a engine conhece. Nenhuma delas é uma palavra que uma criança leia. */
|
|
3
|
+
export declare const ACTIONS: readonly ["up", "down", "left", "right", "action1", "action2", "action3", "action4", "leftShoulder", "leftTrigger", "rightShoulder", "rightTrigger", "start", "select"];
|
|
4
|
+
export type Action = (typeof ACTIONS)[number];
|
|
5
|
+
/** As quatro direções, que todo transporte tem de alcançar. */
|
|
6
|
+
export declare const DIRECTIONS: readonly ["up", "down", "left", "right"];
|
|
7
|
+
/**
|
|
8
|
+
* Os oito verbos, em DUAS metades que se nomeiam por critérios diferentes, e isso é decisão e não descuido:
|
|
9
|
+
*
|
|
10
|
+
* · `action1`..`action4` — o losango. NUMERADOS, porque quatro posições em cruz não têm nomes que
|
|
11
|
+
* atravessem gêneros: o que uma plataforma chama de pulo, um quiz chama de confirmar.
|
|
12
|
+
* · `leftShoulder`, `leftTrigger`, `rightShoulder`, `rightTrigger` — NOMEADOS pela anatomia da mão: dois
|
|
13
|
+
* dedos por mão, um por cima do outro. ADR-0086.
|
|
14
|
+
*
|
|
15
|
+
* ⚠️ NÃO HÁ HIERARQUIA ENTRE ELES no contrato. A ordem é a de apresentação — no assistente do gamepad, na
|
|
16
|
+
* tela de remapeamento — e não uma escala de importância: um jogo pode usar `rightShoulder` e nenhuma outra.
|
|
17
|
+
*/
|
|
18
|
+
export declare const VERBS: readonly ["action1", "action2", "action3", "action4", "leftShoulder", "leftTrigger", "rightShoulder", "rightTrigger"];
|
|
19
|
+
/**
|
|
20
|
+
* As duas de SISTEMA. Não pertencem ao mundo do jogo: `start` pausa e retoma, `select` abre o que é da sessão.
|
|
21
|
+
*
|
|
22
|
+
* ⚠️ E É POR ISSO QUE A ENGINE PODE NOMEÁ-LAS e não pode nomear as outras. O ADR-0074 proíbe um nome abstrato
|
|
23
|
+
* chegar a uma pessoa porque `action1` não diz nada a ninguém — mas «start» e «select» são o que está escrito
|
|
24
|
+
* no próprio controle desde 1983, e a criança lê a legenda antes de ler a nossa.
|
|
25
|
+
*/
|
|
26
|
+
export declare const SYSTEM: readonly ["start", "select"];
|
|
27
|
+
/** O que a criança lê e ouve para uma posição: o nome, e a frase que o explica quando ela pergunta. */
|
|
28
|
+
export interface ActionWord {
|
|
29
|
+
/** «Pular», «Confirmar», «Colocar peça». NUNCA `action2` — nome abstrato que chega a uma pessoa é defeito. */
|
|
30
|
+
readonly label: string;
|
|
31
|
+
/**
|
|
32
|
+
* A versão CURTA, para onde não cabe a longa. Cai em `label` quando ausente.
|
|
33
|
+
*
|
|
34
|
+
* ⚠️ NÃO É PREFERÊNCIA DE ESTILO: É LARGURA MEDIDA. A legenda do título põe a palavra debaixo de um glifo,
|
|
35
|
+
* numa fileira de quatro, e `ui/shell` já trazia a nota de que ali não cabe o «Correr / interagir» que a
|
|
36
|
+
* lista de remapeamento usa — por isso o dicionário tinha DUAS famílias de chaves para as mesmas quatro
|
|
37
|
+
* ações, `act.*` e `legend.*`. A distinção é do jogo, não da engine, então atravessa com as palavras.
|
|
38
|
+
*
|
|
39
|
+
* ⚠️ E O RECUO PARA `label` DEGRADA VISUALMENTE, não funcionalmente: um jogo que não declare `short` vê a
|
|
40
|
+
* palavra longa espremida, e não uma legenda vazia. Escolhido assim porque uma legenda muda é pior do que
|
|
41
|
+
* uma legenda apertada — a primeira some para quem usa leitor de tela, a segunda não.
|
|
42
|
+
*/
|
|
43
|
+
readonly short?: string;
|
|
44
|
+
/** Opcional, para a tela de remapeamento: o que este botão faz, numa frase. */
|
|
45
|
+
readonly hint?: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* O vocabulário de UM jogo: para cada posição que ele usa, a palavra dele.
|
|
49
|
+
*
|
|
50
|
+
* ⚠️ PARCIAL DE PROPÓSITO. Um jogo declara SÓ as posições que usa. Exigir as quatorze obrigaria um quiz a
|
|
51
|
+
* inventar nome para um gatilho que ele não tem, e um nome inventado acaba numa tela de remapeamento à
|
|
52
|
+
* frente de uma criança.
|
|
53
|
+
*/
|
|
54
|
+
export type ActionPreset = Partial<Readonly<Record<Action, ActionWord>>>;
|
|
55
|
+
/** As posições que este preset nomeia, na ordem canônica de `ACTIONS`. */
|
|
56
|
+
export declare function presetActions(p: ActionPreset): Action[];
|
|
57
|
+
/**
|
|
58
|
+
* A pergunta que a engine faz ao jogo quando precisa MOSTRAR uma ação: «como é que isto se chama?».
|
|
59
|
+
*
|
|
60
|
+
* É a fronteira do corte em forma de função. A engine sabe que existe uma posição; só o jogo sabe a palavra,
|
|
61
|
+
* e é esta função que atravessa.
|
|
62
|
+
*/
|
|
63
|
+
export type ActionLabeller = (a: Action) => string;
|
|
64
|
+
/**
|
|
65
|
+
* Constrói o tradutor a partir de um preset.
|
|
66
|
+
*
|
|
67
|
+
* ⚠️ E O QUE ELE FAZ COM UMA AÇÃO SEM NOME É A DECISÃO INTEIRA. Devolver `action7` seria pôr um nome abstrato
|
|
68
|
+
* à frente de uma criança, que o ADR-0074 chama de defeito em tantas palavras. Devolver vazio calaria o leitor
|
|
69
|
+
* de tela, que é o defeito silencioso que `speakableProblems` já persegue noutro sítio.
|
|
70
|
+
*
|
|
71
|
+
* Devolve `null`, e quem chama decide: o assistente de controle NÃO PERGUNTA por uma ação que o jogo não
|
|
72
|
+
* nomeia — se o jogo não a usa, não há o que mapear. Uma ausência vira menos um passo, nunca um passo mudo.
|
|
73
|
+
*/
|
|
74
|
+
export declare function labellerFrom(p: ActionPreset): (a: Action) => string | null;
|
|
75
|
+
/**
|
|
76
|
+
* O mesmo, na versão CURTA — para a legenda do título, onde a palavra vive debaixo de um glifo.
|
|
77
|
+
*
|
|
78
|
+
* ⚠️ RECUA PARA `label`, e o recuo é a decisão: uma legenda apertada é pior que uma bonita e melhor que uma
|
|
79
|
+
* vazia. Vazia sumiria também para quem usa leitor de tela, que é o custo que não se aceita aqui.
|
|
80
|
+
*/
|
|
81
|
+
export declare function shortLabellerFrom(p: ActionPreset): (a: Action) => string | null;
|
|
82
|
+
/**
|
|
83
|
+
* Um preset é bem-formado? Devolve os problemas — VAZIA quer dizer conforme.
|
|
84
|
+
*
|
|
85
|
+
* ⚠️ O QUE ELE APANHA É O RÓTULO VAZIO, e é o mesmo defeito silencioso de `speakableProblems` em
|
|
86
|
+
* `core/contract`: um `label` em branco não quebra nada, não avisa ninguém, e deixa a tela de remapeamento
|
|
87
|
+
* com uma linha muda — que para quem usa leitor de tela é um botão que existe e não tem nome.
|
|
88
|
+
*/
|
|
89
|
+
export declare function presetProblems(p: ActionPreset | null | undefined): string[];
|
|
90
|
+
/** É uma ação conhecida? Guarda de fronteira para dado que veio de fora (mapa salvo, remapeamento). */
|
|
91
|
+
export declare function isAction(x: unknown): x is Action;
|
|
92
|
+
/**
|
|
93
|
+
* Uma declaração de ações usadas por um jogo é bem-formada? Devolve os problemas — VAZIA quer dizer conforme.
|
|
94
|
+
*
|
|
95
|
+
* ⚠️ EXIGIR AS QUATRO DIREÇÕES SERIA ERRADO, e a tentação é grande: um quiz navega por `up`/`down` e não usa
|
|
96
|
+
* `left`/`right`; um jogo de um botão não usa nenhuma. Quem decide o conjunto é o jogo. O que esta função
|
|
97
|
+
* confere é que o conjunto é NOMEÁVEL e não está vazio — um jogo sem ação nenhuma não tem como ser jogado, e
|
|
98
|
+
* hoje isso falharia em silêncio no primeiro transporte que tentasse ligar-se a nada.
|
|
99
|
+
*/
|
|
100
|
+
export declare function actionSetProblems(used: readonly string[] | null | undefined): string[];
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// core/actions — AS QUATORZE AÇÕES ABSTRATAS. A fonte única do vocabulário de entrada da engine.
|
|
3
|
+
//
|
|
4
|
+
// ========================= O QUE ISTO É =========================
|
|
5
|
+
// O ADR-0074 decidiu que a engine conhece POSIÇÕES, não verbos, e que o JOGO fornece as palavras. Este módulo
|
|
6
|
+
// é a lista dessas posições e nada mais: zero dependências, zero I/O, importável dos dois lados da fronteira.
|
|
7
|
+
//
|
|
8
|
+
// ⚠️ ATÉ AQUI A LISTA SÓ EXISTIA NUM REGISTRO. O código continua com os oito nomes de plataforma
|
|
9
|
+
// (`jump`, `run`, `swap`, `especial`) espalhados por `input/gamepad`, `input/edges`, `input/devices` e
|
|
10
|
+
// `input/keydown` — a migração é a issue #103 e não aconteceu. Este módulo é o alvo dela: um sítio para onde
|
|
11
|
+
// os transportes passam a apontar, escrito antes para que a migração tenha destino em vez de o inventar.
|
|
12
|
+
//
|
|
13
|
+
// ========================= POR QUE QUATORZE E NÃO NOVE =========================
|
|
14
|
+
// O ADR-0074 registrou nove e escreveu, entre as suas próprias desvantagens, que «nove é um TETO que um gênero
|
|
15
|
+
// pode querer exceder, e o registro tem de dizer o que acontece». O Dev excedeu-o em 2026-09-06, pedindo mais
|
|
16
|
+
// cinco para jogos mais elaborados. O ADR-0085 é o que acontece.
|
|
17
|
+
//
|
|
18
|
+
// ⚠️ E OS QUATRO NOVOS CHAMAM-SE `leftShoulder`, `leftTrigger`, `rightShoulder`, `rightTrigger` — NÃO
|
|
19
|
+
// `action5`..`action8`. Eu argumentei pelos números e o Dev decidiu contra, em 2026-09-06, com uma razão que
|
|
20
|
+
// o ADR-0086 registra: `action7` é ilegível no ponto onde alguém programa, e um vocabulário que ninguém
|
|
21
|
+
// consegue ler em voz alta não é abstração, é cifra. O ADR-0085 dizia o contrário; foi supersedido, não
|
|
22
|
+
// emendado.
|
|
23
|
+
//
|
|
24
|
+
// ⚠️ E A OBJEÇÃO CONTINUA ESCRITA, porque ela não some por a decisão ter sido outra: ombro e gatilho são
|
|
25
|
+
// FORMA DE GAMEPAD. Num teclado, `leftTrigger` é a tecla Y; num toque, um slot; num reconhecedor de fala, uma
|
|
26
|
+
// palavra. O ADR-0086 responde que a forma é ANATÔMICA antes de ser de gamepad — dois dedos por mão, um por
|
|
27
|
+
// cima do outro — e que isso atravessa transportes melhor do que um número atravessa. Quem discordar leia os
|
|
28
|
+
// dois registros, que é para isso que eles existem.
|
|
29
|
+
//
|
|
30
|
+
// `start` e `select` mantêm nome pelo critério que já valia para `start`: são FUNÇÃO, não posição — o que é
|
|
31
|
+
// do sistema e não do mundo do jogo.
|
|
32
|
+
/** As quatorze posições que a engine conhece. Nenhuma delas é uma palavra que uma criança leia. */
|
|
33
|
+
export const ACTIONS = [
|
|
34
|
+
'up', 'down', 'left', 'right',
|
|
35
|
+
'action1', 'action2', 'action3', 'action4',
|
|
36
|
+
'leftShoulder', 'leftTrigger', 'rightShoulder', 'rightTrigger',
|
|
37
|
+
'start', 'select',
|
|
38
|
+
];
|
|
39
|
+
/** As quatro direções, que todo transporte tem de alcançar. */
|
|
40
|
+
export const DIRECTIONS = ['up', 'down', 'left', 'right'];
|
|
41
|
+
/**
|
|
42
|
+
* Os oito verbos, em DUAS metades que se nomeiam por critérios diferentes, e isso é decisão e não descuido:
|
|
43
|
+
*
|
|
44
|
+
* · `action1`..`action4` — o losango. NUMERADOS, porque quatro posições em cruz não têm nomes que
|
|
45
|
+
* atravessem gêneros: o que uma plataforma chama de pulo, um quiz chama de confirmar.
|
|
46
|
+
* · `leftShoulder`, `leftTrigger`, `rightShoulder`, `rightTrigger` — NOMEADOS pela anatomia da mão: dois
|
|
47
|
+
* dedos por mão, um por cima do outro. ADR-0086.
|
|
48
|
+
*
|
|
49
|
+
* ⚠️ NÃO HÁ HIERARQUIA ENTRE ELES no contrato. A ordem é a de apresentação — no assistente do gamepad, na
|
|
50
|
+
* tela de remapeamento — e não uma escala de importância: um jogo pode usar `rightShoulder` e nenhuma outra.
|
|
51
|
+
*/
|
|
52
|
+
export const VERBS = [
|
|
53
|
+
'action1', 'action2', 'action3', 'action4',
|
|
54
|
+
'leftShoulder', 'leftTrigger', 'rightShoulder', 'rightTrigger',
|
|
55
|
+
];
|
|
56
|
+
/**
|
|
57
|
+
* As duas de SISTEMA. Não pertencem ao mundo do jogo: `start` pausa e retoma, `select` abre o que é da sessão.
|
|
58
|
+
*
|
|
59
|
+
* ⚠️ E É POR ISSO QUE A ENGINE PODE NOMEÁ-LAS e não pode nomear as outras. O ADR-0074 proíbe um nome abstrato
|
|
60
|
+
* chegar a uma pessoa porque `action1` não diz nada a ninguém — mas «start» e «select» são o que está escrito
|
|
61
|
+
* no próprio controle desde 1983, e a criança lê a legenda antes de ler a nossa.
|
|
62
|
+
*/
|
|
63
|
+
export const SYSTEM = ['start', 'select'];
|
|
64
|
+
/** As posições que este preset nomeia, na ordem canônica de `ACTIONS`. */
|
|
65
|
+
export function presetActions(p) {
|
|
66
|
+
return ACTIONS.filter((a) => p[a] !== undefined);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Constrói o tradutor a partir de um preset.
|
|
70
|
+
*
|
|
71
|
+
* ⚠️ E O QUE ELE FAZ COM UMA AÇÃO SEM NOME É A DECISÃO INTEIRA. Devolver `action7` seria pôr um nome abstrato
|
|
72
|
+
* à frente de uma criança, que o ADR-0074 chama de defeito em tantas palavras. Devolver vazio calaria o leitor
|
|
73
|
+
* de tela, que é o defeito silencioso que `speakableProblems` já persegue noutro sítio.
|
|
74
|
+
*
|
|
75
|
+
* Devolve `null`, e quem chama decide: o assistente de controle NÃO PERGUNTA por uma ação que o jogo não
|
|
76
|
+
* nomeia — se o jogo não a usa, não há o que mapear. Uma ausência vira menos um passo, nunca um passo mudo.
|
|
77
|
+
*/
|
|
78
|
+
export function labellerFrom(p) {
|
|
79
|
+
return (a) => {
|
|
80
|
+
const w = p[a];
|
|
81
|
+
return w && w.label.trim() ? w.label : null;
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* O mesmo, na versão CURTA — para a legenda do título, onde a palavra vive debaixo de um glifo.
|
|
86
|
+
*
|
|
87
|
+
* ⚠️ RECUA PARA `label`, e o recuo é a decisão: uma legenda apertada é pior que uma bonita e melhor que uma
|
|
88
|
+
* vazia. Vazia sumiria também para quem usa leitor de tela, que é o custo que não se aceita aqui.
|
|
89
|
+
*/
|
|
90
|
+
export function shortLabellerFrom(p) {
|
|
91
|
+
return (a) => {
|
|
92
|
+
const w = p[a];
|
|
93
|
+
if (!w)
|
|
94
|
+
return null;
|
|
95
|
+
const curto = w.short && w.short.trim() ? w.short : w.label;
|
|
96
|
+
return curto && curto.trim() ? curto : null;
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Um preset é bem-formado? Devolve os problemas — VAZIA quer dizer conforme.
|
|
101
|
+
*
|
|
102
|
+
* ⚠️ O QUE ELE APANHA É O RÓTULO VAZIO, e é o mesmo defeito silencioso de `speakableProblems` em
|
|
103
|
+
* `core/contract`: um `label` em branco não quebra nada, não avisa ninguém, e deixa a tela de remapeamento
|
|
104
|
+
* com uma linha muda — que para quem usa leitor de tela é um botão que existe e não tem nome.
|
|
105
|
+
*/
|
|
106
|
+
export function presetProblems(p) {
|
|
107
|
+
if (!p)
|
|
108
|
+
return ['preset: missing'];
|
|
109
|
+
const problemas = [];
|
|
110
|
+
const nomeadas = presetActions(p);
|
|
111
|
+
if (nomeadas.length === 0)
|
|
112
|
+
problemas.push('preset: names no action - the child would see an unlabelled control');
|
|
113
|
+
for (const chave of Object.keys(p)) {
|
|
114
|
+
if (!isAction(chave))
|
|
115
|
+
problemas.push(`preset: ${chave} is not an action`);
|
|
116
|
+
}
|
|
117
|
+
for (const a of nomeadas) {
|
|
118
|
+
const w = p[a];
|
|
119
|
+
if (!w || !w.label || !w.label.trim()) {
|
|
120
|
+
problemas.push(`preset: ${a} has an empty label - the remap screen would show a nameless button`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return problemas;
|
|
124
|
+
}
|
|
125
|
+
/** É uma ação conhecida? Guarda de fronteira para dado que veio de fora (mapa salvo, remapeamento). */
|
|
126
|
+
export function isAction(x) {
|
|
127
|
+
return typeof x === 'string' && ACTIONS.includes(x);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Uma declaração de ações usadas por um jogo é bem-formada? Devolve os problemas — VAZIA quer dizer conforme.
|
|
131
|
+
*
|
|
132
|
+
* ⚠️ EXIGIR AS QUATRO DIREÇÕES SERIA ERRADO, e a tentação é grande: um quiz navega por `up`/`down` e não usa
|
|
133
|
+
* `left`/`right`; um jogo de um botão não usa nenhuma. Quem decide o conjunto é o jogo. O que esta função
|
|
134
|
+
* confere é que o conjunto é NOMEÁVEL e não está vazio — um jogo sem ação nenhuma não tem como ser jogado, e
|
|
135
|
+
* hoje isso falharia em silêncio no primeiro transporte que tentasse ligar-se a nada.
|
|
136
|
+
*/
|
|
137
|
+
export function actionSetProblems(used) {
|
|
138
|
+
const p = [];
|
|
139
|
+
if (!used)
|
|
140
|
+
return ['action set: missing'];
|
|
141
|
+
if (used.length === 0)
|
|
142
|
+
return ['action set: empty - a game with no action cannot be played'];
|
|
143
|
+
const desconhecidas = used.filter((a) => !isAction(a));
|
|
144
|
+
if (desconhecidas.length)
|
|
145
|
+
p.push(`action set: unknown action(s) ${desconhecidas.join(', ')}`);
|
|
146
|
+
if (new Set(used).size !== used.length)
|
|
147
|
+
p.push('action set: has a repeated action');
|
|
148
|
+
return p;
|
|
149
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
/**
|
|
3
|
+
* ANDA UM PASSO NUM ANEL — passar do último volta ao primeiro, e antes do primeiro está o último.
|
|
4
|
+
*
|
|
5
|
+
* Era `clampIndex`, que prendia nas pontas "porque o original nunca faz wrap em lista de itens". O ADR-0044
|
|
6
|
+
* derrubou isso, e o motivo é de uso, não de gosto: com UM MENU POR TELA, toda lista pode ser um anel, e o
|
|
7
|
+
* item mais indesejado (`quit`) fica a UMA tecla do mais urgente (`resume`) sem estar perto dele. Quem não
|
|
8
|
+
* enxerga não varre a lista à procura do fim — ela pergunta "e antes do primeiro?" e recebe uma resposta.
|
|
9
|
+
*
|
|
10
|
+
* A XAG 106 permite o anel exatamente para menu LINEAR, e o proíbe para grade 2-D de blocos: ali "voltar ao
|
|
11
|
+
* primeiro" não tem significado espacial.
|
|
12
|
+
*
|
|
13
|
+
* ⚠️ NAVEGAR LISTA É ANEL; AJUSTAR VALOR É LIMITE. `selectStep` e `rangeStep` (em ui/menu-nav) continuam
|
|
14
|
+
* presos nas pontas, e a diferença é real: passar do volume máximo para o mínimo com uma tecla é um susto,
|
|
15
|
+
* não uma conveniência — e num jogo com pistas de áudio para cegueira, um susto de volume é dano.
|
|
16
|
+
*/
|
|
17
|
+
export declare function passoNoAnel(len: number, idx: number, delta: number): number;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// core/anel — A CONTA DO ANEL, e nada mais.
|
|
3
|
+
//
|
|
4
|
+
// Ela morava em `ui/menu-nav`, e saiu de lá quando o item 7 do ADR-0044 fez `ui/pause-icons` precisar dela
|
|
5
|
+
// também: `menu-nav` já importava `pause-icons` (por `mostrarSubmenuDaPausa` e `PM_ITENS_VISIVEIS`), e a volta
|
|
6
|
+
// fecharia um CICLO de importação. Ciclo em ESM não estoura na hora — estoura no boot, em TDZ, quando um dos
|
|
7
|
+
// dois lê o outro durante a avaliação. É o tipo de defeito que aparece uma vez, em produção, e some ao ser
|
|
8
|
+
// investigado.
|
|
9
|
+
//
|
|
10
|
+
// Módulo-folha de propósito: zero dependências, uma função, nenhuma I/O. `ui/menu-nav` continua a
|
|
11
|
+
// reexportando, porque é de lá que os testes e os outros menus já a importavam — o nome público não muda.
|
|
12
|
+
/**
|
|
13
|
+
* ANDA UM PASSO NUM ANEL — passar do último volta ao primeiro, e antes do primeiro está o último.
|
|
14
|
+
*
|
|
15
|
+
* Era `clampIndex`, que prendia nas pontas "porque o original nunca faz wrap em lista de itens". O ADR-0044
|
|
16
|
+
* derrubou isso, e o motivo é de uso, não de gosto: com UM MENU POR TELA, toda lista pode ser um anel, e o
|
|
17
|
+
* item mais indesejado (`quit`) fica a UMA tecla do mais urgente (`resume`) sem estar perto dele. Quem não
|
|
18
|
+
* enxerga não varre a lista à procura do fim — ela pergunta "e antes do primeiro?" e recebe uma resposta.
|
|
19
|
+
*
|
|
20
|
+
* A XAG 106 permite o anel exatamente para menu LINEAR, e o proíbe para grade 2-D de blocos: ali "voltar ao
|
|
21
|
+
* primeiro" não tem significado espacial.
|
|
22
|
+
*
|
|
23
|
+
* ⚠️ NAVEGAR LISTA É ANEL; AJUSTAR VALOR É LIMITE. `selectStep` e `rangeStep` (em ui/menu-nav) continuam
|
|
24
|
+
* presos nas pontas, e a diferença é real: passar do volume máximo para o mínimo com uma tecla é um susto,
|
|
25
|
+
* não uma conveniência — e num jogo com pistas de áudio para cegueira, um susto de volume é dano.
|
|
26
|
+
*/
|
|
27
|
+
export function passoNoAnel(len, idx, delta) {
|
|
28
|
+
if (len <= 0)
|
|
29
|
+
return 0;
|
|
30
|
+
return ((idx + delta) % len + len) % len; // o `+ len` extra: `%` de negativo em JS devolve negativo
|
|
31
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
export type CollisionCtx = {
|
|
3
|
+
world: number[][];
|
|
4
|
+
W: number;
|
|
5
|
+
H: number;
|
|
6
|
+
isWheelchair: () => boolean;
|
|
7
|
+
isModoCego: () => boolean;
|
|
8
|
+
caneDiv: () => number;
|
|
9
|
+
wcSolid: () => ReadonlySet<string>;
|
|
10
|
+
gateTiles: () => ReadonlySet<string>;
|
|
11
|
+
gateOpen: () => boolean;
|
|
12
|
+
};
|
|
13
|
+
export declare function initCollision(ctx: CollisionCtx): void;
|
|
14
|
+
export declare const caneBlockPx: () => number;
|
|
15
|
+
export declare const isSolidType: (t: number) => boolean;
|
|
16
|
+
export declare const tileAt: (tx: number, ty: number) => number;
|
|
17
|
+
export declare const solidTile: (x: number, y: number) => boolean;
|
|
18
|
+
export declare const solidAt: (tx: number, ty: number) => boolean;
|
|
19
|
+
export declare const surfTop: (x: number, y: number) => boolean;
|
|
20
|
+
export declare const isWcRampRiser: (col: number, row: number) => boolean;
|
|
21
|
+
export declare function rampSurfaceY(cx: number, py: number): number | null;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// core/collision.ts — consultas de colisão da GRADE do mundo (determinístico). Extraído do game.js (Estágio 4).
|
|
3
|
+
// Design testável: o módulo NÃO guarda cópias do estado mutável — recebe CLOSURES em initCollision() que leem o
|
|
4
|
+
// estado VIVO do game.js (wheelchair/modoCego/caneBlockDiv/wcSolid/gateTiles/gateOpen). Assim a colisão sempre
|
|
5
|
+
// enxerga o valor atual (sem risco de dessincronização) e os testes passam um ctx falso → funções 100% puras.
|
|
6
|
+
// TILE/TILE_TYPES são constantes (import direto). Consumido pela física (resolveX/resolveY/stepPlayer) no game.js.
|
|
7
|
+
import { TILE, TILE_TYPES, ehPerigo, ehTrampolim } from './constants.js';
|
|
8
|
+
// (`TileType` era declarado AQUI, privado, e era por isso que a tabela semântica não era usável de fora.
|
|
9
|
+
// Mudou de casa para `core/constants`, junto da tabela que ele descreve.)
|
|
10
|
+
const TYPES = TILE_TYPES;
|
|
11
|
+
// closures no-op até initCollision() (evita null-check no caminho quente da física).
|
|
12
|
+
let _world = [], _W = 0, _H = 0;
|
|
13
|
+
let _isWheelchair = () => false;
|
|
14
|
+
let _isModoCego = () => false;
|
|
15
|
+
let _caneDiv = () => 1;
|
|
16
|
+
let _wcSolid = () => new Set();
|
|
17
|
+
let _gateTiles = () => new Set();
|
|
18
|
+
let _gateOpen = () => true;
|
|
19
|
+
// Liga a colisão ao mundo + estado vivo. Chamado UMA vez no boot do game.js (após WORLD pronto). Idempotente.
|
|
20
|
+
export function initCollision(ctx) {
|
|
21
|
+
_world = ctx.world;
|
|
22
|
+
_W = ctx.W;
|
|
23
|
+
_H = ctx.H;
|
|
24
|
+
_isWheelchair = ctx.isWheelchair;
|
|
25
|
+
_isModoCego = ctx.isModoCego;
|
|
26
|
+
_caneDiv = ctx.caneDiv;
|
|
27
|
+
_wcSolid = ctx.wcSolid;
|
|
28
|
+
_gateTiles = ctx.gateTiles;
|
|
29
|
+
_gateOpen = ctx.gateOpen;
|
|
30
|
+
}
|
|
31
|
+
// distância (px) entre batidas de bengala = TILE / divisor.
|
|
32
|
+
export const caneBlockPx = () => TILE / _caneDiv();
|
|
33
|
+
// Um TIPO de tile é sólido? Cadeirante: PERIGO e TRAMPOLIM viram chão. Modo cego: o PERIGO vira chão.
|
|
34
|
+
// Pelas perguntas e não pelos números 9 e 5: a regra é sobre o que o tile É, e assim ela continua verdadeira
|
|
35
|
+
// num mapa com outra numeração — que é exatamente o que um segundo jogo tem.
|
|
36
|
+
export const isSolidType = (t) => ((_isWheelchair() && (ehPerigo(t) || ehTrampolim(t))) || (_isModoCego() && ehPerigo(t))) ? true : !!TYPES[t]?.solid;
|
|
37
|
+
// Tile na posição (fora do mundo = pedra(2), parede natural). Coordenadas em TILES.
|
|
38
|
+
export const tileAt = (tx, ty) => (tx < 0 || tx >= _W || ty < 0 || ty >= _H) ? 2 : _world[ty][tx];
|
|
39
|
+
// Sólido "de pisar" (inclui sólidos só-cadeirante). isSolidType já cobre lava/trampolim no modo cadeira.
|
|
40
|
+
export const solidTile = (x, y) => {
|
|
41
|
+
const t = tileAt(x, y);
|
|
42
|
+
return (_isWheelchair() && _wcSolid().has(x + ',' + y)) || isSolidType(t);
|
|
43
|
+
};
|
|
44
|
+
// Sólido "de barrar" (inclui portão fechado + sólidos só-cadeirante). Usado pela física.
|
|
45
|
+
export const solidAt = (tx, ty) => (!_gateOpen() && _gateTiles().has(tx + ',' + ty)) || (_isWheelchair() && _wcSolid().has(tx + ',' + ty)) || isSolidType(tileAt(tx, ty));
|
|
46
|
+
// Topo caminhável: sólido com ar logo acima (é uma superfície onde se anda).
|
|
47
|
+
export const surfTop = (x, y) => solidTile(x, y) && !solidTile(x, y - 1);
|
|
48
|
+
// Cadeirante: (col,row) é o "degrau" que uma rampa de 1 tile cobre? (superfície com vizinho-diagonal 1 acima)
|
|
49
|
+
export const isWcRampRiser = (col, row) => surfTop(col, row) && (surfTop(col - 1, row + 1) || surfTop(col + 1, row + 1));
|
|
50
|
+
// Altura (px) da diagonal 45° de uma rampa de 1 tile no x=cx do mundo; null se não há rampa por perto de py.
|
|
51
|
+
export function rampSurfaceY(cx, py) {
|
|
52
|
+
const tcx = Math.floor(cx / TILE), fx = cx - tcx * TILE, ty = Math.floor(py / TILE);
|
|
53
|
+
for (let y = ty - 1; y <= ty + 1; y++) {
|
|
54
|
+
if (y < 1 || !surfTop(tcx, y))
|
|
55
|
+
continue;
|
|
56
|
+
if (surfTop(tcx + 1, y - 1))
|
|
57
|
+
return y * TILE - fx; // degrau sobe p/ direita: y*TILE → (y-1)*TILE
|
|
58
|
+
if (surfTop(tcx - 1, y - 1))
|
|
59
|
+
return (y - 1) * TILE + fx; // degrau sobe p/ esquerda: (y-1)*TILE → y*TILE
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
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
|
+
export declare const ANIM: {
|
|
22
|
+
walkHold: number;
|
|
23
|
+
runHold: number;
|
|
24
|
+
idleHold: number;
|
|
25
|
+
swimHold: number;
|
|
26
|
+
clingHold: number;
|
|
27
|
+
climbHold: number;
|
|
28
|
+
flavorDelay: number;
|
|
29
|
+
};
|
|
30
|
+
export declare const EASY: {
|
|
31
|
+
grav: number;
|
|
32
|
+
jump: number;
|
|
33
|
+
speed: number;
|
|
34
|
+
pad: number;
|
|
35
|
+
slowFall: number;
|
|
36
|
+
tramp: number;
|
|
37
|
+
};
|
|
38
|
+
/** O que um tipo de tile é. Estava privado em `core/collision`; público aqui, que é onde a tabela mora. */
|
|
39
|
+
export type TileType = {
|
|
40
|
+
solid?: boolean;
|
|
41
|
+
bounce?: number;
|
|
42
|
+
water?: boolean;
|
|
43
|
+
jump?: boolean;
|
|
44
|
+
ladder?: boolean;
|
|
45
|
+
tramp?: boolean;
|
|
46
|
+
hazard?: boolean;
|
|
47
|
+
gate?: boolean;
|
|
48
|
+
key?: boolean;
|
|
49
|
+
/** Ar de REGIÃO SECRETA (o tile 0). A propriedade é nova; o significado não — `TILE_NAME[0]` já dizia
|
|
50
|
+
* "ar escuro/secreto". Estava só na tabela de NOMES, que ninguém consulta para decidir nada. */
|
|
51
|
+
secreto?: boolean;
|
|
52
|
+
};
|
|
53
|
+
export declare const TILE_TYPES: Record<number, TileType>;
|
|
54
|
+
/** Machuca ao encostar (lava). */
|
|
55
|
+
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
|
+
/** Trampolim: arremessa para cima. */
|
|
61
|
+
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
|
+
export declare const TILE_COLOR: {
|
|
69
|
+
0: string;
|
|
70
|
+
1: string;
|
|
71
|
+
2: string;
|
|
72
|
+
3: string;
|
|
73
|
+
4: string;
|
|
74
|
+
5: string;
|
|
75
|
+
6: string;
|
|
76
|
+
7: string;
|
|
77
|
+
8: string;
|
|
78
|
+
9: string;
|
|
79
|
+
10: string;
|
|
80
|
+
11: string;
|
|
81
|
+
12: string;
|
|
82
|
+
13: string;
|
|
83
|
+
14: string;
|
|
84
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// Constantes puras (imutáveis) do jogo — módulo-folha, ZERO deps. TypeScript (Estágio 3): tipos inferidos dos literais.
|
|
3
|
+
// TUNE é objeto mutável (o painel de debug ajusta propriedades), mas NUNCA reatribuído → o import const
|
|
4
|
+
// funciona (mutar propriedade é ok; reatribuir é que quebraria). Ver docs/plano-modularizacao.md.
|
|
5
|
+
export const LOGICAL_W = 320, LOGICAL_H = 180, TILE = 16;
|
|
6
|
+
export const COIN_TARGET = 10;
|
|
7
|
+
export const TUNE = {
|
|
8
|
+
jumpVel: 3.5, waterJump: 3.5, waterJumpRun: 4, waterStrokeFrames: 30,
|
|
9
|
+
trampBase: 5, trampMax: 8, gravity: 0.15, hWalk: 2, hRun: 3, climbSpeed: 1.5,
|
|
10
|
+
maxFall: 7, waterMaxFall: 3, hTurbo: 4.5, ultraJumpVel: 10, // E12: power-ups (valores do José)
|
|
11
|
+
};
|
|
12
|
+
export const JUMP_BASE = TUNE.jumpVel * Math.sqrt(8 / 5); // ~4.43 (altura confortável)
|
|
13
|
+
// E15: cadência de animação (ticks por quadro) — regulável ao vivo no painel ?debug=true. Como TUNE, é objeto
|
|
14
|
+
// mutável (o debug ajusta propriedades) mas NUNCA reatribuído → import const funciona. andar 6; correr 8 (~8fps,
|
|
15
|
+
// pedido do José); idle 20; swim 24; cling 10; escada 8; flavor ~6s.
|
|
16
|
+
export const ANIM = { walkHold: 6, runHold: 8, idleHold: 20, swimHold: 24, clingHold: 10, climbHold: 8, flavorDelay: 360 };
|
|
17
|
+
// Modo FÁCIL (acessibilidade motora): multiplicadores que suavizam a física. grav ×2/3, pulo ×8/7, velocidade
|
|
18
|
+
// ×0,7, zona-morta do pad ×4, queda lenta ×1,4, trampolim ×3,4. Lido pela física (game.js) e por jumpVel (player).
|
|
19
|
+
export const EASY = { grav: 2 / 3, jump: 8 / 7, speed: 0.7, pad: 4, slowFall: 1.4, tramp: 3.4 };
|
|
20
|
+
export const TILE_TYPES = {
|
|
21
|
+
0: { solid: false, secreto: true }, 1: { solid: false }, 2: { solid: true, bounce: 0.28 }, 3: { solid: false, water: true, jump: true },
|
|
22
|
+
4: { solid: false, ladder: true }, 5: { solid: true, bounce: 1.1, tramp: true }, 6: { solid: true, bounce: 0 },
|
|
23
|
+
7: { solid: false }, 8: { solid: false }, 9: { solid: false, hazard: true }, 10: { solid: true, gate: true },
|
|
24
|
+
11: { solid: false, key: true }, 12: { solid: false }, 13: { solid: false }, 14: { solid: false },
|
|
25
|
+
};
|
|
26
|
+
/* ===================== QUEM PODE IMPORTAR ESTAS PERGUNTAS =====================
|
|
27
|
+
*
|
|
28
|
+
* Nem todo módulo que lê tile pode ler a TABELA. Quatro de engine — `render/scene-sky`, `render/scene-city`,
|
|
29
|
+
* `platform/audio-ambient` e `platform/audio-nav` — recebem `tileAt` por INJEÇÃO, de propósito: é o que os
|
|
30
|
+
* deixa rodar no project `node` com um mundo de mentira, e é o que os faz não conhecer a numeração DESTE mapa.
|
|
31
|
+
* Importar a tabela neles trocaria um número mágico por uma dependência nova, o que é pior.
|
|
32
|
+
*
|
|
33
|
+
* Eles ficam com as comparações que têm até a decisão do eixo (#64): sob contrato declarado, quem recebe
|
|
34
|
+
* `tileAt` por injeção deve receber TAMBÉM a semântica, e a forma dessa entrega ainda não tem evidência que a
|
|
35
|
+
* escolha. Acrescentar campos de ctx agora seria chutar a forma da pergunta — o erro que o `menu-nav` ensinou
|
|
36
|
+
* a não cometer (`getPhase()` contra `isNavigable()`).
|
|
37
|
+
*
|
|
38
|
+
* Regra prática: importe daqui se o módulo já importa `core/collision`; se ele recebe `tileAt` por ctx, não.
|
|
39
|
+
*/
|
|
40
|
+
/* ===================== as perguntas, com nome =====================
|
|
41
|
+
* Uma função por propriedade, e não `TILE_TYPES[t]?.hazard` espalhado: o nome é o que torna a intenção legível
|
|
42
|
+
* no ponto de uso (`ehPerigo(t)` contra `t === 9`) e é o que dá UM lugar para mudar no dia em que a resposta
|
|
43
|
+
* deixar de vir de uma tabela global. Todas devolvem `false` para tipo desconhecido — um tile que ninguém
|
|
44
|
+
* declarou não machuca, não é água e não se escala; inventar semântica para o desconhecido é pior que negá-la.
|
|
45
|
+
*/
|
|
46
|
+
const prop = (t, k) => !!TILE_TYPES[t]?.[k];
|
|
47
|
+
/** Machuca ao encostar (lava). */
|
|
48
|
+
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
|
+
/** Trampolim: arremessa para cima. */
|
|
54
|
+
export const ehTrampolim = (t) => prop(t, 'tramp');
|
|
55
|
+
/** Portão: barra até a chave abrir. */
|
|
56
|
+
export const ehPortao = (t) => prop(t, 'gate');
|
|
57
|
+
/** A chave que abre o portão. */
|
|
58
|
+
export const ehChave = (t) => prop(t, 'key');
|
|
59
|
+
/** Ar de região secreta: o que a escuridão cobre até alguém entrar. */
|
|
60
|
+
export const ehSecreto = (t) => prop(t, 'secreto');
|
|
61
|
+
export const TILE_COLOR = {
|
|
62
|
+
0: '#0a0a14', 1: '#241f38', 2: '#6b6480', 3: '#2f6fae', 4: '#8a5a2b', 5: '#34e29b', 6: '#3a3a46',
|
|
63
|
+
7: '#7fdcff', 8: '#ffd23f', 9: '#ff5b3a', 10: '#9a8a6f', 11: '#ffe06a', 12: '#3a86ff', 13: '#8a5cff', 14: '#ff6fae',
|
|
64
|
+
};
|