@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,109 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
import { type HcRoleKey, type PaintableRole } from './hc-role-data.js';
|
|
3
|
+
export type { PaintableRole, HcRoleKey } from './hc-role-data.js';
|
|
4
|
+
export { HC_ROLE_KEYS, HC_ROLE_DEF } from './hc-role-data.js';
|
|
5
|
+
export declare const HC_ROLE: Record<HcRoleKey, [number, number, number]>;
|
|
6
|
+
/** Persiste HC_ROLE (chamado por setRoleColor/resetRoleColors no game.js). Migrado de localStorage direto → store. */
|
|
7
|
+
export declare function saveHcRole(): void;
|
|
8
|
+
export interface DirectCfg {
|
|
9
|
+
off: number;
|
|
10
|
+
mul: number;
|
|
11
|
+
bgMul: number;
|
|
12
|
+
}
|
|
13
|
+
export declare const DIRECT_CFG: Record<string, DirectCfg>;
|
|
14
|
+
export declare function dcfg(mode: string): DirectCfg;
|
|
15
|
+
export declare function dimDesat(c: CanvasRenderingContext2D, w: number, h: number, mul: number, blue: number, off?: number): void;
|
|
16
|
+
export interface HighContrastCtx {
|
|
17
|
+
W: number;
|
|
18
|
+
H: number;
|
|
19
|
+
outlineFg: () => number;
|
|
20
|
+
outlineBg: () => number;
|
|
21
|
+
getWorldCanvasNormal: () => HTMLCanvasElement;
|
|
22
|
+
getWorldTexNormal: () => unknown;
|
|
23
|
+
/**
|
|
24
|
+
* OS SPRITES QUE O JOGO QUER RECOLORIDOS, POR ID. A engine cacheia por `(id, modo)` e nunca sabe o que o id
|
|
25
|
+
* significa — pode ser uma moeda, uma sílaba, uma peça de tabuleiro.
|
|
26
|
+
*
|
|
27
|
+
* Era um par cravado, `coinCanvasNormal` + `coinTexNormal`, e o nome era a dívida: a API do cache de alto
|
|
28
|
+
* contraste tinha a forma de UM sprite de UM jogo. Um segundo jogo que quisesse recolorir a peça dele não
|
|
29
|
+
* tinha por onde — teria de chamar a peça de "moeda", ou de reimplementar o cache.
|
|
30
|
+
*
|
|
31
|
+
* Função e não valor porque o canvas de um sprite pode ser reescrito no boot (o mesmo motivo do
|
|
32
|
+
* `getWorldCanvasNormal`), e um valor lido uma vez congelaria o do primeiro instante.
|
|
33
|
+
*/
|
|
34
|
+
sprites: () => Record<string, {
|
|
35
|
+
canvas: HTMLCanvasElement | null;
|
|
36
|
+
tex: unknown;
|
|
37
|
+
}>;
|
|
38
|
+
/**
|
|
39
|
+
* TILE → PAPEL SEMÂNTICO, e é o consumidor quem sabe. `null` = estrutura (sem repintura).
|
|
40
|
+
*
|
|
41
|
+
* Era uma tabela fixa AQUI DENTRO — `9` perigo, `4|5|10` escalável, `3` água — e o ADR-0027 a nomeia como o
|
|
42
|
+
* acoplamento nº 1 da base: quatro linhas das quais TODO o alto contraste dependia. Elas diziam, pela
|
|
43
|
+
* estrutura, que "perigo é o tile 9" era verdade da engine. É verdade DESTE MAPA. Um segundo jogo com outra
|
|
44
|
+
* numeração pintava o chão de laranja e a lava de cinza — sem erro, sem teste vermelho, para quem menos
|
|
45
|
+
* pode conferir isso olhando.
|
|
46
|
+
*
|
|
47
|
+
* A tabela do jogo de plataforma mora em `game/tile-roles`. Trocar de tabela é trocar de jogo, e é só isso.
|
|
48
|
+
*/
|
|
49
|
+
roleOf: (t: number) => PaintableRole | null;
|
|
50
|
+
}
|
|
51
|
+
export declare function initHighContrast(c: HighContrastCtx): void;
|
|
52
|
+
/** Mundo em alto contraste: base dessaturada/clareada + repintura por papel (com escada como caso especial:
|
|
53
|
+
* trilhos/degraus, não faixa sólida) + contorno de 2º plano no perímetro externo (navegável × não-navegável). */
|
|
54
|
+
export declare function worldToTextureDirect(srcCanvas: HTMLCanvasElement, mode: string): unknown;
|
|
55
|
+
/** Superfície mínima de PIXI.Texture que directBgTexture/directSpriteTexture tocam (resource.source pode ser
|
|
56
|
+
* canvas OU imagem — parallax de tema "cidade" carrega PNG via PIXI.Texture.from(img)). Estrutural (como o
|
|
57
|
+
* FxGraphics de render/fx.ts) para não arrastar os tipos de união de Resource do PIXI aqui dentro. */
|
|
58
|
+
interface DirectTexSource {
|
|
59
|
+
orig: {
|
|
60
|
+
width: number;
|
|
61
|
+
height: number;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* O RECORTE do quadro dentro da base. Passou a existir aqui com o atlas (item 22): antes toda textura de
|
|
65
|
+
* personagem era uma tela própria e base e recorte coincidiam. Opcional porque `directBgTexture` recebe
|
|
66
|
+
* texturas que de fato ocupam a base inteira (parallax, árvore) e não precisa dele.
|
|
67
|
+
*/
|
|
68
|
+
frame?: {
|
|
69
|
+
x: number;
|
|
70
|
+
y: number;
|
|
71
|
+
width: number;
|
|
72
|
+
height: number;
|
|
73
|
+
};
|
|
74
|
+
baseTexture: {
|
|
75
|
+
valid: boolean;
|
|
76
|
+
resource?: {
|
|
77
|
+
source?: (HTMLCanvasElement | HTMLImageElement) | null;
|
|
78
|
+
} | null;
|
|
79
|
+
once(event: 'loaded', cb: () => void): void;
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/** Fundo/decoração em alto contraste: só dessaturação/escurecimento (recua) — sem repintura por papel nem
|
|
83
|
+
* contorno. Usada por game.js para árvores (treeTexFor) e parallax (parallaxTexFor). */
|
|
84
|
+
export declare function directBgTexture(srcTex: DirectTexSource, mode: string): unknown;
|
|
85
|
+
/** Sprite de 1º plano (player/moeda/power-up) em alto contraste: mantém a cor da arte + contorno ESCURO
|
|
86
|
+
* (WCAG 2.4.7 — "salta" do fundo recuado). `mode` não é usado (mantido só p/ paridade de assinatura com as
|
|
87
|
+
* outras 3 funções "direct*" — o dispatch por DIRECT_CFG[mode] já aconteceu no chamador). fg=0 → sem contorno. */
|
|
88
|
+
export declare function directSpriteCanvas(srcCanvas: HTMLCanvasElement, mode: string): HTMLCanvasElement;
|
|
89
|
+
/** Variante PIXI.Texture de directSpriteCanvas (player: a textura de origem já é uma PIXI.Texture, não um
|
|
90
|
+
* canvas cru). `mode` idem — não usado, mantido por paridade de assinatura. */
|
|
91
|
+
export declare function directSpriteTexture(srcTex: DirectTexSource, mode: string): unknown;
|
|
92
|
+
/** Textura do MUNDO para `mode` (normal → a textura viva; hc-* → Renderização Direta, cacheada). */
|
|
93
|
+
export declare function worldTexFor(mode: string): unknown;
|
|
94
|
+
/**
|
|
95
|
+
* Textura do sprite `id` para `mode` (mesma lógica de `worldTexFor`, cache por `id|modo`).
|
|
96
|
+
*
|
|
97
|
+
* Fora dos modos de renderização direta devolve a textura normal declarada pelo jogo — e devolve `undefined`
|
|
98
|
+
* para um id que o jogo não declarou, em vez de lançar: um sprite ausente vira "sem textura" no desenho, que
|
|
99
|
+
* é degradação; lançar aqui derrubaria o quadro inteiro por causa de um item.
|
|
100
|
+
*/
|
|
101
|
+
export declare function spriteTexFor(id: string, mode: string): unknown;
|
|
102
|
+
/** Invalida o cache de mundo (tema/cenário mudou → worldCanvasNormal é outro canvas). */
|
|
103
|
+
export declare function clearWorldTexCache(): void;
|
|
104
|
+
/**
|
|
105
|
+
* Invalida o cache de sprites (cor de papel mudou → `_rebakeDirect` no game.js). Sem argumento limpa TUDO;
|
|
106
|
+
* com um `id`, só as entradas daquele sprite — o que um jogo com muitos sprites vai querer, e o que um cache
|
|
107
|
+
* chaveado só por modo não conseguia oferecer.
|
|
108
|
+
*/
|
|
109
|
+
export declare function clearSpriteTexCache(id?: string): void;
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// render/high-contrast — Renderização Direta (Estágio 4, Tier 2): o motor de alto contraste de acessibilidade
|
|
3
|
+
// (ADR-0011) — outline + color-blocking por papel + fundo recuado (dessaturado/escurecido), 3 níveis de
|
|
4
|
+
// contraste (3:1/4,5:1/7:1). worldToTextureDirect/directBgTexture/directSprite{Canvas,Texture} + os caches
|
|
5
|
+
// _worldTexHC/_coinTexHC (worldTexFor/coinTexFor) + a paleta HC_ROLE (persistida) vivem aqui. INJETADO via
|
|
6
|
+
// initHighContrast: os limites do grid (W/H), os dois níveis de contorno (outlineFg/outlineBg — mutados por
|
|
7
|
+
// setOutlineFg/setOutlineBg no game.js) e os canvases/texturas NORMAIS do mundo/moeda (mundo é `let` e muda de
|
|
8
|
+
// cenário → getter; moeda nunca é reatribuída → valor direto). `_lastSharedViz` FICA no game.js: não é cache de
|
|
9
|
+
// alto contraste, é o cache de "modo já aplicado" que TODO o pipeline de render estático (mundo/parallax/moedas/
|
|
10
|
+
// power-ups/portão) usa para decidir quando reaplicar texturas por viewport — 7 subsistemas fora daqui escrevem
|
|
11
|
+
// nele. `_pupTexHC`/`_playerDirect` (power-ups/player) também ficam no game.js: consomem directSpriteCanvas/
|
|
12
|
+
// directSpriteTexture DAQUI mas não estão na lista de extração desta onda.
|
|
13
|
+
// Ver docs/2-Architecture/adr/ADR-0011-visual-accessibility.yaml + docs/5-Refactoring/plano-modularizacao-mapa.md.
|
|
14
|
+
import { makeCanvas, tex } from './canvas.js';
|
|
15
|
+
import { outlineCanvas } from './sprite-fx.js';
|
|
16
|
+
import { TILE } from '../core/constants.js';
|
|
17
|
+
import { tileAt } from '../core/collision.js';
|
|
18
|
+
import { HC_ROLE_DEF } from './hc-role-data.js';
|
|
19
|
+
import * as store from '../platform/storage.js';
|
|
20
|
+
export { HC_ROLE_KEYS, HC_ROLE_DEF } from './hc-role-data.js';
|
|
21
|
+
export const HC_ROLE = (() => {
|
|
22
|
+
const d = JSON.parse(JSON.stringify(HC_ROLE_DEF));
|
|
23
|
+
const s = store.getJSON(store.KEYS.hcrole, null);
|
|
24
|
+
if (s && typeof s === 'object') {
|
|
25
|
+
for (const k of Object.keys(d)) {
|
|
26
|
+
const v = s[k];
|
|
27
|
+
if (Array.isArray(v) && v.length === 3)
|
|
28
|
+
d[k] = v.map((n) => Math.max(0, Math.min(255, n | 0)));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return d;
|
|
32
|
+
})();
|
|
33
|
+
/** Persiste HC_ROLE (chamado por setRoleColor/resetRoleColors no game.js). Migrado de localStorage direto → store. */
|
|
34
|
+
export function saveHcRole() { store.setJSON(store.KEYS.hcrole, HC_ROLE); }
|
|
35
|
+
// off/mul = mapa da plataforma (mais off = mais clara → mais contraste); bgMul = fundo (menor = mais escuro/
|
|
36
|
+
// recuado). Contraste plataforma×fundo ≈ 3 / 4,5 / 7 (ver ADR-0011: 3:1 é o default, "7:1 fica feio").
|
|
37
|
+
export const DIRECT_CFG = {
|
|
38
|
+
'hc-direto': { off: 55, mul: 0.5, bgMul: 0.30 },
|
|
39
|
+
'hc-direto-45': { off: 66, mul: 0.5, bgMul: 0.28 },
|
|
40
|
+
'hc-direto-7': { off: 100, mul: 0.48, bgMul: 0.13 },
|
|
41
|
+
};
|
|
42
|
+
export function dcfg(mode) { return DIRECT_CFG[mode] || DIRECT_CFG['hc-direto']; }
|
|
43
|
+
/* ===================== dessaturação/escurecimento (fundo/estrutura) ===================== */
|
|
44
|
+
export function dimDesat(c, w, h, mul, blue, off) {
|
|
45
|
+
off = off || 0;
|
|
46
|
+
const img = c.getImageData(0, 0, w, h), d = img.data;
|
|
47
|
+
for (let i = 0; i < d.length; i += 4) {
|
|
48
|
+
if (d[i + 3] < 8)
|
|
49
|
+
continue;
|
|
50
|
+
const l = 0.299 * d[i] + 0.587 * d[i + 1] + 0.114 * d[i + 2], g = off + l * mul;
|
|
51
|
+
d[i] = Math.min(255, g) | 0;
|
|
52
|
+
d[i + 1] = Math.min(255, g * 1.02) | 0;
|
|
53
|
+
d[i + 2] = Math.min(255, g * blue) | 0;
|
|
54
|
+
}
|
|
55
|
+
c.putImageData(img, 0, 0);
|
|
56
|
+
}
|
|
57
|
+
let ctx = null;
|
|
58
|
+
export function initHighContrast(c) { ctx = c; }
|
|
59
|
+
function requireCtx() {
|
|
60
|
+
if (!ctx)
|
|
61
|
+
throw new Error('render/high-contrast: initHighContrast(ctx) ainda não foi chamado');
|
|
62
|
+
return ctx;
|
|
63
|
+
}
|
|
64
|
+
/* ===================== Renderização Direta: mundo + fundo + sprites de 1º plano ===================== */
|
|
65
|
+
/** Mundo em alto contraste: base dessaturada/clareada + repintura por papel (com escada como caso especial:
|
|
66
|
+
* trilhos/degraus, não faixa sólida) + contorno de 2º plano no perímetro externo (navegável × não-navegável). */
|
|
67
|
+
export function worldToTextureDirect(srcCanvas, mode) {
|
|
68
|
+
const hc = requireCtx();
|
|
69
|
+
const cfg = dcfg(mode);
|
|
70
|
+
const cv = makeCanvas(srcCanvas.width, srcCanvas.height), c = cv.getContext('2d');
|
|
71
|
+
c.drawImage(srcCanvas, 0, 0);
|
|
72
|
+
dimDesat(c, cv.width, cv.height, cfg.mul, 1.22, cfg.off); // base: estrutura vira cinza-azulado (mais clara = mais contraste)
|
|
73
|
+
for (let y = 0; y < hc.H; y++)
|
|
74
|
+
for (let x = 0; x < hc.W; x++) {
|
|
75
|
+
const t = tileAt(x, y), role = hc.roleOf(t);
|
|
76
|
+
if (!role)
|
|
77
|
+
continue; // repinta tiles não-estruturais pela cor do papel
|
|
78
|
+
const X = x * TILE, Y = y * TILE;
|
|
79
|
+
// ⚠️ O QUE SOBRA DE PLATAFORMA AQUI. A escada é desenhada com trilhos e degraus porque uma faixa sólida
|
|
80
|
+
// não LÊ como escada — decisão de acessibilidade, que serviria a qualquer jogo com algo escalável. Mas a
|
|
81
|
+
// forma da pergunta injetada ("este tile se desenha como escada?" · "que pintor este papel usa?") ainda
|
|
82
|
+
// não tem evidência que a escolha, e foi um consumidor que mostrou, no menu-nav, que a forma importa mais
|
|
83
|
+
// que a existência da injeção. Declarado em vez de adivinhado. Ver game/tile-roles.
|
|
84
|
+
if (t === 4) { // ESCADA: preto + trilhos e degraus ciano → lê como escada (não faixa verde sólida)
|
|
85
|
+
c.fillStyle = '#0a0e14';
|
|
86
|
+
c.fillRect(X, Y, TILE, TILE);
|
|
87
|
+
c.fillStyle = 'rgb(' + HC_ROLE.climb.join(',') + ')';
|
|
88
|
+
c.fillRect(X + 1, Y, 2, TILE);
|
|
89
|
+
c.fillRect(X + TILE - 3, Y, 2, TILE); // trilhos laterais (cor do papel, customizável)
|
|
90
|
+
for (let ry = 2; ry < TILE - 1; ry += 5)
|
|
91
|
+
c.fillRect(X + 1, Y + ry, TILE - 2, 2); // degraus
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
const rc = HC_ROLE[role], img = c.getImageData(X, Y, TILE, TILE), d = img.data, lo = role === 'hazard' ? 0.58 : 0.44;
|
|
95
|
+
for (let i = 0; i < d.length; i += 4) {
|
|
96
|
+
if (d[i + 3] < 8)
|
|
97
|
+
continue;
|
|
98
|
+
const g = (0.299 * d[i] + 0.587 * d[i + 1] + 0.114 * d[i + 2]) / 255, f = lo + (1 - lo) * g;
|
|
99
|
+
d[i] = Math.min(255, rc[0] * f) | 0;
|
|
100
|
+
d[i + 1] = Math.min(255, rc[1] * f) | 0;
|
|
101
|
+
d[i + 2] = Math.min(255, rc[2] * f) | 0;
|
|
102
|
+
}
|
|
103
|
+
c.putImageData(img, X, Y);
|
|
104
|
+
}
|
|
105
|
+
const th = hc.outlineBg(); // contorno de 2º plano: SÓ o perímetro externo (bordas voltadas ao ar) — não em cada bloco
|
|
106
|
+
if (th > 0) {
|
|
107
|
+
const air = (x, y) => { const t = tileAt(x, y); return t === 0 || t === 1; };
|
|
108
|
+
c.fillStyle = 'rgba(200,222,255,0.97)';
|
|
109
|
+
for (let y = 0; y < hc.H; y++)
|
|
110
|
+
for (let x = 0; x < hc.W; x++) {
|
|
111
|
+
const t = tileAt(x, y);
|
|
112
|
+
if (t === 0 || t === 1)
|
|
113
|
+
continue;
|
|
114
|
+
const X = x * TILE, Y = y * TILE;
|
|
115
|
+
if (air(x, y - 1))
|
|
116
|
+
c.fillRect(X, Y, TILE, th);
|
|
117
|
+
if (air(x, y + 1))
|
|
118
|
+
c.fillRect(X, Y + TILE - th, TILE, th);
|
|
119
|
+
if (air(x - 1, y))
|
|
120
|
+
c.fillRect(X, Y, th, TILE);
|
|
121
|
+
if (air(x + 1, y))
|
|
122
|
+
c.fillRect(X + TILE - th, Y, th, TILE);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return tex(cv);
|
|
126
|
+
}
|
|
127
|
+
/** Fundo/decoração em alto contraste: só dessaturação/escurecimento (recua) — sem repintura por papel nem
|
|
128
|
+
* contorno. Usada por game.js para árvores (treeTexFor) e parallax (parallaxTexFor). */
|
|
129
|
+
export function directBgTexture(srcTex, mode) {
|
|
130
|
+
const cfg = dcfg(mode);
|
|
131
|
+
const cv = makeCanvas(Math.max(1, srcTex.orig.width), Math.max(1, srcTex.orig.height)), dst = tex(cv);
|
|
132
|
+
const paint = () => {
|
|
133
|
+
const s = srcTex.baseTexture.resource && srcTex.baseTexture.resource.source;
|
|
134
|
+
if (!s || !s.width)
|
|
135
|
+
return;
|
|
136
|
+
cv.width = s.width;
|
|
137
|
+
cv.height = s.height;
|
|
138
|
+
const c = cv.getContext('2d');
|
|
139
|
+
c.clearRect(0, 0, cv.width, cv.height);
|
|
140
|
+
c.drawImage(s, 0, 0);
|
|
141
|
+
dimDesat(c, cv.width, cv.height, cfg.bgMul, 1.2);
|
|
142
|
+
dst.update();
|
|
143
|
+
};
|
|
144
|
+
if (srcTex.baseTexture.valid)
|
|
145
|
+
paint();
|
|
146
|
+
else
|
|
147
|
+
srcTex.baseTexture.once('loaded', paint);
|
|
148
|
+
return dst;
|
|
149
|
+
}
|
|
150
|
+
/** Sprite de 1º plano (player/moeda/power-up) em alto contraste: mantém a cor da arte + contorno ESCURO
|
|
151
|
+
* (WCAG 2.4.7 — "salta" do fundo recuado). `mode` não é usado (mantido só p/ paridade de assinatura com as
|
|
152
|
+
* outras 3 funções "direct*" — o dispatch por DIRECT_CFG[mode] já aconteceu no chamador). fg=0 → sem contorno. */
|
|
153
|
+
export function directSpriteCanvas(srcCanvas, mode) {
|
|
154
|
+
void mode;
|
|
155
|
+
const fg = requireCtx().outlineFg();
|
|
156
|
+
return fg > 0 ? outlineCanvas(srcCanvas, fg) : srcCanvas;
|
|
157
|
+
}
|
|
158
|
+
/** Variante PIXI.Texture de directSpriteCanvas (player: a textura de origem já é uma PIXI.Texture, não um
|
|
159
|
+
* canvas cru). `mode` idem — não usado, mantido por paridade de assinatura. */
|
|
160
|
+
export function directSpriteTexture(srcTex, mode) {
|
|
161
|
+
void mode;
|
|
162
|
+
const fg = requireCtx().outlineFg();
|
|
163
|
+
if (fg <= 0)
|
|
164
|
+
return srcTex;
|
|
165
|
+
const th = fg;
|
|
166
|
+
const cv = makeCanvas(Math.max(1, srcTex.orig.width), Math.max(1, srcTex.orig.height)), dst = tex(cv);
|
|
167
|
+
const paint = () => {
|
|
168
|
+
const s = srcTex.baseTexture.resource && srcTex.baseTexture.resource.source;
|
|
169
|
+
if (!s || !s.width)
|
|
170
|
+
return;
|
|
171
|
+
// ===================== O RECORTE VEM ANTES DO CONTORNO =====================
|
|
172
|
+
// Aqui morava o "kage bunshin". Esta função lia a BASE e ignorava o `frame`, e havia um comentário
|
|
173
|
+
// explicando por que isso era seguro: "só é chamada p/ texturas de player (sempre canvas-sourced)". A
|
|
174
|
+
// afirmação era verdadeira e VIROU FALSA — o item 22 empacotou os sprites num atlas de 256×207, e desde
|
|
175
|
+
// então só os quadros que passam pelo tapa-costuras (idle/andar/correr) viram tela própria. Pulo, escada,
|
|
176
|
+
// parede, teto, nado e voo são RECORTE dentro do atlas, e contornar a base deles desenhava o atlas
|
|
177
|
+
// inteiro: todos os quadros do personagem de uma vez, em grade.
|
|
178
|
+
//
|
|
179
|
+
// O tapa-costuras é assíncrono, e por isso o idle também aparecia: com alto contraste ligado cedo, o
|
|
180
|
+
// cache `_playerDirect` memoriza a versão baseada no atlas e a guarda para sempre.
|
|
181
|
+
const f = srcTex.frame;
|
|
182
|
+
const precisaRecortar = !!f && (f.x !== 0 || f.y !== 0 || f.width !== s.width || f.height !== s.height);
|
|
183
|
+
let fonte = s;
|
|
184
|
+
if (precisaRecortar && f) {
|
|
185
|
+
const rec = makeCanvas(Math.max(1, f.width), Math.max(1, f.height));
|
|
186
|
+
const rc = rec.getContext('2d');
|
|
187
|
+
rc.imageSmoothingEnabled = false; // pixel art: reamostrar aqui borraria o contorno que o modo promete
|
|
188
|
+
rc.drawImage(s, f.x, f.y, f.width, f.height, 0, 0, f.width, f.height);
|
|
189
|
+
fonte = rec;
|
|
190
|
+
}
|
|
191
|
+
// outlineCanvas só declara HTMLCanvasElement; o recorte já devolve canvas, e a base sem recorte é
|
|
192
|
+
// canvas-sourced pelos caminhos que restam. O cast preserva o runtime idêntico ao original.
|
|
193
|
+
const o = outlineCanvas(fonte, th);
|
|
194
|
+
cv.width = o.width;
|
|
195
|
+
cv.height = o.height;
|
|
196
|
+
const c = cv.getContext('2d');
|
|
197
|
+
c.clearRect(0, 0, cv.width, cv.height);
|
|
198
|
+
c.drawImage(o, 0, 0);
|
|
199
|
+
dst.update();
|
|
200
|
+
};
|
|
201
|
+
if (srcTex.baseTexture.valid)
|
|
202
|
+
paint();
|
|
203
|
+
else
|
|
204
|
+
srcTex.baseTexture.once('loaded', paint);
|
|
205
|
+
return dst;
|
|
206
|
+
}
|
|
207
|
+
/* ===================== caches preguiçosos (mundo/moeda) + seletor por modo ===================== */
|
|
208
|
+
const _worldTexHC = {};
|
|
209
|
+
/** Cache de sprite recolorido, chaveado por `id|modo`. Era `_coinTexHC[modo]` — um cache por jogo. */
|
|
210
|
+
const _spriteTexHC = {};
|
|
211
|
+
/** Textura do MUNDO para `mode` (normal → a textura viva; hc-* → Renderização Direta, cacheada). */
|
|
212
|
+
export function worldTexFor(mode) {
|
|
213
|
+
const hc = requireCtx();
|
|
214
|
+
if (DIRECT_CFG[mode]) {
|
|
215
|
+
if (!_worldTexHC[mode])
|
|
216
|
+
_worldTexHC[mode] = worldToTextureDirect(hc.getWorldCanvasNormal(), mode);
|
|
217
|
+
return _worldTexHC[mode];
|
|
218
|
+
}
|
|
219
|
+
return hc.getWorldTexNormal();
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Textura do sprite `id` para `mode` (mesma lógica de `worldTexFor`, cache por `id|modo`).
|
|
223
|
+
*
|
|
224
|
+
* Fora dos modos de renderização direta devolve a textura normal declarada pelo jogo — e devolve `undefined`
|
|
225
|
+
* para um id que o jogo não declarou, em vez de lançar: um sprite ausente vira "sem textura" no desenho, que
|
|
226
|
+
* é degradação; lançar aqui derrubaria o quadro inteiro por causa de um item.
|
|
227
|
+
*/
|
|
228
|
+
export function spriteTexFor(id, mode) {
|
|
229
|
+
const hc = requireCtx();
|
|
230
|
+
const src = hc.sprites()[id];
|
|
231
|
+
if (!src)
|
|
232
|
+
return undefined;
|
|
233
|
+
if (DIRECT_CFG[mode]) {
|
|
234
|
+
const chave = id + '|' + mode;
|
|
235
|
+
if (!_spriteTexHC[chave] && src.canvas)
|
|
236
|
+
_spriteTexHC[chave] = tex(directSpriteCanvas(src.canvas, mode));
|
|
237
|
+
return _spriteTexHC[chave] ?? src.tex;
|
|
238
|
+
}
|
|
239
|
+
return src.tex;
|
|
240
|
+
}
|
|
241
|
+
/** Invalida o cache de mundo (tema/cenário mudou → worldCanvasNormal é outro canvas). */
|
|
242
|
+
export function clearWorldTexCache() { for (const k in _worldTexHC)
|
|
243
|
+
delete _worldTexHC[k]; }
|
|
244
|
+
/**
|
|
245
|
+
* Invalida o cache de sprites (cor de papel mudou → `_rebakeDirect` no game.js). Sem argumento limpa TUDO;
|
|
246
|
+
* com um `id`, só as entradas daquele sprite — o que um jogo com muitos sprites vai querer, e o que um cache
|
|
247
|
+
* chaveado só por modo não conseguia oferecer.
|
|
248
|
+
*/
|
|
249
|
+
export function clearSpriteTexCache(id) {
|
|
250
|
+
for (const k in _spriteTexHC) {
|
|
251
|
+
if (id === undefined || k.startsWith(id + '|'))
|
|
252
|
+
delete _spriteTexHC[k];
|
|
253
|
+
}
|
|
254
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
/**
|
|
3
|
+
* 17-sample `feFuncR/G/B` table for tableValues, blending contrast-stretch (linear, α=1.3, μ=0.5) with an
|
|
4
|
+
* S-curve (quadratic) by `t`. NOT clamped — mirrors game.js's lqCurve verbatim (callers clamp `t` upstream).
|
|
5
|
+
*/
|
|
6
|
+
export declare function lqCurve(t: number): string;
|
|
7
|
+
/**
|
|
8
|
+
* Continuous `t` -> the i18n KEY of the screen-reader/UI label. NOT clamped — mirrors game.js's lqName verbatim
|
|
9
|
+
* (t<=0 and t>=1-ish inputs fall through to the end labels naturally).
|
|
10
|
+
*
|
|
11
|
+
* Returns a key rather than resolved text for two reasons. This module is a render leaf and stays free of the
|
|
12
|
+
* i18n dependency; and its parameter is already called `t`, so importing i18n's `t` here would shadow it — a
|
|
13
|
+
* rename to dodge a name clash is a worse reason to change a signature than the boundary itself.
|
|
14
|
+
*/
|
|
15
|
+
export declare function lqName(t: number): string;
|
|
16
|
+
/** Reads (or lazily creates) the shared `<filter id="lq-enh">` SVG node, appended once to `document.body`. */
|
|
17
|
+
export declare function ensureLqFilter(): SVGFilterElement;
|
|
18
|
+
/** CSS `filter` fragment for the current lqT ('' when off) — ensures the SVG node exists BEFORE returning the
|
|
19
|
+
* `url()` reference (a dangling reference would hide the canvas). Composed by callers with viz-mode filters. */
|
|
20
|
+
export declare function lqFilter(): string;
|
|
21
|
+
/** Current lqT (0..1) — read by the visual-settings panel and the debug/`__incl` surface. */
|
|
22
|
+
export declare function getLqT(): number;
|
|
23
|
+
export interface LqFilterCtx {
|
|
24
|
+
/**
|
|
25
|
+
* Recomposes the app's CSS filter after lqT changes. In game.js this is either a full `applyVizGlobal()`
|
|
26
|
+
* re-apply (single-player: also touches viz-mode texture caches, `_lastSharedViz`) or a direct
|
|
27
|
+
* `app.view.style.filter = ...` write composed with the active viz-mode filter (multiplayer). That
|
|
28
|
+
* composition — and the texture-cache invalidation it triggers elsewhere — belongs to other subsystems and
|
|
29
|
+
* stays in game.js; this module only owns lqT + the SVG filter node.
|
|
30
|
+
*/
|
|
31
|
+
onChange: () => void;
|
|
32
|
+
}
|
|
33
|
+
/** Wires the injected recompose callback. Call once during game.js boot. */
|
|
34
|
+
export declare function initLqFilter(ctx: LqFilterCtx): void;
|
|
35
|
+
/** Sets lqT (clamped to 0..1), persists it, updates the live SVG table (if the filter is already on-screen),
|
|
36
|
+
* then calls the injected `onChange` to let game.js recompose the CSS filter. Mirrors game.js's setLq. */
|
|
37
|
+
export declare function setLq(t: number): void;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// render/lq-filter.ts — Realce de contraste Linear→Quadrático (baixa visão, PESQUISA-ALTO-CONTRASTE §2.3).
|
|
3
|
+
// Curva de tom POR PIXEL na tela inteira, composta via SVG feComponentTransfer (17 amostras, sRGB) no CSS
|
|
4
|
+
// filter do canvas. `lqCurve`/`lqName` são puros (project node); `ensureLqFilter`/`setLq` são a casca fina que
|
|
5
|
+
// cria o nó SVG e escreve no DOM. Recompor o CSS filter final (junto com os modos de cor) fica em game.js via
|
|
6
|
+
// `onChange` injetado — não duplica _lastSharedViz/_rebakeDirect (outros subsistemas). Extraído verbatim.
|
|
7
|
+
import * as store from '../platform/storage.js';
|
|
8
|
+
const FILTER_ID = 'lq-enh';
|
|
9
|
+
function clamp01(t) {
|
|
10
|
+
return Math.max(0, Math.min(1, t));
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* 17-sample `feFuncR/G/B` table for tableValues, blending contrast-stretch (linear, α=1.3, μ=0.5) with an
|
|
14
|
+
* S-curve (quadratic) by `t`. NOT clamped — mirrors game.js's lqCurve verbatim (callers clamp `t` upstream).
|
|
15
|
+
*/
|
|
16
|
+
export function lqCurve(t) {
|
|
17
|
+
const N = 17, a = 1.3, out = [];
|
|
18
|
+
for (let i = 0; i < N; i++) {
|
|
19
|
+
const x = i / (N - 1);
|
|
20
|
+
const lin = Math.min(1, Math.max(0, a * (x - 0.5) + 0.5));
|
|
21
|
+
const quad = x < 0.5 ? 2 * x * x : 1 - 2 * (1 - x) * (1 - x);
|
|
22
|
+
out.push(((1 - t) * lin + t * quad).toFixed(4));
|
|
23
|
+
}
|
|
24
|
+
return out.join(' ');
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Continuous `t` -> the i18n KEY of the screen-reader/UI label. NOT clamped — mirrors game.js's lqName verbatim
|
|
28
|
+
* (t<=0 and t>=1-ish inputs fall through to the end labels naturally).
|
|
29
|
+
*
|
|
30
|
+
* Returns a key rather than resolved text for two reasons. This module is a render leaf and stays free of the
|
|
31
|
+
* i18n dependency; and its parameter is already called `t`, so importing i18n's `t` here would shadow it — a
|
|
32
|
+
* rename to dodge a name clash is a worse reason to change a signature than the boundary itself.
|
|
33
|
+
*/
|
|
34
|
+
export function lqName(t) {
|
|
35
|
+
return t <= 0 ? 'lq.off' : t < 0.34 ? 'lq.linear' : t < 0.67 ? 'lq.mixed' : 'lq.quadratic';
|
|
36
|
+
}
|
|
37
|
+
// ---------- Thin DOM shell ----------
|
|
38
|
+
/** Current L→Q amount (0..1), persisted in platform/storage under `KEYS.lq` ('incl_lq'). Module-local state. */
|
|
39
|
+
let lqT = clamp01(store.getNum(store.KEYS.lq, 0));
|
|
40
|
+
/** Reads (or lazily creates) the shared `<filter id="lq-enh">` SVG node, appended once to `document.body`. */
|
|
41
|
+
export function ensureLqFilter() {
|
|
42
|
+
const existing = document.getElementById(FILTER_ID);
|
|
43
|
+
if (existing)
|
|
44
|
+
return existing;
|
|
45
|
+
const NS = 'http://www.w3.org/2000/svg';
|
|
46
|
+
const svg = document.createElementNS(NS, 'svg');
|
|
47
|
+
svg.setAttribute('width', '0');
|
|
48
|
+
svg.setAttribute('height', '0');
|
|
49
|
+
svg.setAttribute('aria-hidden', 'true');
|
|
50
|
+
svg.style.position = 'absolute';
|
|
51
|
+
const f = document.createElementNS(NS, 'filter');
|
|
52
|
+
f.id = FILTER_ID;
|
|
53
|
+
f.setAttribute('color-interpolation-filters', 'sRGB');
|
|
54
|
+
const ct = document.createElementNS(NS, 'feComponentTransfer');
|
|
55
|
+
['feFuncR', 'feFuncG', 'feFuncB'].forEach((ch) => {
|
|
56
|
+
const fn = document.createElementNS(NS, ch);
|
|
57
|
+
fn.setAttribute('type', 'table');
|
|
58
|
+
fn.setAttribute('tableValues', lqCurve(lqT));
|
|
59
|
+
ct.appendChild(fn);
|
|
60
|
+
});
|
|
61
|
+
f.appendChild(ct);
|
|
62
|
+
svg.appendChild(f);
|
|
63
|
+
document.body.appendChild(svg);
|
|
64
|
+
return f;
|
|
65
|
+
}
|
|
66
|
+
/** CSS `filter` fragment for the current lqT ('' when off) — ensures the SVG node exists BEFORE returning the
|
|
67
|
+
* `url()` reference (a dangling reference would hide the canvas). Composed by callers with viz-mode filters. */
|
|
68
|
+
export function lqFilter() {
|
|
69
|
+
if (lqT <= 0)
|
|
70
|
+
return '';
|
|
71
|
+
ensureLqFilter();
|
|
72
|
+
return `url(#${FILTER_ID})`;
|
|
73
|
+
}
|
|
74
|
+
/** Current lqT (0..1) — read by the visual-settings panel and the debug/`__incl` surface. */
|
|
75
|
+
export function getLqT() {
|
|
76
|
+
return lqT;
|
|
77
|
+
}
|
|
78
|
+
let onChange = () => { };
|
|
79
|
+
/** Wires the injected recompose callback. Call once during game.js boot. */
|
|
80
|
+
export function initLqFilter(ctx) {
|
|
81
|
+
onChange = ctx.onChange;
|
|
82
|
+
}
|
|
83
|
+
/** Sets lqT (clamped to 0..1), persists it, updates the live SVG table (if the filter is already on-screen),
|
|
84
|
+
* then calls the injected `onChange` to let game.js recompose the CSS filter. Mirrors game.js's setLq. */
|
|
85
|
+
export function setLq(t) {
|
|
86
|
+
lqT = clamp01(t);
|
|
87
|
+
store.set(store.KEYS.lq, lqT);
|
|
88
|
+
if (lqT > 0) {
|
|
89
|
+
const f = ensureLqFilter();
|
|
90
|
+
const tv = lqCurve(lqT);
|
|
91
|
+
f.querySelectorAll('feFuncR,feFuncG,feFuncB').forEach((fn) => fn.setAttribute('tableValues', tv));
|
|
92
|
+
}
|
|
93
|
+
onChange();
|
|
94
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
import * as PIXI from 'pixi.js';
|
|
3
|
+
export declare function initMinimap(stage: PIXI.Container, W: number, H: number): void;
|
|
4
|
+
export declare function markSeen(camX: number, camY: number): void;
|
|
5
|
+
export declare function redrawMinimapIfDirty(): void;
|
|
6
|
+
export declare function drawMinimapPlayer(worldX: number, worldY: number): void;
|
|
7
|
+
export declare function resetMinimap(): void;
|
|
8
|
+
export declare function setMinimapCorner(touch: boolean): void;
|
|
9
|
+
export declare function setMinimapVisible(v: boolean): void;
|
|
10
|
+
export declare function getMinimap(): PIXI.Container | null;
|
|
11
|
+
export declare function minimapSeenCount(): number;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// render/minimap.ts — minimapa com FOG-OF-WAR (Estágio 4, Tier 1). Um container PixiJS no canto do stage: fundo
|
|
3
|
+
// + tiles JÁ VISTOS (revelados pela câmera) + ponto do jogador. O que já foi visto persiste até o fim da fase.
|
|
4
|
+
// initMinimap cria os objetos (import PURO — nada de PIXI no import); markSeen revela; redrawMinimapIfDirty
|
|
5
|
+
// repinta só quando mudou; drawMinimapPlayer põe o ponto; resetMinimap re-escurece (fim de fase). Cor do tile
|
|
6
|
+
// vem de core/collision (tileAt/isSolidType). Deps: PIXI (npm), constants (TILE/LOGICAL_*), collision.
|
|
7
|
+
import * as PIXI from 'pixi.js';
|
|
8
|
+
import { LOGICAL_W, LOGICAL_H, TILE } from '../core/constants.js';
|
|
9
|
+
import { tileAt, isSolidType } from '../core/collision.js';
|
|
10
|
+
const MM_SCALE = 0.8, MM_PAD = 4;
|
|
11
|
+
let _minimap = null;
|
|
12
|
+
let _mmTiles = null, _mmPlayer = null;
|
|
13
|
+
let _seen = [];
|
|
14
|
+
let _dirty = false;
|
|
15
|
+
let _W = 0, _H = 0, _mmW = 0, _mmH = 0;
|
|
16
|
+
// Cria o container + graphics no stage e a grade de "visto" (W×H). Chamado uma vez no boot do game.js.
|
|
17
|
+
export function initMinimap(stage, W, H) {
|
|
18
|
+
_W = W;
|
|
19
|
+
_H = H;
|
|
20
|
+
_mmW = W * MM_SCALE;
|
|
21
|
+
_mmH = H * MM_SCALE;
|
|
22
|
+
_minimap = new PIXI.Container();
|
|
23
|
+
_minimap.x = MM_PAD;
|
|
24
|
+
_minimap.y = LOGICAL_H - _mmH - MM_PAD;
|
|
25
|
+
_minimap.alpha = 0.92;
|
|
26
|
+
stage.addChild(_minimap);
|
|
27
|
+
const mmBg = new PIXI.Graphics();
|
|
28
|
+
mmBg.beginFill(0x05070f, 0.72);
|
|
29
|
+
mmBg.drawRect(-1, -1, _mmW + 2, _mmH + 2);
|
|
30
|
+
mmBg.endFill();
|
|
31
|
+
_mmTiles = new PIXI.Graphics();
|
|
32
|
+
_mmPlayer = new PIXI.Graphics();
|
|
33
|
+
_minimap.addChild(mmBg, _mmTiles, _mmPlayer);
|
|
34
|
+
_seen = Array.from({ length: H }, () => new Uint8Array(W));
|
|
35
|
+
_dirty = false;
|
|
36
|
+
}
|
|
37
|
+
// Marca como visto todos os tiles dentro da janela da câmera (camX,camY = canto sup-esq do viewport, em px).
|
|
38
|
+
export function markSeen(camX, camY) {
|
|
39
|
+
const tx0 = Math.max(0, (camX / TILE) | 0), tx1 = Math.min(_W - 1, ((camX + LOGICAL_W) / TILE) | 0);
|
|
40
|
+
const ty0 = Math.max(0, (camY / TILE) | 0), ty1 = Math.min(_H - 1, ((camY + LOGICAL_H) / TILE) | 0);
|
|
41
|
+
for (let ty = ty0; ty <= ty1; ty++)
|
|
42
|
+
for (let tx = tx0; tx <= tx1; tx++)
|
|
43
|
+
if (!_seen[ty][tx]) {
|
|
44
|
+
_seen[ty][tx] = 1;
|
|
45
|
+
_dirty = true;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// Repinta os tiles vistos SÓ se algo novo foi revelado (barato: o fog muda pouco por frame). Cor por tipo.
|
|
49
|
+
export function redrawMinimapIfDirty() {
|
|
50
|
+
if (!_dirty || !_mmTiles)
|
|
51
|
+
return;
|
|
52
|
+
_mmTiles.clear();
|
|
53
|
+
for (let ty = 0; ty < _H; ty++)
|
|
54
|
+
for (let tx = 0; tx < _W; tx++) {
|
|
55
|
+
if (!_seen[ty][tx])
|
|
56
|
+
continue;
|
|
57
|
+
const t = tileAt(tx, ty);
|
|
58
|
+
const col = isSolidType(t) ? 0x9a93b5 : (t === 3 ? 0x2f6fae : (t === 9 ? 0xff5b3a : 0x232038)); // sólido / água / lava / ar
|
|
59
|
+
_mmTiles.beginFill(col, 1);
|
|
60
|
+
_mmTiles.drawRect(tx * MM_SCALE, ty * MM_SCALE, MM_SCALE + 0.4, MM_SCALE + 0.4);
|
|
61
|
+
_mmTiles.endFill();
|
|
62
|
+
}
|
|
63
|
+
_dirty = false;
|
|
64
|
+
}
|
|
65
|
+
// Ponto do jogador (worldX,worldY em px do mundo; o game.js passa o centro do corpo).
|
|
66
|
+
export function drawMinimapPlayer(worldX, worldY) {
|
|
67
|
+
if (!_mmPlayer)
|
|
68
|
+
return;
|
|
69
|
+
_mmPlayer.clear();
|
|
70
|
+
_mmPlayer.beginFill(0xffd23f, 1);
|
|
71
|
+
_mmPlayer.drawRect((worldX / TILE) * MM_SCALE - 1, (worldY / TILE) * MM_SCALE - 1, 2.6, 2.6);
|
|
72
|
+
_mmPlayer.endFill();
|
|
73
|
+
}
|
|
74
|
+
// Fim de fase: o fog-of-war volta a escurecer (o mapa some até ser revisto).
|
|
75
|
+
export function resetMinimap() { _seen.forEach((r) => r.fill(0)); _dirty = true; }
|
|
76
|
+
// Reposiciona o minimapa: no toque vai p/ o canto sup-dir (não briga com os controles); senão inf-esq.
|
|
77
|
+
export function setMinimapCorner(touch) {
|
|
78
|
+
if (!_minimap)
|
|
79
|
+
return;
|
|
80
|
+
if (touch) {
|
|
81
|
+
_minimap.x = LOGICAL_W - _mmW - MM_PAD;
|
|
82
|
+
_minimap.y = MM_PAD;
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
_minimap.x = MM_PAD;
|
|
86
|
+
_minimap.y = LOGICAL_H - _mmH - MM_PAD;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
export function setMinimapVisible(v) { if (_minimap)
|
|
90
|
+
_minimap.visible = v; } // some no título / no multiplayer
|
|
91
|
+
export function getMinimap() { return _minimap; } // p/ o __incl (debug/teste)
|
|
92
|
+
export function minimapSeenCount() { let n = 0; for (const r of _seen)
|
|
93
|
+
for (const v of r)
|
|
94
|
+
n += v; return n; } // stat de fog revelado
|