@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,52 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
import type { RenderizarEm } from './port.js';
|
|
3
|
+
/** O que `pixiFilterFor` toca de um `PIXI.ColorMatrixFilter` — e só isso. */
|
|
4
|
+
interface ColorMatrixLike {
|
|
5
|
+
matrix: number[];
|
|
6
|
+
brightness(b: number, multiply: boolean): void;
|
|
7
|
+
contrast(amount: number, multiply: boolean): void;
|
|
8
|
+
}
|
|
9
|
+
interface ColorMatrixCtor {
|
|
10
|
+
new (): ColorMatrixLike;
|
|
11
|
+
}
|
|
12
|
+
/** `PIXI.BlurFilter` é opaco aqui: só se constrói com a força e se entrega para o `filters` do sprite. */
|
|
13
|
+
interface BlurCtor {
|
|
14
|
+
new (strength: number): unknown;
|
|
15
|
+
}
|
|
16
|
+
/** O sprite reaproveitado para carimbar o overlay de baixa visão — só a textura é trocada. */
|
|
17
|
+
interface TexturedSprite {
|
|
18
|
+
texture: unknown;
|
|
19
|
+
}
|
|
20
|
+
/** `app.renderer` — só a passada extra em render-texture (`clear:false` = por cima da cena já desenhada). */
|
|
21
|
+
export interface ViewportsCtx {
|
|
22
|
+
ColorMatrixFilter: ColorMatrixCtor | null | undefined;
|
|
23
|
+
BlurFilter: BlurCtor | null | undefined;
|
|
24
|
+
parallaxTexNormal: unknown[];
|
|
25
|
+
getTreeTexNormal: () => unknown;
|
|
26
|
+
getLvOverlaySpr: () => TexturedSprite;
|
|
27
|
+
renderizarEm: RenderizarEm;
|
|
28
|
+
getVpTex: () => unknown[];
|
|
29
|
+
cvdDefsHost: Element | null;
|
|
30
|
+
}
|
|
31
|
+
export interface ViewportsApi {
|
|
32
|
+
/** Camada de parallax `i` no `mode`: alto contraste recua o fundo (dessatura/escurece); resto = textura crua. */
|
|
33
|
+
parallaxTexFor(i: number, mode: string): unknown;
|
|
34
|
+
/** Decoração de fundo (árvore) no `mode`: mesma regra do parallax, cache próprio. */
|
|
35
|
+
treeTexFor(mode: string): unknown;
|
|
36
|
+
/** Quadro do jogador no `mode`: alto contraste ganha contorno escuro (salta do fundo recuado). */
|
|
37
|
+
playerVizTex(base: unknown, mode: string): unknown;
|
|
38
|
+
/** Filtro GPU do viewport para o `mode` (array de filtros, ou `null` quando o modo não usa filtro). */
|
|
39
|
+
pixiFilterFor(mode: string): unknown;
|
|
40
|
+
/** Canvas 320×180 do overlay de baixa visão (`haze`/`tunnel`/`macular`/`diabetic`). */
|
|
41
|
+
lvOverlayCanvas(lv: string): HTMLCanvasElement;
|
|
42
|
+
/** Textura do overlay de baixa visão (memoizada). `blur` não tem overlay — é filtro puro → `null`. */
|
|
43
|
+
lvOverlayTex(lv: string): unknown;
|
|
44
|
+
/** Carimba o overlay de baixa visão do jogador `i` DENTRO da render-texture do viewport, por cima da cena. */
|
|
45
|
+
renderVpOverlay(i: number, mode: string): void;
|
|
46
|
+
/** Invalida o cache de parallax recolorido — o cenário mudou, as texturas cruas são outras. */
|
|
47
|
+
clearParallaxTexCache(): void;
|
|
48
|
+
/** Invalida o cache de quadros do jogador com contorno (chamado por `rebakeDirect` em viz-setters). */
|
|
49
|
+
clearPlayerDirectCache(): void;
|
|
50
|
+
}
|
|
51
|
+
export declare function initViewports(ctx: ViewportsCtx): ViewportsApi;
|
|
52
|
+
export {};
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// render/viewports — a FÁBRICA de imagem dos modos de visão acessível: como um MODO vira PIXEL.
|
|
3
|
+
//
|
|
4
|
+
// Par de render/viz-setters, que levou a POLÍTICA ("qual modo vale onde": por jogador, global, overlays, painéis).
|
|
5
|
+
// Aqui mora a outra metade — a produção do pixel em si, que a política consome:
|
|
6
|
+
// · `pixiFilterFor` — modo → filtro GPU do viewport (daltonismo, cegueira, baixa visão)
|
|
7
|
+
// · `parallaxTexFor` — camada de fundo i, recolorida (ou não) para o modo
|
|
8
|
+
// · `treeTexFor` — decoração de fundo, idem
|
|
9
|
+
// · `playerVizTex` — quadro do jogador com contorno escuro no alto contraste
|
|
10
|
+
// · `lvOverlay*` / `renderVpOverlay` — a névoa/túnel/mancha de baixa visão DENTRO da render-texture do viewport
|
|
11
|
+
// Os quatro primeiros entravam em viz-setters por injeção (`parallaxTexFor`/`treeTexFor`/`playerVizTex`/
|
|
12
|
+
// `pixiFilterFor` no `VizSettersCtx`) enquanto ainda moravam no game.js; a partir daqui quem os fornece é este
|
|
13
|
+
// módulo. Ver docs/5-Refactoring/plano-modularizacao-mapa.md (B2).
|
|
14
|
+
//
|
|
15
|
+
// A DUPLICAÇÃO QUE ESTA EXTRAÇÃO CUROU: as seis matrizes de daltonismo estavam escritas duas vezes — como
|
|
16
|
+
// `<feColorMatrix>` no app/index.html (caminho de tela única) e como `PIXI.ColorMatrixFilter` dentro de
|
|
17
|
+
// `pixiFilterFor` (caminho multi-tela). Saíram para render/cvd-matrices (folha, zero deps); `pixiFilterFor` lê
|
|
18
|
+
// de lá e `initViewports` GERA os `<filter>` do documento de lá também. O sintoma da divergência era silencioso
|
|
19
|
+
// e de acessibilidade: a mesma pessoa daltônica veria cores diferentes em tela única e em multi-tela.
|
|
20
|
+
//
|
|
21
|
+
// Fronteiras que este módulo NÃO reabre:
|
|
22
|
+
// · `parallaxTexNormal` e `treeTexNormal` (as texturas CRUAS, fonte do recolor) continuam nascendo no game.js
|
|
23
|
+
// — a primeira é preenchida por `setCenario` (carrega PNG por tema), a segunda pelo desenho procedural da
|
|
24
|
+
// árvore. Entram injetadas. `parallaxTexNormal` é `const` cujos ELEMENTOS `setCenario` troca in place → entra
|
|
25
|
+
// por VALOR (o array é o mesmo objeto); `vpTex` é `let` que `configureRender` REATRIBUI a cada troca de nº de
|
|
26
|
+
// telas → entra por GETTER.
|
|
27
|
+
// · `lvOverlaySpr` e o renderer são objetos PIXI criados no game.js e entram por interface ESTRUTURAL, para o
|
|
28
|
+
// módulo rodar no project `node` sem importar PIXI. Mesmo precedente de render/scene-sky e game/traffic.
|
|
29
|
+
// · `_lastSharedViz` NÃO é daqui (é o registro do render estático, fica no game.js) — ver o topo de viz-setters.
|
|
30
|
+
//
|
|
31
|
+
// POR QUE `getTreeTexNormal`/`getLvOverlaySpr` são GETTERS e não valores, sendo os dois `const` no game.js:
|
|
32
|
+
// não é reatribuição, é ORDEM DE BOOT. `clearParallaxTexCache` precisa existir dentro de `setCenario`, que o
|
|
33
|
+
// game.js chama no topo do módulo (na restauração do cenário salvo) — bem ANTES de `treeTexNormal` e
|
|
34
|
+
// `lvOverlaySpr` serem declarados. Com getters, `initViewports` pode ser chamado cedo o bastante para aquela
|
|
35
|
+
// chamada não cair em TDZ; com valores, o `const` ainda não inicializado derrubaria a restauração do tema
|
|
36
|
+
// dentro de um `try/catch` que apenas cai para 'cidade' — o jogador perderia o cenário escolhido em silêncio.
|
|
37
|
+
//
|
|
38
|
+
// SEM I/O no import: nada de makeCanvas/tex no corpo do módulo (precedente de render/textures). O único efeito
|
|
39
|
+
// de `initViewports` é gerar os `<filter>` SVG, que é justamente o ponto da cura da duplicação.
|
|
40
|
+
import { LOGICAL_W, LOGICAL_H } from '../core/constants.js';
|
|
41
|
+
import { makeCanvas, tex } from './canvas.js';
|
|
42
|
+
import { DIRECT_CFG, directBgTexture, directSpriteTexture } from './high-contrast.js';
|
|
43
|
+
import { VIZ_BY_KEY } from './viz-modes.js';
|
|
44
|
+
import { CVD_MATRIX, installCvdFilters } from './cvd-matrices.js';
|
|
45
|
+
export function initViewports(ctx) {
|
|
46
|
+
// Gera os seis <filter> de daltonismo do documento a partir de render/cvd-matrices — a MESMA fonte que
|
|
47
|
+
// pixiFilterFor lê logo abaixo. É por isso que tela única e multi-tela não podem mais divergir.
|
|
48
|
+
installCvdFilters(ctx.cvdDefsHost);
|
|
49
|
+
/* ===================== fundo: parallax e decoração ===================== */
|
|
50
|
+
const _parallaxTexHC = {}; // {mode: [tex,tex,tex]}
|
|
51
|
+
function parallaxTexFor(i, mode) {
|
|
52
|
+
if (DIRECT_CFG[mode]) {
|
|
53
|
+
(_parallaxTexHC[mode] = _parallaxTexHC[mode] || []);
|
|
54
|
+
if (!_parallaxTexHC[mode][i])
|
|
55
|
+
_parallaxTexHC[mode][i] = directBgTexture(ctx.parallaxTexNormal[i], mode);
|
|
56
|
+
return _parallaxTexHC[mode][i]; // direto: fundo recua
|
|
57
|
+
}
|
|
58
|
+
return ctx.parallaxTexNormal[i];
|
|
59
|
+
}
|
|
60
|
+
function clearParallaxTexCache() { for (const k in _parallaxTexHC)
|
|
61
|
+
delete _parallaxTexHC[k]; }
|
|
62
|
+
const _treeTexHC = {};
|
|
63
|
+
function treeTexFor(mode) {
|
|
64
|
+
if (DIRECT_CFG[mode]) {
|
|
65
|
+
if (!_treeTexHC[mode])
|
|
66
|
+
_treeTexHC[mode] = directBgTexture(ctx.getTreeTexNormal(), mode);
|
|
67
|
+
return _treeTexHC[mode]; // direto: decoração recua
|
|
68
|
+
}
|
|
69
|
+
return ctx.getTreeTexNormal();
|
|
70
|
+
}
|
|
71
|
+
/* ===================== frente: o jogador ===================== */
|
|
72
|
+
// {mode: Map<texturaBase, texturaComContorno>} — chaveado pela textura de ORIGEM porque o jogador troca de
|
|
73
|
+
// quadro toda frame; um Map por modo evita recontornar o mesmo quadro a cada volta da animação.
|
|
74
|
+
let _playerDirect = {};
|
|
75
|
+
function playerVizTex(base, mode) {
|
|
76
|
+
if (!base)
|
|
77
|
+
return base;
|
|
78
|
+
if (DIRECT_CFG[mode]) {
|
|
79
|
+
const mm = (_playerDirect[mode] = _playerDirect[mode] || new Map());
|
|
80
|
+
if (!mm.has(base))
|
|
81
|
+
mm.set(base, directSpriteTexture(base, mode));
|
|
82
|
+
return mm.get(base); // direto: player com contorno escuro → salta
|
|
83
|
+
}
|
|
84
|
+
return base;
|
|
85
|
+
}
|
|
86
|
+
function clearPlayerDirectCache() { _playerDirect = {}; }
|
|
87
|
+
/* ===================== o filtro do viewport ===================== */
|
|
88
|
+
// Cacheado por MODO e por identidade: o mesmo array de filtros volta sempre, então trocar de viewport não
|
|
89
|
+
// reconstrói o filtro (nem invalida o shader do PIXI). Guarda `null` também — `mode in cache` e não
|
|
90
|
+
// `cache[mode]` — para que modo sem filtro (normal, hc-*) não seja reprocessado a cada frame.
|
|
91
|
+
const _vpFilterCache = {};
|
|
92
|
+
function pixiFilterFor(mode) {
|
|
93
|
+
if (mode in _vpFilterCache)
|
|
94
|
+
return _vpFilterCache[mode];
|
|
95
|
+
let f = null;
|
|
96
|
+
const CM = ctx.ColorMatrixFilter, BL = ctx.BlurFilter;
|
|
97
|
+
const cvd = CVD_MATRIX[mode]; // fonte única: os MESMOS números que geram o <feColorMatrix> do HTML
|
|
98
|
+
if (cvd && CM) {
|
|
99
|
+
const c = new CM();
|
|
100
|
+
c.matrix = cvd.slice();
|
|
101
|
+
f = [c];
|
|
102
|
+
} // slice: o filtro não fica com o array do módulo
|
|
103
|
+
else if (mode === 'blind' && CM) {
|
|
104
|
+
const c = new CM();
|
|
105
|
+
c.brightness(0, false);
|
|
106
|
+
f = [c];
|
|
107
|
+
}
|
|
108
|
+
else if (mode === 'lv-blur' && BL) {
|
|
109
|
+
f = [new BL(5)];
|
|
110
|
+
}
|
|
111
|
+
else if (mode === 'lv-haze' && CM) {
|
|
112
|
+
const c = new CM();
|
|
113
|
+
c.contrast(-0.45, false);
|
|
114
|
+
c.brightness(1.12, true);
|
|
115
|
+
f = [c];
|
|
116
|
+
}
|
|
117
|
+
else if ((mode === 'lv-tunnel' || mode === 'lv-diabetic' || mode === 'lv-macular') && BL) {
|
|
118
|
+
f = [new BL(mode === 'lv-tunnel' ? 1.5 : 2)];
|
|
119
|
+
}
|
|
120
|
+
return _vpFilterCache[mode] = f;
|
|
121
|
+
}
|
|
122
|
+
/* ===================== baixa visão: o overlay como textura ===================== */
|
|
123
|
+
// O que o filtro GPU não sabe fazer: névoa de catarata, o túnel do glaucoma, a mancha central da degeneração
|
|
124
|
+
// macular e as manchas espalhadas da retinopatia. São desenho, não transformação de cor — vêm como textura.
|
|
125
|
+
function lvOverlayCanvas(lv) {
|
|
126
|
+
const W = LOGICAL_W, H = LOGICAL_H, cv = makeCanvas(W, H), c = cv.getContext('2d'), cx = W / 2, cy = H / 2;
|
|
127
|
+
if (lv === 'haze') {
|
|
128
|
+
c.fillStyle = 'rgba(244,246,250,0.42)';
|
|
129
|
+
c.fillRect(0, 0, W, H);
|
|
130
|
+
}
|
|
131
|
+
else if (lv === 'tunnel') {
|
|
132
|
+
const g = c.createRadialGradient(cx, cy, H * 0.12, cx, cy, H * 0.6);
|
|
133
|
+
g.addColorStop(0, 'rgba(0,0,0,0)');
|
|
134
|
+
g.addColorStop(.5, 'rgba(0,0,0,.55)');
|
|
135
|
+
g.addColorStop(1, 'rgba(0,0,0,.99)');
|
|
136
|
+
c.fillStyle = g;
|
|
137
|
+
c.fillRect(0, 0, W, H);
|
|
138
|
+
}
|
|
139
|
+
else if (lv === 'macular') {
|
|
140
|
+
const g = c.createRadialGradient(cx, cy, 2, cx, cy, H * 0.34);
|
|
141
|
+
g.addColorStop(0, 'rgba(12,12,15,.95)');
|
|
142
|
+
g.addColorStop(.55, 'rgba(12,12,15,.5)');
|
|
143
|
+
g.addColorStop(1, 'rgba(12,12,15,0)');
|
|
144
|
+
c.fillStyle = g;
|
|
145
|
+
c.fillRect(0, 0, W, H);
|
|
146
|
+
}
|
|
147
|
+
else if (lv === 'diabetic') {
|
|
148
|
+
for (const [fx, fy, fr] of [[.22, .3, .1], [.64, .22, .075], [.8, .58, .11], [.4, .7, .085], [.16, .8, .07], [.54, .48, .06]]) {
|
|
149
|
+
const x = fx * W, y = fy * H, r = fr * W, g = c.createRadialGradient(x, y, 1, x, y, r);
|
|
150
|
+
g.addColorStop(0, 'rgba(10,10,14,.95)');
|
|
151
|
+
g.addColorStop(.5, 'rgba(10,10,14,.7)');
|
|
152
|
+
g.addColorStop(1, 'rgba(10,10,14,0)');
|
|
153
|
+
c.fillStyle = g;
|
|
154
|
+
c.fillRect(x - r, y - r, 2 * r, 2 * r);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return cv;
|
|
158
|
+
}
|
|
159
|
+
const _lvOverlayTex = {};
|
|
160
|
+
function lvOverlayTex(lv) {
|
|
161
|
+
if (lv === 'blur')
|
|
162
|
+
return null; // desfoque é filtro puro, não tem o que carimbar
|
|
163
|
+
if (!_lvOverlayTex[lv])
|
|
164
|
+
_lvOverlayTex[lv] = tex(lvOverlayCanvas(lv));
|
|
165
|
+
return _lvOverlayTex[lv];
|
|
166
|
+
}
|
|
167
|
+
// Overlay DENTRO da render-texture (a bolinha indicadora do viewport fica por cima, FORA do filtro — é por
|
|
168
|
+
// isso que ela continua visível no modo cegueira; ver updateVpDots em render/viz-setters).
|
|
169
|
+
function renderVpOverlay(i, mode) {
|
|
170
|
+
const m = VIZ_BY_KEY[mode];
|
|
171
|
+
if (!m || m.kind !== 'lowvision')
|
|
172
|
+
return;
|
|
173
|
+
const t = lvOverlayTex(m.lv);
|
|
174
|
+
if (t) {
|
|
175
|
+
const spr = ctx.getLvOverlaySpr();
|
|
176
|
+
spr.texture = t;
|
|
177
|
+
ctx.renderizarEm(spr, ctx.getVpTex()[i], false);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
parallaxTexFor, treeTexFor, playerVizTex, pixiFilterFor,
|
|
182
|
+
lvOverlayCanvas, lvOverlayTex, renderVpOverlay,
|
|
183
|
+
clearParallaxTexCache, clearPlayerDirectCache,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
/** O eixo do CONTRASTE. `padrao` não é ausência de tema: é o tema desenhado do jogo. */
|
|
3
|
+
export type Tema = 'padrao' | 'hc3' | 'hc45' | 'hc7';
|
|
4
|
+
/** O eixo da CORREÇÃO DE COR. `tricro` = visão tricromática, e é um nome, não uma ausência. */
|
|
5
|
+
export type Correcao = 'tricro' | 'protan' | 'deuter' | 'tritan';
|
|
6
|
+
/** A simulação, que NÃO é eixo. `null` = nenhuma a correr. */
|
|
7
|
+
export type Simulacao = null | 'sim-protan' | 'sim-deuter' | 'sim-tritan' | 'lv-blur' | 'lv-haze' | 'lv-tunnel' | 'lv-macular' | 'lv-diabetic' | 'blind';
|
|
8
|
+
/** O estado visual de UM jogador. Substitui a string única de `p.viz`. */
|
|
9
|
+
export interface VisualState {
|
|
10
|
+
readonly tema: Tema;
|
|
11
|
+
readonly correcao: Correcao;
|
|
12
|
+
readonly simulacao: Simulacao;
|
|
13
|
+
}
|
|
14
|
+
export declare const PADRAO: VisualState;
|
|
15
|
+
/**
|
|
16
|
+
* Os dois eixos estão no padrão?
|
|
17
|
+
*
|
|
18
|
+
* ⚠️ É A PERGUNTA QUE LIBERA A SIMULAÇÃO, e é por isso que ela é uma função e não um booleano guardado:
|
|
19
|
+
* guardar o resultado deixaria as duas coisas divergirem, e a divergência aqui significa uma demonstração a
|
|
20
|
+
* correr por cima de uma adaptação — que ensina uma coisa falsa.
|
|
21
|
+
*/
|
|
22
|
+
export declare function nosPadroes(v: VisualState): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Esta simulação pode correr AGORA? E se não, por quê?
|
|
25
|
+
*
|
|
26
|
+
* ⚠️ DEVOLVE O MOTIVO E NÃO SÓ `false`. O ADR-0076 exige que a recusa seja VISÍVEL e explicada — nunca
|
|
27
|
+
* silenciosamente removida, nunca aceita e depois ignorada. E o motivo é um FACTO SOBRE A DEMONSTRAÇÃO, não
|
|
28
|
+
* uma repreensão a quem escolheu: quem ligou o alto contraste ligou-o porque precisa.
|
|
29
|
+
*/
|
|
30
|
+
export type MotivoIndisponivel = 'tema' | 'correcao' | 'ambos';
|
|
31
|
+
export declare function simulacaoIndisponivel(v: VisualState): MotivoIndisponivel | null;
|
|
32
|
+
/** A chave de modo DIRETO que este tema usa, ou `null` para o tema padrão. */
|
|
33
|
+
export declare function temaDireto(v: VisualState): string | null;
|
|
34
|
+
/**
|
|
35
|
+
* A chave de FILTRO CSS que este estado usa, ou `null`.
|
|
36
|
+
*
|
|
37
|
+
* ⚠️ SIMULAÇÃO VENCE CORREÇÃO AQUI, e não é uma regra de precedência escondida: as duas não podem coexistir
|
|
38
|
+
* porque `simulacaoIndisponivel` já as separa — uma simulação só corre com a correção no padrão. Este `??`
|
|
39
|
+
* é o que acontece quando alguém constrói um estado à mão que a interface não deixaria montar, e escolher a
|
|
40
|
+
* simulação é o menos errado dos dois: ela é a intenção mais recente e mais visível.
|
|
41
|
+
*/
|
|
42
|
+
export declare function filtroChave(v: VisualState): string | null;
|
|
43
|
+
/**
|
|
44
|
+
* As DUAS coisas que a raiz precisa aplicar, num objeto só.
|
|
45
|
+
*
|
|
46
|
+
* ⚠️ Devolver os dois JUNTOS é o ponto da issue #104: enquanto eram um campo, aplicar um apagava o outro.
|
|
47
|
+
* Aqui um estado com tema `hc7` e correção `deuter` devolve os dois preenchidos, e é o que o gate afirma.
|
|
48
|
+
*/
|
|
49
|
+
export interface Aplicacao {
|
|
50
|
+
/** A chave do modo direto (alto contraste), ou `null`. */
|
|
51
|
+
readonly direto: string | null;
|
|
52
|
+
/** A chave do filtro CSS (correção ou simulação), ou `null`. */
|
|
53
|
+
readonly filtro: string | null;
|
|
54
|
+
}
|
|
55
|
+
export declare function aplicacao(v: VisualState): Aplicacao;
|
|
56
|
+
/** Há uma simulação a correr? É a pergunta que `simulatesDisability` fazia à string. */
|
|
57
|
+
export declare function ehSimulacao(v: VisualState): boolean;
|
|
58
|
+
/** É a simulação de CEGUEIRA? O quiz e o sonar perguntam isto para se comportarem sem tela. */
|
|
59
|
+
export declare function ehCego(v: VisualState): boolean;
|
|
60
|
+
/** É uma das cinco simulações de BAIXA VISÃO? Elas pedem o overlay como textura, e não só um filtro. */
|
|
61
|
+
export declare function ehBaixaVisao(v: VisualState): boolean;
|
|
62
|
+
/** O tema está fora do padrão? Era o `/^hc-direto/.test(s.viz)` espalhado pela interface. */
|
|
63
|
+
export declare function temAltoContraste(v: VisualState): boolean;
|
|
64
|
+
/** Próximo TEMA no ciclo do ícone da barra rápida. Anda só no seu eixo, e não toca na correção. */
|
|
65
|
+
export declare function proximoTema(v: VisualState): VisualState;
|
|
66
|
+
/**
|
|
67
|
+
* Próxima CORREÇÃO no ciclo do ícone. Anda só no seu eixo, e não toca no tema.
|
|
68
|
+
*
|
|
69
|
+
* ⚠️ A ASSIMETRIA DO ORIGINAL FICA REGISTRADA E NÃO É COPIADA: `nextCvd` mapeava um valor desconhecido para
|
|
70
|
+
* o ÍNDICE 1 (`fix-protan`) enquanto `nextContrast` mapeava para 0. Era um comentário no ficheiro a explicar
|
|
71
|
+
* uma diferença que ninguém tinha decidido. Aqui as duas começam no padrão, porque um valor desconhecido é
|
|
72
|
+
* exatamente o caso em que não se sabe o que a criança queria — e o padrão é a única resposta que não
|
|
73
|
+
* escolhe por ela.
|
|
74
|
+
*/
|
|
75
|
+
export declare function proximaCorrecao(v: VisualState): VisualState;
|
|
76
|
+
/**
|
|
77
|
+
* A chave que o sprite do jogador usa para escolher textura.
|
|
78
|
+
*
|
|
79
|
+
* ⚠️ É a SIMULAÇÃO quando há uma, e o TEMA quando não há — nesta ordem porque é a ordem do que a criança vê:
|
|
80
|
+
* uma cegueira simulada apaga a tela inteira, e nesse instante o tema não muda nada do que ela percebe.
|
|
81
|
+
*/
|
|
82
|
+
export declare function chaveDeTextura(v: VisualState): string;
|
|
83
|
+
/**
|
|
84
|
+
* Traduz o valor salvo. Aceita o antigo (string) e o novo (objeto), e devolve sempre um estado válido.
|
|
85
|
+
*
|
|
86
|
+
* ⚠️ IDEMPOTENTE POR CONSTRUÇÃO: um objeto já migrado atravessa com os campos conferidos. Importa porque a
|
|
87
|
+
* leitura acontece por jogador e mais de uma vez por sessão.
|
|
88
|
+
*
|
|
89
|
+
* ⚠️ E DESCONHECIDO CAI NO PADRÃO EM VEZ DE ESTOURAR. O dado vem do navegador de uma criança e pode ser de
|
|
90
|
+
* uma versão futura, de outra máquina, ou lixo. Um `throw` aqui tiraria o jogo do ar por causa de uma
|
|
91
|
+
* preferência; o padrão apenas devolve o jogo como ele nasce.
|
|
92
|
+
*/
|
|
93
|
+
export declare function migrarVisual(salvo: unknown): VisualState;
|
|
94
|
+
export declare const TEMAS: readonly Tema[];
|
|
95
|
+
export declare const CORRECOES: readonly Correcao[];
|
|
96
|
+
export declare const SIMULACOES: readonly Simulacao[];
|
|
97
|
+
/** As chaves antigas que a migração conhece — exportada para o gate poder exigir que TODAS estejam cobertas. */
|
|
98
|
+
export declare const CHAVES_ANTIGAS: readonly string[];
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// render/viz-axes — OS DOIS EIXOS, e a simulação que NÃO é um deles (ADR-0076). Módulo-folha, zero deps.
|
|
3
|
+
//
|
|
4
|
+
// ========================= O DEFEITO QUE ISTO CONSERTA =========================
|
|
5
|
+
// Hoje o menu visual é UM rádio e `p.viz` guarda UMA string. Escolher `fix-deuter` desliga o contraste 7:1;
|
|
6
|
+
// escolher um nível de contraste desliga a correção. ⚠️ UMA CRIANÇA COM DALTONISMO QUE TAMBÉM PRECISE DE
|
|
7
|
+
// ALTO CONTRASTE NÃO PODE TER OS DOIS — e as duas necessidades coexistem numa mesma pessoa com frequência.
|
|
8
|
+
//
|
|
9
|
+
// ========================= POR QUE A SIMULAÇÃO NÃO É UM EIXO =========================
|
|
10
|
+
// Correção e simulação são ambas implementadas como filtro e servem a PROPÓSITOS OPOSTOS: uma deixa uma
|
|
11
|
+
// criança JOGAR, a outra deixa alguém SENTIR como é não conseguir. Agrupá-las pelo mecanismo já as fundiu
|
|
12
|
+
// duas vezes — no ADR-0011 (as correções listadas no painel de Empatia, de modo que *«a criança que
|
|
13
|
+
// PRECISAVA da correção tinha de a procurar no menu sobre fingir»*) e no ADR-0075.
|
|
14
|
+
//
|
|
15
|
+
// ⚠️ UM GRUPO DE AJUSTES CHAMA-SE PELO QUE ELE SERVE, NUNCA PELO COMO É IMPLEMENTADO.
|
|
16
|
+
//
|
|
17
|
+
// ========================= E POR QUE A SIMULAÇÃO É TRAVADA NOS DOIS PADRÕES =========================
|
|
18
|
+
// De uma tela já corrigida, uma simulação não mostra nem a deficiência nem a correção; por cima de um tema
|
|
19
|
+
// de alto contraste, mostra o que o tema faz e não o que a deuteranopia faz. Uma demonstração a correr em
|
|
20
|
+
// cima de uma adaptação não é uma demonstração mais fraca — **ela ensina uma coisa falsa**.
|
|
21
|
+
export const PADRAO = Object.freeze({ tema: 'padrao', correcao: 'tricro', simulacao: null });
|
|
22
|
+
/**
|
|
23
|
+
* Os dois eixos estão no padrão?
|
|
24
|
+
*
|
|
25
|
+
* ⚠️ É A PERGUNTA QUE LIBERA A SIMULAÇÃO, e é por isso que ela é uma função e não um booleano guardado:
|
|
26
|
+
* guardar o resultado deixaria as duas coisas divergirem, e a divergência aqui significa uma demonstração a
|
|
27
|
+
* correr por cima de uma adaptação — que ensina uma coisa falsa.
|
|
28
|
+
*/
|
|
29
|
+
export function nosPadroes(v) {
|
|
30
|
+
return v.tema === 'padrao' && v.correcao === 'tricro';
|
|
31
|
+
}
|
|
32
|
+
export function simulacaoIndisponivel(v) {
|
|
33
|
+
const t = v.tema !== 'padrao';
|
|
34
|
+
const c = v.correcao !== 'tricro';
|
|
35
|
+
if (t && c)
|
|
36
|
+
return 'ambos';
|
|
37
|
+
if (t)
|
|
38
|
+
return 'tema';
|
|
39
|
+
if (c)
|
|
40
|
+
return 'correcao';
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
/* ===================== A COMPOSIÇÃO ===================== */
|
|
44
|
+
//
|
|
45
|
+
// ⚠️ ELA JÁ ERA MECANICAMENTE POSSÍVEL, e é isso que torna o defeito mais caro do que parecia: o TEMA de
|
|
46
|
+
// alto contraste vai pela RENDERIZAÇÃO DIRETA (`DIRECT_CFG`/PIXI) e a CORREÇÃO vai por FILTRO CSS
|
|
47
|
+
// (`url(#cvd-fix-*)`). São dois mecanismos que não colidem. O que impedia os dois de coexistir não era a
|
|
48
|
+
// máquina — era o campo único que só cabia um valor.
|
|
49
|
+
/** A chave de modo DIRETO que este tema usa, ou `null` para o tema padrão. */
|
|
50
|
+
export function temaDireto(v) {
|
|
51
|
+
return v.tema === 'hc3' ? 'hc-direto'
|
|
52
|
+
: v.tema === 'hc45' ? 'hc-direto-45'
|
|
53
|
+
: v.tema === 'hc7' ? 'hc-direto-7'
|
|
54
|
+
: null;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* A chave de FILTRO CSS que este estado usa, ou `null`.
|
|
58
|
+
*
|
|
59
|
+
* ⚠️ SIMULAÇÃO VENCE CORREÇÃO AQUI, e não é uma regra de precedência escondida: as duas não podem coexistir
|
|
60
|
+
* porque `simulacaoIndisponivel` já as separa — uma simulação só corre com a correção no padrão. Este `??`
|
|
61
|
+
* é o que acontece quando alguém constrói um estado à mão que a interface não deixaria montar, e escolher a
|
|
62
|
+
* simulação é o menos errado dos dois: ela é a intenção mais recente e mais visível.
|
|
63
|
+
*/
|
|
64
|
+
export function filtroChave(v) {
|
|
65
|
+
if (v.simulacao)
|
|
66
|
+
return v.simulacao;
|
|
67
|
+
return v.correcao === 'tricro' ? null : 'fix-' + (v.correcao === 'deuter' ? 'deuter' : v.correcao);
|
|
68
|
+
}
|
|
69
|
+
export function aplicacao(v) {
|
|
70
|
+
return { direto: temaDireto(v), filtro: filtroChave(v) };
|
|
71
|
+
}
|
|
72
|
+
/* ===================== O QUE OS LEITORES DE FACTO PERGUNTAM ===================== */
|
|
73
|
+
//
|
|
74
|
+
// ⚠️ ESTAS FUNÇÕES SAÍRAM DE UMA MEDIDA, e não de um desenho a priori. Os 31 leitores de `p.viz` foram
|
|
75
|
+
// classificados pelo que PERGUNTAM, e a lista curta abaixo é o resultado — quatro perguntam o «kind», duas
|
|
76
|
+
// se é simulação, uma a textura, e o resto é escrita. Um leitor que precise de algo fora daqui é sinal de
|
|
77
|
+
// que a pergunta dele merecia um nome.
|
|
78
|
+
//
|
|
79
|
+
// ⚠️ E A MEDIDA TROUXE UM ACHADO: `ui/pause-icons` já tem `nextContrast` e `nextCvd`, cada uma a ciclar
|
|
80
|
+
// DENTRO do seu eixo. A interface já pensava em dois eixos há muito tempo; era o ARMAZENAMENTO que os
|
|
81
|
+
// colapsava num campo. As funções de ciclo abaixo são as mesmas duas, agora com onde guardar o resultado.
|
|
82
|
+
/** Há uma simulação a correr? É a pergunta que `simulatesDisability` fazia à string. */
|
|
83
|
+
export function ehSimulacao(v) {
|
|
84
|
+
return v.simulacao !== null;
|
|
85
|
+
}
|
|
86
|
+
/** É a simulação de CEGUEIRA? O quiz e o sonar perguntam isto para se comportarem sem tela. */
|
|
87
|
+
export function ehCego(v) {
|
|
88
|
+
return v.simulacao === 'blind';
|
|
89
|
+
}
|
|
90
|
+
/** É uma das cinco simulações de BAIXA VISÃO? Elas pedem o overlay como textura, e não só um filtro. */
|
|
91
|
+
export function ehBaixaVisao(v) {
|
|
92
|
+
return v.simulacao !== null && v.simulacao.startsWith('lv-');
|
|
93
|
+
}
|
|
94
|
+
/** O tema está fora do padrão? Era o `/^hc-direto/.test(s.viz)` espalhado pela interface. */
|
|
95
|
+
export function temAltoContraste(v) {
|
|
96
|
+
return v.tema !== 'padrao';
|
|
97
|
+
}
|
|
98
|
+
/** Próximo TEMA no ciclo do ícone da barra rápida. Anda só no seu eixo, e não toca na correção. */
|
|
99
|
+
export function proximoTema(v) {
|
|
100
|
+
const i = TEMAS.indexOf(v.tema);
|
|
101
|
+
return { ...v, tema: TEMAS[(i < 0 ? 0 : i + 1) % TEMAS.length] };
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Próxima CORREÇÃO no ciclo do ícone. Anda só no seu eixo, e não toca no tema.
|
|
105
|
+
*
|
|
106
|
+
* ⚠️ A ASSIMETRIA DO ORIGINAL FICA REGISTRADA E NÃO É COPIADA: `nextCvd` mapeava um valor desconhecido para
|
|
107
|
+
* o ÍNDICE 1 (`fix-protan`) enquanto `nextContrast` mapeava para 0. Era um comentário no ficheiro a explicar
|
|
108
|
+
* uma diferença que ninguém tinha decidido. Aqui as duas começam no padrão, porque um valor desconhecido é
|
|
109
|
+
* exatamente o caso em que não se sabe o que a criança queria — e o padrão é a única resposta que não
|
|
110
|
+
* escolhe por ela.
|
|
111
|
+
*/
|
|
112
|
+
export function proximaCorrecao(v) {
|
|
113
|
+
const i = CORRECOES.indexOf(v.correcao);
|
|
114
|
+
return { ...v, correcao: CORRECOES[(i < 0 ? 0 : i + 1) % CORRECOES.length] };
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* A chave que o sprite do jogador usa para escolher textura.
|
|
118
|
+
*
|
|
119
|
+
* ⚠️ É a SIMULAÇÃO quando há uma, e o TEMA quando não há — nesta ordem porque é a ordem do que a criança vê:
|
|
120
|
+
* uma cegueira simulada apaga a tela inteira, e nesse instante o tema não muda nada do que ela percebe.
|
|
121
|
+
*/
|
|
122
|
+
export function chaveDeTextura(v) {
|
|
123
|
+
return v.simulacao ?? temaDireto(v) ?? 'normal';
|
|
124
|
+
}
|
|
125
|
+
/* ===================== A MIGRAÇÃO ===================== */
|
|
126
|
+
//
|
|
127
|
+
// ⚠️ ELA NÃO É OPCIONAL E VEM ANTES DA PRIMEIRA LEITURA DA FORMA NOVA. O ajuste salvo guarda o valor único
|
|
128
|
+
// antigo; sem a tradução, o modo visual que cada criança já escolheu é DESCARTADO — e quem escolheu um
|
|
129
|
+
// desses valores escolheu-o porque enxerga assim.
|
|
130
|
+
/** O valor único antigo → o estado de dois eixos. Chave desconhecida cai no padrão, e nunca estoura. */
|
|
131
|
+
const DE_CHAVE_UNICA = Object.freeze({
|
|
132
|
+
normal: PADRAO,
|
|
133
|
+
// Os três níveis de contraste viram TEMA, e a correção fica no padrão.
|
|
134
|
+
'hc-direto': { tema: 'hc3', correcao: 'tricro', simulacao: null },
|
|
135
|
+
'hc-direto-45': { tema: 'hc45', correcao: 'tricro', simulacao: null },
|
|
136
|
+
'hc-direto-7': { tema: 'hc7', correcao: 'tricro', simulacao: null },
|
|
137
|
+
// As três correções viram CORREÇÃO, e o tema fica no padrão.
|
|
138
|
+
'fix-protan': { tema: 'padrao', correcao: 'protan', simulacao: null },
|
|
139
|
+
'fix-deuter': { tema: 'padrao', correcao: 'deuter', simulacao: null },
|
|
140
|
+
'fix-tritan': { tema: 'padrao', correcao: 'tritan', simulacao: null },
|
|
141
|
+
// ⚠️ AS NOVE SIMULAÇÕES VOLTAM COM OS DOIS EIXOS NO PADRÃO, e não é perda de informação: uma simulação
|
|
142
|
+
// só era possível a partir do padrão de qualquer maneira, porque ela SUBSTITUÍA tudo o resto. O que a
|
|
143
|
+
// forma nova acrescenta é dizer isso em vez de o deixar implícito.
|
|
144
|
+
'sim-protan': { tema: 'padrao', correcao: 'tricro', simulacao: 'sim-protan' },
|
|
145
|
+
'sim-deuter': { tema: 'padrao', correcao: 'tricro', simulacao: 'sim-deuter' },
|
|
146
|
+
'sim-tritan': { tema: 'padrao', correcao: 'tricro', simulacao: 'sim-tritan' },
|
|
147
|
+
'lv-blur': { tema: 'padrao', correcao: 'tricro', simulacao: 'lv-blur' },
|
|
148
|
+
'lv-haze': { tema: 'padrao', correcao: 'tricro', simulacao: 'lv-haze' },
|
|
149
|
+
'lv-tunnel': { tema: 'padrao', correcao: 'tricro', simulacao: 'lv-tunnel' },
|
|
150
|
+
'lv-macular': { tema: 'padrao', correcao: 'tricro', simulacao: 'lv-macular' },
|
|
151
|
+
'lv-diabetic': { tema: 'padrao', correcao: 'tricro', simulacao: 'lv-diabetic' },
|
|
152
|
+
blind: { tema: 'padrao', correcao: 'tricro', simulacao: 'blind' },
|
|
153
|
+
});
|
|
154
|
+
/**
|
|
155
|
+
* Traduz o valor salvo. Aceita o antigo (string) e o novo (objeto), e devolve sempre um estado válido.
|
|
156
|
+
*
|
|
157
|
+
* ⚠️ IDEMPOTENTE POR CONSTRUÇÃO: um objeto já migrado atravessa com os campos conferidos. Importa porque a
|
|
158
|
+
* leitura acontece por jogador e mais de uma vez por sessão.
|
|
159
|
+
*
|
|
160
|
+
* ⚠️ E DESCONHECIDO CAI NO PADRÃO EM VEZ DE ESTOURAR. O dado vem do navegador de uma criança e pode ser de
|
|
161
|
+
* uma versão futura, de outra máquina, ou lixo. Um `throw` aqui tiraria o jogo do ar por causa de uma
|
|
162
|
+
* preferência; o padrão apenas devolve o jogo como ele nasce.
|
|
163
|
+
*/
|
|
164
|
+
export function migrarVisual(salvo) {
|
|
165
|
+
if (typeof salvo === 'string')
|
|
166
|
+
return DE_CHAVE_UNICA[salvo] ?? PADRAO;
|
|
167
|
+
if (salvo && typeof salvo === 'object') {
|
|
168
|
+
const o = salvo;
|
|
169
|
+
return {
|
|
170
|
+
tema: TEMAS.includes(o.tema) ? o.tema : PADRAO.tema,
|
|
171
|
+
correcao: CORRECOES.includes(o.correcao) ? o.correcao : PADRAO.correcao,
|
|
172
|
+
simulacao: SIMULACOES.includes(o.simulacao) ? o.simulacao : null,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
return PADRAO;
|
|
176
|
+
}
|
|
177
|
+
export const TEMAS = ['padrao', 'hc3', 'hc45', 'hc7'];
|
|
178
|
+
export const CORRECOES = ['tricro', 'protan', 'deuter', 'tritan'];
|
|
179
|
+
export const SIMULACOES = [
|
|
180
|
+
null, 'sim-protan', 'sim-deuter', 'sim-tritan',
|
|
181
|
+
'lv-blur', 'lv-haze', 'lv-tunnel', 'lv-macular', 'lv-diabetic', 'blind',
|
|
182
|
+
];
|
|
183
|
+
/** As chaves antigas que a migração conhece — exportada para o gate poder exigir que TODAS estejam cobertas. */
|
|
184
|
+
export const CHAVES_ANTIGAS = Object.keys(DE_CHAVE_UNICA);
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
/**
|
|
3
|
+
* `sim` — este modo SIMULA uma deficiência em quem não a tem, em vez de CORRIGIR a tela para quem a tem.
|
|
4
|
+
*
|
|
5
|
+
* O `kind` não responde a isso e nunca respondeu: `sim-deuter` (simular daltonismo) e `fix-deuter` (corrigir
|
|
6
|
+
* daltonismo) compartilham `kind:'filter'`, ainda que sirvam a duas pessoas opostas. Enquanto ninguém
|
|
7
|
+
* precisava distinguir, a lacuna era inofensiva. Passou a não ser quando o menu de empatia ganhou um
|
|
8
|
+
* "restaurar padrões" (ADR-0028): desligar as simulações é o objetivo do botão, e desligar as correções junto
|
|
9
|
+
* seria tirar de uma criança daltônica a única correção que ela tem — a partir do menu que existe para quem
|
|
10
|
+
* NÃO tem a deficiência. Um reset que faz isso é pior que a armadilha que ele deveria desfazer.
|
|
11
|
+
*
|
|
12
|
+
* Só os 9 modos marcados simulam. Os 3 `fix-*` corrigem, os 3 `hc-direto*` corrigem, e `normal` não faz nada.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Um modo de visão. `nome` e `desc` guardam CHAVE i18n, não texto — mesma decisão de `CenarioTema.nome` e
|
|
16
|
+
* `RM_LABEL`, e pelo mesmo motivo: uma tabela de `const` com texto resolve UMA vez, no import, e fica
|
|
17
|
+
* congelada no idioma do boot. Este menu é o que uma criança de baixa visão ou daltônica lê para configurar o
|
|
18
|
+
* PRÓPRIO jogo; deixá-lo em português numa build em inglês tira dela a única página que ela precisava ler.
|
|
19
|
+
*
|
|
20
|
+
* Quem EXIBE resolve (`render/viz-setters`, `consumer-quiz`), e por isso este módulo continua FOLHA: dado
|
|
21
|
+
* puro, sem dependência nenhuma, importável dos dois lados da fronteira.
|
|
22
|
+
*/
|
|
23
|
+
export type VizMode = {
|
|
24
|
+
key: string;
|
|
25
|
+
kind: string;
|
|
26
|
+
nome: string;
|
|
27
|
+
desc: string;
|
|
28
|
+
lv?: string;
|
|
29
|
+
sim?: true;
|
|
30
|
+
};
|
|
31
|
+
export declare const VIZ_MODES: VizMode[];
|
|
32
|
+
export declare const VIZ_BY_KEY: Record<string, VizMode>;
|
|
33
|
+
export declare const VIZ_FILTER: Record<string, string>;
|
|
34
|
+
export declare const VIZ_CYCLE: string[];
|
|
35
|
+
/**
|
|
36
|
+
* Este modo simula uma deficiência? Chave desconhecida (ou vazia) → false, porque a pergunta que o chamador
|
|
37
|
+
* está fazendo é "posso desligar isto sem tirar nada de ninguém?", e a resposta honesta diante do
|
|
38
|
+
* desconhecido é não.
|
|
39
|
+
*/
|
|
40
|
+
export declare function simulatesDisability(key: string): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* As CORREÇÕES de daltonismo: daltonizam a tela para quem TEM a condição. Derivadas do catálogo, nunca
|
|
43
|
+
* listadas à mão — são exatamente os filtros que não simulam.
|
|
44
|
+
*
|
|
45
|
+
* Existem como lista própria porque moram no menu de ACESSIBILIDADE VISUAL, e não no de empatia (decisão do
|
|
46
|
+
* Dev, issue #60). Ficaram anos no menu errado por um motivo que este arquivo agora conserta: `kind` não
|
|
47
|
+
* distinguia simular de corrigir, então o painel de empatia, que se recorta por `kind:'filter'`, arrastava as
|
|
48
|
+
* três junto. Dois públicos opostos na mesma lista — quem quer sentir como é ser daltônico e quem é.
|
|
49
|
+
*/
|
|
50
|
+
export declare const VIZ_CORRECTIONS: readonly VizMode[];
|
|
51
|
+
/** Este modo precisa de um CANVAS de mundo para existir? Só os `hcnew` precisam: eles repintam texturas. */
|
|
52
|
+
export declare function needsCanvas(key: string): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Os modos que funcionam em QUALQUER jogo — os que se aplicam como filtro de CSS sobre um elemento.
|
|
55
|
+
*
|
|
56
|
+
* Derivada, nunca listada à mão: um modo novo entra na pilha certa por causa do `kind` que ele declara, e não
|
|
57
|
+
* porque alguém lembrou de acrescentá-lo aqui. É a mesma regra de `VIZ_CORRECTIONS`.
|
|
58
|
+
*/
|
|
59
|
+
export declare const VIZ_DOM_ONLY: readonly VizMode[];
|
|
60
|
+
/** Os modos que EXIGEM mundo. Complemento exato de `VIZ_DOM_ONLY` — juntos, os 16, sem sobra nem repetição. */
|
|
61
|
+
export declare const VIZ_CANVAS_ONLY: readonly VizMode[];
|