@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,228 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// render/city-tex — a ARTE PROCEDURAL DA RUA: os bichos, os pedestres e os carros do cenário Cidade.
|
|
3
|
+
//
|
|
4
|
+
// Três blocos que viviam soltos no main.js (`LIFE_TEX`, `ADULT_TEX`, `CAR_TEX`) e que são a MESMA coisa vista
|
|
5
|
+
// de três ângulos: retângulos coloridos carimbados num canvas offscreen que vira PIXI.Texture. Por isso caem
|
|
6
|
+
// juntos aqui, e não em três módulos: o que os une não é o assunto (bicho ≠ carro), é a TÉCNICA e o CONSUMIDOR
|
|
7
|
+
// — quem os pede é a Cidade, e só ela. `render/props` (moeda, árvore) e `render/world-tex` (o nível inteiro)
|
|
8
|
+
// ficam onde estão: props é decoração de QUALQUER cenário, world-tex é o mapa, não a rua.
|
|
9
|
+
//
|
|
10
|
+
// A DUPLICAÇÃO QUE ESTA EXTRAÇÃO CUROU: cada um dos três blocos declarava o SEU `mk` e o SEU `px`, com
|
|
11
|
+
// assinaturas que não conversavam — `mk(w,h,paint)` com `px(x,y,w,h,cor)`; `mk(paint)` de 16×32 fixo com
|
|
12
|
+
// `px(x,y,w,h)` e a cor presa numa `const` de fora; `mk(body,dark,top)` de 78×36 fixo com `px(x,y,w,h,cl)`.
|
|
13
|
+
// Três grafias para "pinte um retângulo". Agora existe uma só, `pixelCanvas`/`pixelTexture` em render/canvas.
|
|
14
|
+
// EFEITO COLATERAL ACEITO: as silhuetas de adulto passaram a receber a cor em CADA chamada (`ADULT_COL`) em vez
|
|
15
|
+
// de um `fillStyle` único antes do laço. O bitmap resultante é idêntico ao byte — só o número de atribuições de
|
|
16
|
+
// `fillStyle` muda —, e em troca some a última assinatura divergente.
|
|
17
|
+
//
|
|
18
|
+
// SEM I/O NO IMPORT — este é o ponto mais importante do arquivo. As texturas de origem nasciam em IIFE no corpo
|
|
19
|
+
// do main.js, o que funciona lá porque o main.js só roda no navegador; um módulo em `render/` é importado pelo
|
|
20
|
+
// project `node` do Vitest, onde `makeCanvas` toca `document` e explode. Por isso NÃO há constante pronta aqui:
|
|
21
|
+
// há `createCityTextures()`, chamada uma vez pelo main.js no boot. Mesmo precedente de `render/textures`
|
|
22
|
+
// (caches preenchidos por `initTextures`) e `render/viewports` (`initViewports`).
|
|
23
|
+
//
|
|
24
|
+
// A FRONTEIRA COM O JOGO NÃO SE MEXE: `game/life` e `game/traffic` continuam recebendo as texturas por INJEÇÃO
|
|
25
|
+
// (`initLife({ lifeTex, adultTex, … })`, `initTraffic({ CAR_TEX, … })`) — eles nunca importaram este módulo e
|
|
26
|
+
// continuam sem importar, para seguirem testáveis em `node` com texturas de mentira. O main.js é que passou a
|
|
27
|
+
// buscar aqui o que antes fabricava em casa. Quem cria as CAMADAS (`lifeLayer`, `carLayer`) e solda o z-order
|
|
28
|
+
// segue sendo o main.js: z-order é composição de cena, não arte.
|
|
29
|
+
//
|
|
30
|
+
// ARMADILHAS AO MEXER NA ARTE:
|
|
31
|
+
// · Os comentários de arte (`// cabeça alta / bicando`, `// M2: magro, de boné`, `// coluna B + retrovisor`)
|
|
32
|
+
// são a ÚNICA documentação de qual retângulo é qual parte da figura. Não os apague ao editar coordenadas.
|
|
33
|
+
// · A silhueta F2 NÃO chama `arms` — é verbatim do original, não esquecimento: os dois retângulos altos do
|
|
34
|
+
// cabelo longo (`2,4,3,13` e `11,4,3,13`) ocupam exatamente onde os braços iriam. Se um dia isso for
|
|
35
|
+
// "consertado", a F2 muda de forma no jogo.
|
|
36
|
+
// · Os carros são 3× NATIVOS (78×36 desenhados, sem upscale) porque a rua fica na camada da frente, perto da
|
|
37
|
+
// câmera; os bichos são 1× (7×6 … 13×9). Não normalize as escalas: `game/traffic` conta com os 78px.
|
|
38
|
+
// · Dois quadros por figura, sempre na ordem `[f0, f1]`: `game/life` alterna por índice (`f = f ? 0 : 1`).
|
|
39
|
+
// Inverter o par não quebra nenhum tipo — quebra a animação, em silêncio.
|
|
40
|
+
import { pixelTexture } from './canvas.js';
|
|
41
|
+
/* ===================== bichos: pombo, pombo voando, gato, cão ===================== */
|
|
42
|
+
/** Pombo no chão 7×6 — corpo cinza, bico laranja, pés. f alterna cabeça alta ↔ bicando. */
|
|
43
|
+
export const paintPombo = (f) => (px) => {
|
|
44
|
+
px(1, 2, 4, 2, '#9aa3b2');
|
|
45
|
+
px(0, 3, 2, 1, '#7d8695');
|
|
46
|
+
if (f === 0) {
|
|
47
|
+
px(4, 1, 2, 2, '#b9c2d0');
|
|
48
|
+
px(6, 2, 1, 1, '#e0a23c');
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
px(4, 3, 2, 2, '#b9c2d0');
|
|
52
|
+
px(6, 4, 1, 1, '#e0a23c');
|
|
53
|
+
} // cabeça alta / bicando
|
|
54
|
+
px(2, 5, 1, 1, '#c96a2e');
|
|
55
|
+
px(4, 5, 1, 1, '#c96a2e');
|
|
56
|
+
};
|
|
57
|
+
/** Pombo em revoada 8×7 — mesmo corpo, asa batendo. */
|
|
58
|
+
export const paintPomboFly = (f) => (px) => {
|
|
59
|
+
px(2, 3, 4, 2, '#9aa3b2');
|
|
60
|
+
px(6, 2, 2, 2, '#b9c2d0');
|
|
61
|
+
px(7, 3, 1, 1, '#e0a23c');
|
|
62
|
+
if (f === 0)
|
|
63
|
+
px(1, 0, 4, 2, '#c8d0dc');
|
|
64
|
+
else
|
|
65
|
+
px(1, 5, 4, 2, '#c8d0dc'); // asa cima/baixo
|
|
66
|
+
};
|
|
67
|
+
/** Gato 12×8 — corpo/cabeça/orelhas/rabo escuros, olho verde; f alterna as patas. */
|
|
68
|
+
export const paintGato = (f) => (px) => {
|
|
69
|
+
px(1, 3, 8, 3, '#454b58');
|
|
70
|
+
px(8, 1, 3, 3, '#454b58');
|
|
71
|
+
px(8, 0, 1, 1, '#454b58');
|
|
72
|
+
px(10, 0, 1, 1, '#454b58');
|
|
73
|
+
px(0, 2, 1, 3, '#454b58');
|
|
74
|
+
px(9, 2, 1, 1, '#9fe07a');
|
|
75
|
+
if (f === 0) {
|
|
76
|
+
px(2, 6, 1, 2, '#454b58');
|
|
77
|
+
px(7, 6, 1, 2, '#454b58');
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
px(3, 6, 1, 2, '#454b58');
|
|
81
|
+
px(6, 6, 1, 2, '#454b58');
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
/** Cão 13×9 — corpo marrom, focinho escuro, orelha; f alterna as patas. */
|
|
85
|
+
export const paintCao = (f) => (px) => {
|
|
86
|
+
px(1, 3, 9, 4, '#8a6a44');
|
|
87
|
+
px(9, 1, 4, 4, '#8a6a44');
|
|
88
|
+
px(12, 2, 1, 2, '#3a2d1c');
|
|
89
|
+
px(9, 0, 2, 2, '#6d5334');
|
|
90
|
+
px(0, 2, 1, 3, '#8a6a44');
|
|
91
|
+
if (f === 0) {
|
|
92
|
+
px(2, 7, 1, 2, '#6d5334');
|
|
93
|
+
px(8, 7, 1, 2, '#6d5334');
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
px(3, 7, 1, 2, '#6d5334');
|
|
97
|
+
px(7, 7, 1, 2, '#6d5334');
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
/** Catálogo dos bichos: tamanho do canvas + painter por quadro. Fonte única que `createCityTextures` percorre
|
|
101
|
+
* e que o teste de regressão de arte itera — as chaves são as MESMAS que `game/life` lê em `LifeTexAtlas`. */
|
|
102
|
+
export const CREATURE_ART = {
|
|
103
|
+
pombo: { w: 7, h: 6, paint: paintPombo },
|
|
104
|
+
pomboFly: { w: 8, h: 7, paint: paintPomboFly },
|
|
105
|
+
gato: { w: 12, h: 8, paint: paintGato },
|
|
106
|
+
cao: { w: 13, h: 9, paint: paintCao },
|
|
107
|
+
};
|
|
108
|
+
/* ===================== adultos: 6 silhuetas 16×32 ===================== */
|
|
109
|
+
// Adultos = SILHUETAS 16×32 (mesma proporção/tamanho do personagem), formatos distintos M/F (pedido do José)
|
|
110
|
+
/** Tamanho da silhueta — casado com o do personagem jogável (render/sprites). */
|
|
111
|
+
export const ADULT_W = 16, ADULT_H = 32;
|
|
112
|
+
/** Cor única de toda silhueta (era o `const col` fora do `mk` no main.js). */
|
|
113
|
+
export const ADULT_COL = '#262b38';
|
|
114
|
+
/** Pernas: calça (duas colunas alternando o passo) ou saia + pernas curtas. */
|
|
115
|
+
const legs = (px, f, skirt) => {
|
|
116
|
+
if (skirt) {
|
|
117
|
+
px(4, 20, 8, 6, ADULT_COL);
|
|
118
|
+
if (f === 0) {
|
|
119
|
+
px(5, 26, 2, 6, ADULT_COL);
|
|
120
|
+
px(9, 26, 2, 6, ADULT_COL);
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
px(4, 26, 2, 6, ADULT_COL);
|
|
124
|
+
px(10, 26, 2, 6, ADULT_COL);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
if (f === 0) {
|
|
129
|
+
px(5, 20, 3, 12, ADULT_COL);
|
|
130
|
+
px(9, 20, 3, 11, ADULT_COL);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
px(4, 20, 3, 11, ADULT_COL);
|
|
134
|
+
px(10, 20, 3, 12, ADULT_COL);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
/** Braços pendentes, defasados entre os quadros (dá o balanço da caminhada). */
|
|
139
|
+
const arms = (px, f) => {
|
|
140
|
+
if (f === 0) {
|
|
141
|
+
px(2, 10, 2, 8, ADULT_COL);
|
|
142
|
+
px(12, 10, 2, 8, ADULT_COL);
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
px(2, 11, 2, 7, ADULT_COL);
|
|
146
|
+
px(12, 9, 2, 8, ADULT_COL);
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
/** As 6 silhuetas — 3 masculinas + 3 femininas, todas 16×32. A ordem é a do original (M1, M2, M3, F1, F2, F3);
|
|
150
|
+
* `game/life` sorteia por índice, então reordenar troca qual silhueta cada sorteio produz. */
|
|
151
|
+
export const ADULT_SHAPES = [
|
|
152
|
+
(f) => (px) => { px(4, 0, 8, 6, ADULT_COL); px(3, 6, 10, 14, ADULT_COL); arms(px, f); legs(px, f, false); }, // M1: ombros largos
|
|
153
|
+
(f) => (px) => { px(5, 0, 6, 5, ADULT_COL); px(3, 1, 10, 2, ADULT_COL); px(5, 5, 6, 15, ADULT_COL); arms(px, f); legs(px, f, false); }, // M2: magro, de boné
|
|
154
|
+
(f) => (px) => { px(4, 1, 8, 5, ADULT_COL); px(2, 6, 12, 14, ADULT_COL); arms(px, f); legs(px, f, false); }, // M3: troncudo
|
|
155
|
+
(f) => (px) => { px(4, 0, 8, 6, ADULT_COL); px(11, 3, 3, 10, ADULT_COL); px(4, 6, 8, 10, ADULT_COL); px(3, 16, 10, 5, ADULT_COL); arms(px, f); legs(px, f, true); }, // F1: rabo de cavalo + saia
|
|
156
|
+
(f) => (px) => { px(3, 0, 10, 6, ADULT_COL); px(2, 4, 3, 13, ADULT_COL); px(11, 4, 3, 13, ADULT_COL); px(5, 6, 6, 10, ADULT_COL); px(4, 16, 8, 5, ADULT_COL); legs(px, f, true); }, // F2: cabelo longo + vestido (sem `arms` — ver o cabeçalho)
|
|
157
|
+
(f) => (px) => { px(3, 0, 10, 7, ADULT_COL); px(4, 7, 8, 9, ADULT_COL); px(3, 16, 10, 5, ADULT_COL); arms(px, f); legs(px, f, true); }, // F3: chanel + saia
|
|
158
|
+
];
|
|
159
|
+
/* ===================== carros: 4 cores, 78×36 (3× nativo) ===================== */
|
|
160
|
+
/** Tamanho do carro — 3× NATIVO (detalhado, sem upscale); `game/traffic` conta com os 78px de largura. */
|
|
161
|
+
export const CAR_W = 78, CAR_H = 36;
|
|
162
|
+
/** As 4 pinturas de fábrica: `[lataria, sombra, teto]`. */
|
|
163
|
+
export const CAR_PALETTES = [
|
|
164
|
+
['#c8452e', '#7d2717', '#a03a24'], // vermelho
|
|
165
|
+
['#2e6fc8', '#193f7d', '#2757a0'], // azul
|
|
166
|
+
['#3aa15b', '#1f6336', '#2f8a4c'], // verde
|
|
167
|
+
['#c8a12e', '#7d641a', '#a8862a'], // amarelo
|
|
168
|
+
];
|
|
169
|
+
/* ===================== A RODA É REDONDA, E ISSO PRECISOU SER DITO =====================
|
|
170
|
+
* A primeira versão do carro (portada verbatim do monólito) fazia o pneu com UM retângulo de 14×11. Numa
|
|
171
|
+
* arte de retângulos carimbados, a roda é o único elemento que o olho sabe de cor que é redondo — e é o
|
|
172
|
+
* primeiro que denuncia o desenho inteiro. O Dev, olhando a rua: "melhore os carros, estão muito feios com
|
|
173
|
+
* as rodas quadradas."
|
|
174
|
+
*
|
|
175
|
+
* O jeito de ter círculo aqui é o de sempre em pixel art: uma TABELA de linhas, cada uma com o recuo e a
|
|
176
|
+
* largura daquela altura. Não é aproximação de matemática em tempo de execução — é o desenho, escrito. */
|
|
177
|
+
/** Pneu Ø14: `[recuo, largura]` por linha, de cima para baixo. */
|
|
178
|
+
const PNEU = [
|
|
179
|
+
[5, 4], [3, 8], [2, 10], [1, 12], [1, 12], [0, 14], [0, 14],
|
|
180
|
+
[0, 14], [0, 14], [1, 12], [1, 12], [2, 10], [3, 8], [5, 4],
|
|
181
|
+
];
|
|
182
|
+
/** Aro Ø8, no mesmo formato — desenhado dentro do pneu. */
|
|
183
|
+
const ARO = [
|
|
184
|
+
[2, 4], [1, 6], [0, 8], [0, 8], [0, 8], [0, 8], [1, 6], [2, 4],
|
|
185
|
+
];
|
|
186
|
+
/** Um carro 78×36 na paleta dada. Vidros, brilhos, farol e lanterna são fixos (não seguem a lataria). */
|
|
187
|
+
export const paintCar = (body, dark, top) => (px) => {
|
|
188
|
+
px(3, 14, 72, 13, body);
|
|
189
|
+
px(3, 25, 72, 2, dark); // corpo + saia escura
|
|
190
|
+
px(1, 16, 2, 8, dark);
|
|
191
|
+
px(75, 16, 2, 8, dark); // para-choques
|
|
192
|
+
// Cabine com o teto AFUNILADO: eram 40px de bloco reto, e teto reto num carro de perfil lê como caixa.
|
|
193
|
+
px(17, 4, 36, 2, top);
|
|
194
|
+
px(15, 6, 40, 9, top);
|
|
195
|
+
px(17, 6, 36, 9, body);
|
|
196
|
+
px(19, 7, 14, 7, '#bcd6ee');
|
|
197
|
+
px(37, 7, 14, 7, '#bcd6ee'); // vidros
|
|
198
|
+
px(20, 8, 4, 2, '#eef6ff');
|
|
199
|
+
px(38, 8, 4, 2, '#eef6ff'); // brilho dos vidros
|
|
200
|
+
px(34, 7, 3, 7, top);
|
|
201
|
+
px(53, 10, 4, 4, dark); // coluna B + retrovisor
|
|
202
|
+
px(3, 14, 72, 1, 'rgba(255,255,255,.28)'); // realce superior da lataria
|
|
203
|
+
px(35, 15, 1, 10, dark); // frisa entre as duas portas
|
|
204
|
+
px(0, 17, 3, 5, '#ffd9a0');
|
|
205
|
+
px(75, 17, 3, 5, '#ff6a5a'); // farol / lanterna
|
|
206
|
+
const wheel = (wx) => {
|
|
207
|
+
px(wx + 1, 20, 12, 2, dark);
|
|
208
|
+
px(wx - 1, 21, 16, 1, dark); // caixa de roda, em arco
|
|
209
|
+
PNEU.forEach(([dx, w], i) => px(wx + dx, 22 + i, w, 1, '#10131a')); // pneu
|
|
210
|
+
ARO.forEach(([dx, w], i) => px(wx + 3 + dx, 25 + i, w, 1, '#2b3140')); // aro
|
|
211
|
+
px(wx + 6, 28, 2, 2, '#8a93a8'); // cubo
|
|
212
|
+
};
|
|
213
|
+
wheel(11);
|
|
214
|
+
wheel(53);
|
|
215
|
+
};
|
|
216
|
+
/** Assa TODA a arte da rua de uma vez. TOCA `document` — chame só no boot do navegador, nunca no import.
|
|
217
|
+
* Não memoiza de propósito: é chamada uma vez pelo main.js; um cache aqui só esconderia uma segunda chamada. */
|
|
218
|
+
export function createCityTextures() {
|
|
219
|
+
const pair = (w, h, paint) => [pixelTexture(w, h, paint(0)), pixelTexture(w, h, paint(1))]; // ordem [f0, f1] — game/life alterna por índice
|
|
220
|
+
const lifeTex = {};
|
|
221
|
+
for (const k of Object.keys(CREATURE_ART)) {
|
|
222
|
+
const a = CREATURE_ART[k];
|
|
223
|
+
lifeTex[k] = pair(a.w, a.h, a.paint);
|
|
224
|
+
}
|
|
225
|
+
const adultTex = ADULT_SHAPES.map((v) => pair(ADULT_W, ADULT_H, v));
|
|
226
|
+
const carTex = CAR_PALETTES.map(([body, dark, top]) => pixelTexture(CAR_W, CAR_H, paintCar(body, dark, top)));
|
|
227
|
+
return { lifeTex, adultTex, carTex };
|
|
228
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
import { type PixelPainter } from './canvas.js';
|
|
3
|
+
/** O TILE do miolo da parede: alvenaria em fiada alternada. 51 retângulos, 9 cores, fiel ao pixel. */
|
|
4
|
+
export declare const paintTileFill: PixelPainter;
|
|
5
|
+
/** O TILE do TOPO da plataforma: mesma alvenaria com a borda superior iluminada. 62 retângulos, 23 cores. */
|
|
6
|
+
export declare const paintTileSurface: PixelPainter;
|
|
7
|
+
/** Os dois tiles como canvas 16×16 — a forma que `worldCanvas` consumia das imagens carregadas. */
|
|
8
|
+
export declare function cityTiles(): {
|
|
9
|
+
fill: HTMLCanvasElement;
|
|
10
|
+
surface: HTMLCanvasElement;
|
|
11
|
+
};
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// render/city-tiles.ts — OS DOIS TILES DA CIDADE, como DADOS e não como PNG (pilar "arte = dados", ADR-0010).
|
|
3
|
+
//
|
|
4
|
+
// `tile_fill` e `tile_surface` eram dois PNG de 16×16 baixados por `carregarTilesDoTema`. Aqui eles voltam como
|
|
5
|
+
// `PixelPainter` — a mesma forma que `render/city-tex` já usa para os pombos, os gatos e as silhuetas: uma
|
|
6
|
+
// lista de retângulos `px(x, y, w, h, cor)`.
|
|
7
|
+
//
|
|
8
|
+
// POR QUE RETÂNGULOS E NÃO UMA REGRA DE ALVENARIA. A primeira tentativa foi inferir a regra (tijolo 7×6, junta
|
|
9
|
+
// de 1 px, fiada alternada, bisel) e gerá-la. Deu 66% de acerto exato: a arte tem variação FEITA À MÃO — o
|
|
10
|
+
// bisel muda de tijolo para tijolo, e nenhuma regra curta a alcança. A decomposição em retângulos custa 51 e
|
|
11
|
+
// 62 chamadas e acerta 100%. Entre uma regra elegante que erra um quinto dos pixels e uma lista que acerta
|
|
12
|
+
// todos, num tile de 16×16 desenhado à mão, a lista é a resposta honesta.
|
|
13
|
+
//
|
|
14
|
+
// O QUE ISSO COMPRA, além de tirar dois PNG do pacote: o desenho passa a ser recolorível (o alto contraste e
|
|
15
|
+
// as paletas seguras para daltonismo mexem em cores, não em bitmaps), some o carregamento assíncrono — e com
|
|
16
|
+
// ele a guarda de corrida de `set-cenario` e os 404 de boot dos temas que não têm arte própria.
|
|
17
|
+
import { pixelCanvas } from './canvas.js';
|
|
18
|
+
/** O TILE do miolo da parede: alvenaria em fiada alternada. 51 retângulos, 9 cores, fiel ao pixel. */
|
|
19
|
+
export const paintTileFill = (px) => {
|
|
20
|
+
px(0, 0, 1, 1, '#403b48');
|
|
21
|
+
px(1, 0, 3, 1, '#b8b6c1');
|
|
22
|
+
px(4, 0, 2, 1, '#bcbdc9');
|
|
23
|
+
px(6, 0, 2, 1, '#828291');
|
|
24
|
+
px(8, 0, 1, 1, '#403b48');
|
|
25
|
+
px(9, 0, 1, 1, '#b1b0bc');
|
|
26
|
+
px(10, 0, 4, 1, '#b8b6c1');
|
|
27
|
+
px(14, 0, 2, 1, '#403b48');
|
|
28
|
+
px(0, 1, 1, 1, '#b1b0bc');
|
|
29
|
+
px(1, 1, 6, 5, '#828291');
|
|
30
|
+
px(7, 1, 1, 4, '#403b48');
|
|
31
|
+
px(8, 1, 1, 5, '#b8b6c1');
|
|
32
|
+
px(9, 1, 6, 5, '#828291');
|
|
33
|
+
px(15, 1, 1, 5, '#393543');
|
|
34
|
+
px(0, 2, 1, 3, '#b8b6c1');
|
|
35
|
+
px(0, 5, 1, 1, '#b1b0bc');
|
|
36
|
+
px(7, 5, 1, 1, '#3e2e3f');
|
|
37
|
+
px(0, 6, 1, 3, '#403b48');
|
|
38
|
+
px(1, 6, 1, 1, '#777485');
|
|
39
|
+
px(2, 6, 4, 1, '#828291');
|
|
40
|
+
px(6, 6, 3, 3, '#403b48');
|
|
41
|
+
px(9, 6, 5, 1, '#828291');
|
|
42
|
+
px(14, 6, 2, 2, '#403b48');
|
|
43
|
+
px(1, 7, 5, 1, '#403b48');
|
|
44
|
+
px(9, 7, 5, 1, '#403b48');
|
|
45
|
+
px(1, 8, 1, 1, '#b1b0bc');
|
|
46
|
+
px(2, 8, 2, 1, '#b8b6c1');
|
|
47
|
+
px(4, 8, 1, 1, '#bcbdc9');
|
|
48
|
+
px(5, 8, 1, 1, '#b1b0bc');
|
|
49
|
+
px(9, 8, 1, 1, '#b1b0bc');
|
|
50
|
+
px(10, 8, 2, 1, '#b8b6c1');
|
|
51
|
+
px(12, 8, 1, 1, '#bcbdc9');
|
|
52
|
+
px(13, 8, 1, 1, '#b1b0bc');
|
|
53
|
+
px(14, 8, 1, 1, '#403b48');
|
|
54
|
+
px(15, 8, 1, 1, '#393543');
|
|
55
|
+
px(0, 9, 1, 5, '#b1b0bc');
|
|
56
|
+
px(1, 9, 6, 5, '#828291');
|
|
57
|
+
px(7, 9, 1, 3, '#403b48');
|
|
58
|
+
px(8, 9, 1, 5, '#b8b6c1');
|
|
59
|
+
px(9, 9, 6, 5, '#828291');
|
|
60
|
+
px(15, 9, 1, 2, '#403b48');
|
|
61
|
+
px(15, 11, 1, 3, '#393543');
|
|
62
|
+
px(7, 12, 1, 2, '#393543');
|
|
63
|
+
px(0, 14, 1, 2, '#403b48');
|
|
64
|
+
px(1, 14, 5, 1, '#828291');
|
|
65
|
+
px(6, 14, 3, 2, '#403b48');
|
|
66
|
+
px(9, 14, 5, 1, '#828291');
|
|
67
|
+
px(14, 14, 2, 2, '#403b48');
|
|
68
|
+
px(1, 15, 1, 1, '#444653');
|
|
69
|
+
px(2, 15, 4, 1, '#403b48');
|
|
70
|
+
px(9, 15, 5, 1, '#403b48');
|
|
71
|
+
};
|
|
72
|
+
/** O TILE do TOPO da plataforma: mesma alvenaria com a borda superior iluminada. 62 retângulos, 23 cores. */
|
|
73
|
+
export const paintTileSurface = (px) => {
|
|
74
|
+
px(0, 0, 1, 1, '#8b8994');
|
|
75
|
+
px(1, 0, 3, 1, '#bbbac5');
|
|
76
|
+
px(4, 0, 2, 1, '#bdbdc8');
|
|
77
|
+
px(6, 0, 2, 1, '#a6a5b2');
|
|
78
|
+
px(8, 0, 1, 1, '#8b8994');
|
|
79
|
+
px(9, 0, 1, 1, '#b8b7c3');
|
|
80
|
+
px(10, 0, 4, 1, '#bbbac5');
|
|
81
|
+
px(14, 0, 2, 1, '#8b8994');
|
|
82
|
+
px(0, 1, 1, 1, '#b6b5c1');
|
|
83
|
+
px(1, 1, 6, 1, '#9b9aa8');
|
|
84
|
+
px(7, 1, 1, 1, '#74717d');
|
|
85
|
+
px(8, 1, 1, 1, '#bab8c3');
|
|
86
|
+
px(9, 1, 6, 1, '#9b9aa8');
|
|
87
|
+
px(15, 1, 1, 1, '#706e7a');
|
|
88
|
+
px(0, 2, 1, 1, '#b9b7c2');
|
|
89
|
+
px(1, 2, 6, 1, '#90909e');
|
|
90
|
+
px(7, 2, 1, 1, '#5e5a66');
|
|
91
|
+
px(8, 2, 1, 1, '#b9b7c2');
|
|
92
|
+
px(9, 2, 6, 1, '#90909e');
|
|
93
|
+
px(15, 2, 1, 1, '#585562');
|
|
94
|
+
px(0, 3, 1, 2, '#b8b6c1');
|
|
95
|
+
px(1, 3, 6, 3, '#828291');
|
|
96
|
+
px(7, 3, 1, 2, '#403b48');
|
|
97
|
+
px(8, 3, 1, 3, '#b8b6c1');
|
|
98
|
+
px(9, 3, 6, 3, '#828291');
|
|
99
|
+
px(15, 3, 1, 3, '#393543');
|
|
100
|
+
px(0, 5, 1, 1, '#b1b0bc');
|
|
101
|
+
px(7, 5, 1, 1, '#3e2e3f');
|
|
102
|
+
px(0, 6, 1, 3, '#403b48');
|
|
103
|
+
px(1, 6, 1, 1, '#777485');
|
|
104
|
+
px(2, 6, 4, 1, '#828291');
|
|
105
|
+
px(6, 6, 3, 3, '#403b48');
|
|
106
|
+
px(9, 6, 5, 1, '#828291');
|
|
107
|
+
px(14, 6, 2, 2, '#403b48');
|
|
108
|
+
px(1, 7, 5, 1, '#403b48');
|
|
109
|
+
px(9, 7, 5, 1, '#403b48');
|
|
110
|
+
px(1, 8, 1, 1, '#b1b0bc');
|
|
111
|
+
px(2, 8, 2, 1, '#b8b6c1');
|
|
112
|
+
px(4, 8, 1, 1, '#bcbdc9');
|
|
113
|
+
px(5, 8, 1, 1, '#b1b0bc');
|
|
114
|
+
px(9, 8, 1, 1, '#b1b0bc');
|
|
115
|
+
px(10, 8, 2, 1, '#b8b6c1');
|
|
116
|
+
px(12, 8, 1, 1, '#bcbdc9');
|
|
117
|
+
px(13, 8, 1, 1, '#b1b0bc');
|
|
118
|
+
px(14, 8, 1, 1, '#403b48');
|
|
119
|
+
px(15, 8, 1, 1, '#393543');
|
|
120
|
+
px(0, 9, 1, 5, '#b1b0bc');
|
|
121
|
+
px(1, 9, 6, 5, '#828291');
|
|
122
|
+
px(7, 9, 1, 3, '#403b48');
|
|
123
|
+
px(8, 9, 1, 5, '#b8b6c1');
|
|
124
|
+
px(9, 9, 6, 5, '#828291');
|
|
125
|
+
px(15, 9, 1, 2, '#403b48');
|
|
126
|
+
px(15, 11, 1, 3, '#393543');
|
|
127
|
+
px(7, 12, 1, 2, '#393543');
|
|
128
|
+
px(0, 14, 1, 2, '#403b48');
|
|
129
|
+
px(1, 14, 5, 1, '#828291');
|
|
130
|
+
px(6, 14, 3, 2, '#403b48');
|
|
131
|
+
px(9, 14, 5, 1, '#828291');
|
|
132
|
+
px(14, 14, 2, 2, '#403b48');
|
|
133
|
+
px(1, 15, 1, 1, '#444653');
|
|
134
|
+
px(2, 15, 4, 1, '#403b48');
|
|
135
|
+
px(9, 15, 5, 1, '#403b48');
|
|
136
|
+
};
|
|
137
|
+
/** Os dois tiles como canvas 16×16 — a forma que `worldCanvas` consumia das imagens carregadas. */
|
|
138
|
+
export function cityTiles() {
|
|
139
|
+
return { fill: pixelCanvas(16, 16, paintTileFill), surface: pixelCanvas(16, 16, paintTileSurface) };
|
|
140
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
type CrtCfg = {
|
|
3
|
+
scan: number;
|
|
4
|
+
vig: number;
|
|
5
|
+
round: number;
|
|
6
|
+
};
|
|
7
|
+
/** O CRT de fábrica. Ganhou nome porque o "restaurar padrões" do menu (ADR-0028) precisa do MESMO valor que
|
|
8
|
+
* a carga do boot usa quando nada foi salvo — duas cópias seriam duas chances de o reset devolver um CRT que
|
|
9
|
+
* o jogo nunca mostrou. Congelado: um padrão que alguém consiga escrever em tempo de execução não é padrão. */
|
|
10
|
+
export declare const CRT_DEFAULT: Readonly<CrtCfg>;
|
|
11
|
+
/** Liga a contagem de jogadores e a pergunta de a11y. Chamado uma vez pela raiz, antes do 1º `applyCrt()`. */
|
|
12
|
+
export declare function initCrt(deps: {
|
|
13
|
+
numJogadores: () => number;
|
|
14
|
+
a11yVisualAtiva: () => boolean;
|
|
15
|
+
}): void;
|
|
16
|
+
export declare const CRT: CrtCfg;
|
|
17
|
+
export declare function crtScanVars(): void;
|
|
18
|
+
export declare function applyCrt(): void;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// render/crt.ts — estética CRT (menu Sensibilidade visual): scanlines/vinheta/cantos, só CSS (classes em
|
|
3
|
+
// #game-region). Extraído do game.js (Estágio 4, Tier 1). CRT = config {scan,vig,round} (0=off,1,2; scan/vig são
|
|
4
|
+
// on/off) carregada do localStorage com migração do formato antigo booleano. crtScanVars ancora a scanline em
|
|
5
|
+
// PIXELS REAIS (recomputa da altura real do #game-region + dpr → 1 linha por pixel de arte, espaçamento regular).
|
|
6
|
+
// Auto-contido: depende de ui/dom ($). A contagem de jogadores entra por `initCrt` (ver abaixo).
|
|
7
|
+
import { $ } from '../ui/dom.js';
|
|
8
|
+
import { screenGrid } from '../core/screens.js';
|
|
9
|
+
import * as store from '../platform/storage.js';
|
|
10
|
+
// scanline LIGADA por padrão (decisão do José 2026-07-03). Migra incl_crt (booleano) → incl_crt2 (níveis 0..2).
|
|
11
|
+
/** O CRT de fábrica. Ganhou nome porque o "restaurar padrões" do menu (ADR-0028) precisa do MESMO valor que
|
|
12
|
+
* a carga do boot usa quando nada foi salvo — duas cópias seriam duas chances de o reset devolver um CRT que
|
|
13
|
+
* o jogo nunca mostrou. Congelado: um padrão que alguém consiga escrever em tempo de execução não é padrão. */
|
|
14
|
+
export const CRT_DEFAULT = Object.freeze({ scan: 1, vig: 0, round: 1 });
|
|
15
|
+
// A CONTAGEM DE JOGADORES entra por injeção desde 2026-08-26. Era `numPlayers`, um `let` de `core/state`
|
|
16
|
+
// importado como binding vivo — e um `let` de módulo é compartilhado por qualquer segundo jogo que a
|
|
17
|
+
// mesma página carregue (D13 do `demos`, ADR-0038). O que entra aqui é o GETTER da rodada que a raiz
|
|
18
|
+
// possui; o `let` que sobra guarda a função, não o número.
|
|
19
|
+
let _numJogadores = () => 1;
|
|
20
|
+
/**
|
|
21
|
+
* ALGUM jogador está num modo de acessibilidade visual? (qualquer coisa que não seja `normal`.)
|
|
22
|
+
*
|
|
23
|
+
* O ADR-0020 decide: "modos de a11y SUPRIMEM o CRT/efeitos decorativos — precedência a11y > estética". Isso
|
|
24
|
+
* nunca tinha sido implementado, e a emenda de 2026-08-26 mediu: com a vinheta ligada, `crt-vig-1` sobrevivia
|
|
25
|
+
* em `hc-direto`, `fix-deuter`, `lv-blur` e `blind`. Uma vinheta escurecendo as bordas trabalha contra o modo
|
|
26
|
+
* que existe para AUMENTAR contraste.
|
|
27
|
+
*
|
|
28
|
+
* `ALGUM` e não "o jogador 1": o CRT é decoração GLOBAL, uma só para a tela inteira. Não há como escurecer as
|
|
29
|
+
* bordas de meia tela. Se a decoração e a acessibilidade de qualquer criança se contradizem, quem cede é a
|
|
30
|
+
* decoração — que é literalmente o que "precedência a11y > estética" quer dizer.
|
|
31
|
+
*/
|
|
32
|
+
let _a11yVisualAtiva = () => false;
|
|
33
|
+
/** Liga a contagem de jogadores e a pergunta de a11y. Chamado uma vez pela raiz, antes do 1º `applyCrt()`. */
|
|
34
|
+
export function initCrt(deps) {
|
|
35
|
+
_numJogadores = deps.numJogadores;
|
|
36
|
+
_a11yVisualAtiva = deps.a11yVisualAtiva;
|
|
37
|
+
}
|
|
38
|
+
export const CRT = (() => {
|
|
39
|
+
const d = { ...CRT_DEFAULT };
|
|
40
|
+
try {
|
|
41
|
+
const s = JSON.parse(store.get(store.KEYS.crt, null) || store.get(store.KEYS.crtLegacy, null) || 'null');
|
|
42
|
+
const fresh = !store.get(store.KEYS.crt, null); // migração p/ crt2: herda vig/round; scan volta ao padrão ON uma vez
|
|
43
|
+
if (s && typeof s === 'object')
|
|
44
|
+
for (const k in d)
|
|
45
|
+
if (k in s) {
|
|
46
|
+
const v = s[k];
|
|
47
|
+
if (fresh && k === 'scan')
|
|
48
|
+
continue;
|
|
49
|
+
d[k] = v === true ? (k === 'round' ? 2 : 1) : v === false ? (k === 'round' ? 1 : 0) : Math.max(0, Math.min(2, v | 0));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch (e) { /* noop: file:// / modo privado */ }
|
|
53
|
+
d.scan = d.scan ? 1 : 0;
|
|
54
|
+
d.vig = d.vig ? 1 : 0; // scanlines/vinheta são ON/OFF (só cantos têm 3 níveis)
|
|
55
|
+
return d;
|
|
56
|
+
})();
|
|
57
|
+
// Ancora a scanline em px REAIS: 1 linha por pixel de ARTE (kDev inteiro) → espaçamento SEMPRE regular em qualquer dpr.
|
|
58
|
+
export function crtScanVars() {
|
|
59
|
+
const g = $('#game-region');
|
|
60
|
+
if (!g || !CRT.scan)
|
|
61
|
+
return;
|
|
62
|
+
const { rows } = screenGrid(_numJogadores()), dpr = window.devicePixelRatio || 1;
|
|
63
|
+
const perDev = Math.max(2, Math.round((g.clientHeight || 360) * dpr / (180 * rows))); // kDev = px REAIS por linha de arte (INTEIRO)
|
|
64
|
+
g.style.setProperty('--scan-per', (perDev / dpr) + 'px'); // período = kDev px reais (1 linha de arte)
|
|
65
|
+
g.style.setProperty('--scan-line', (Math.max(1, Math.round(dpr)) / dpr) + 'px'); // linha = 1 px REAL
|
|
66
|
+
}
|
|
67
|
+
// Aplica as classes CSS de CRT ao #game-region e persiste. Chamado no boot e ao mexer no menu.
|
|
68
|
+
export function applyCrt() {
|
|
69
|
+
const g = $('#game-region');
|
|
70
|
+
if (!g)
|
|
71
|
+
return;
|
|
72
|
+
['crt-scan-1', 'crt-vig-1', 'crt-round-0', 'crt-round-2'].forEach((c) => g.classList.remove(c));
|
|
73
|
+
// OS DOIS EFEITOS CEDEM À ACESSIBILIDADE, SEM EXCEÇÃO (ADR-0020 + ADR-0047).
|
|
74
|
+
//
|
|
75
|
+
// Houve uma versão com uma chave de escape por efeito, a pedido do Dev. Ele a removeu depois de VER o
|
|
76
|
+
// resultado na tela: "Ceder fez muito bem ao jogo nos modos de acessibilidade". Fica anotado porque a
|
|
77
|
+
// ausência da chave é decisão, não esquecimento — e porque o pilar 2 volta a não ter exceção nenhuma.
|
|
78
|
+
//
|
|
79
|
+
// O valor gravado NÃO é alterado: a preferência continua lá e volta a valer sozinha ao sair do modo de
|
|
80
|
+
// acessibilidade. Suprimir não é desligar — é a distinção que impede a criança de perder o que escolheu
|
|
81
|
+
// toda vez que liga o alto contraste.
|
|
82
|
+
const a11y = _a11yVisualAtiva();
|
|
83
|
+
if (CRT.scan && !a11y) {
|
|
84
|
+
g.classList.add('crt-scan-' + CRT.scan);
|
|
85
|
+
crtScanVars();
|
|
86
|
+
}
|
|
87
|
+
if (CRT.vig && !a11y)
|
|
88
|
+
g.classList.add('crt-vig-' + CRT.vig);
|
|
89
|
+
if (CRT.round !== 1)
|
|
90
|
+
g.classList.add('crt-round-' + CRT.round); // 1 = visual padrão (8px), sem classe
|
|
91
|
+
store.setJSON(store.KEYS.crt, CRT);
|
|
92
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
/** As seis chaves de modo de daltonismo — as MESMAS chaves de `VIZ_MODES` em render/viz-modes. */
|
|
3
|
+
export type CvdKey = 'sim-protan' | 'sim-deuter' | 'sim-tritan' | 'fix-protan' | 'fix-deuter' | 'fix-tritan';
|
|
4
|
+
/** Ordem canônica: as três simulações (o que a pessoa vê) antes das três correções (o que a ajuda a ver). */
|
|
5
|
+
export declare const CVD_KEYS: readonly CvdKey[];
|
|
6
|
+
/**
|
|
7
|
+
* As matrizes, 4×5 em ordem de linha. Linha A = `0 0 0 1 0` em todas: nenhum dos seis modos mexe em alfa.
|
|
8
|
+
*
|
|
9
|
+
* SIMULAÇÃO (Machado, Oliveira & Fernandes 2009, severidade 1.0 — valores conferidos na página dos autores,
|
|
10
|
+
* UFRGS). CORREÇÃO (daltonização canônica): a linha R é identidade — não adianta modular o canal que a pessoa
|
|
11
|
+
* não distingue — e o erro é reinjetado em G e B, onde há discriminação; a soma de cada linha é 1, então
|
|
12
|
+
* branco e cinzas ficam preservados.
|
|
13
|
+
*/
|
|
14
|
+
export declare const CVD_MATRIX: Record<CvdKey, readonly number[]>;
|
|
15
|
+
/**
|
|
16
|
+
* Modo → id do `<filter>` SVG. Os ids são API PÚBLICA deste módulo: `VIZ_FILTER` (render/viz-modes) pede o
|
|
17
|
+
* filtro pelo `url(#…)`, e é este mapa que promete que o filtro com aquele id vai existir no documento.
|
|
18
|
+
* Renomear um id aqui sem renomear lá apaga a canvas (referência de filtro inexistente não renderiza).
|
|
19
|
+
*/
|
|
20
|
+
export declare const CVD_SVG_ID: Record<CvdKey, string>;
|
|
21
|
+
/** Atributo `values` de um `<feColorMatrix>`: as 4 linhas separadas por espaço duplo (legibilidade). PURA. */
|
|
22
|
+
export declare function cvdMatrixValues(k: CvdKey): string;
|
|
23
|
+
/**
|
|
24
|
+
* Gera os seis `<filter>` DENTRO do `<defs id="cvd-defs">` do index.html, a partir de `CVD_MATRIX`.
|
|
25
|
+
*
|
|
26
|
+
* Por que no boot e não escrito à mão no HTML: era exatamente a segunda cópia dos números. Construído com
|
|
27
|
+
* `createElementNS` (e não `innerHTML`) porque `<filter>`/`<feColorMatrix>` só funcionam no namespace SVG —
|
|
28
|
+
* com innerHTML o resultado depende do algoritmo de fragmento do navegador.
|
|
29
|
+
*
|
|
30
|
+
* Idempotente: esvazia o host antes de preencher, então chamar duas vezes não duplica ids (id duplicado faria
|
|
31
|
+
* o navegador escolher o primeiro — falha silenciosa outra vez).
|
|
32
|
+
*
|
|
33
|
+
* @returns quantos filtros foram instalados (0 = host ausente; o chamador decide se isso é fatal).
|
|
34
|
+
*/
|
|
35
|
+
export declare function installCvdFilters(host: Element | null | undefined): number;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// render/cvd-matrices — as SEIS matrizes de daltonismo (3 SIMULAÇÕES Machado 2009 sev. 1.0 + 3 CORREÇÕES
|
|
3
|
+
// C = I + M_err·(I − Sim), M_err de Fidaner et al.), num só lugar. Módulo-folha: ZERO dependências.
|
|
4
|
+
//
|
|
5
|
+
// A DUPLICAÇÃO QUE ESTE MÓDULO CURA (a sétima do refactor). Os mesmos 120 números existiam escritos DUAS
|
|
6
|
+
// vezes, em linguagens diferentes:
|
|
7
|
+
// · `<feColorMatrix values="…">` em app/index.html — caminho de TELA ÚNICA (`filter: url(#cvd-…)` na
|
|
8
|
+
// <canvas>); render/viz-modes só guarda o `url(#cvd-deuter)`, nunca os números.
|
|
9
|
+
// · `PIXI.ColorMatrixFilter` dentro de `pixiFilterFor` no game.js — caminho MULTI-TELA (filtro GPU por
|
|
10
|
+
// viewport, um modo por jogador).
|
|
11
|
+
// Batiam por sorte: nada ligava as duas cópias. Se divergissem, o sintoma seria SILENCIOSO e de
|
|
12
|
+
// ACESSIBILIDADE — a MESMA pessoa daltônica veria cores diferentes em tela única e em multi-tela, sem erro,
|
|
13
|
+
// sem log, sem teste vermelho. Agora os dois caminhos leem daqui: `render/viewports` monta o
|
|
14
|
+
// ColorMatrixFilter com estes arrays, e `installCvdFilters()` GERA os seis `<filter>` do index.html no boot
|
|
15
|
+
// a partir dos mesmos arrays (o HTML só guarda o `<defs id="cvd-defs">` vazio que os recebe).
|
|
16
|
+
//
|
|
17
|
+
// Este é um módulo-folha de propósito (mesmo formato de render/hc-role-data): um arquivo de dados sem
|
|
18
|
+
// dependência nenhuma pode ser lido tanto pelo pipeline de render quanto pelo boot do documento sem arrastar
|
|
19
|
+
// PixiJS para dentro do HTML nem o DOM para dentro do render.
|
|
20
|
+
//
|
|
21
|
+
// LAYOUT: 20 números = 4 linhas de 5 (R, G, B, A), em ordem de linha. É EXATAMENTE o mesmo layout nos dois
|
|
22
|
+
// destinos — `feColorMatrix type="matrix"` e `PIXI.ColorMatrixFilter#matrix` — por isso o array serve aos
|
|
23
|
+
// dois sem conversão. Aplicados em sRGB nos dois caminhos (aproximação padrão da web; ver a decisão 2 da
|
|
24
|
+
// pesquisa). Fonte primária e a conferência valor a valor: docs/research/PESQUISA-DALTONIZACAO.md.
|
|
25
|
+
/** Ordem canônica: as três simulações (o que a pessoa vê) antes das três correções (o que a ajuda a ver). */
|
|
26
|
+
export const CVD_KEYS = ['sim-protan', 'sim-deuter', 'sim-tritan', 'fix-protan', 'fix-deuter', 'fix-tritan'];
|
|
27
|
+
/**
|
|
28
|
+
* As matrizes, 4×5 em ordem de linha. Linha A = `0 0 0 1 0` em todas: nenhum dos seis modos mexe em alfa.
|
|
29
|
+
*
|
|
30
|
+
* SIMULAÇÃO (Machado, Oliveira & Fernandes 2009, severidade 1.0 — valores conferidos na página dos autores,
|
|
31
|
+
* UFRGS). CORREÇÃO (daltonização canônica): a linha R é identidade — não adianta modular o canal que a pessoa
|
|
32
|
+
* não distingue — e o erro é reinjetado em G e B, onde há discriminação; a soma de cada linha é 1, então
|
|
33
|
+
* branco e cinzas ficam preservados.
|
|
34
|
+
*/
|
|
35
|
+
export const CVD_MATRIX = {
|
|
36
|
+
'sim-protan': [0.152286, 1.052583, -0.204868, 0, 0, 0.114503, 0.786281, 0.099216, 0, 0, -0.003882, -0.048116, 1.051998, 0, 0, 0, 0, 0, 1, 0],
|
|
37
|
+
'sim-deuter': [0.367322, 0.860646, -0.227968, 0, 0, 0.280085, 0.672501, 0.047413, 0, 0, -0.011820, 0.042940, 0.968881, 0, 0, 0, 0, 0, 1, 0],
|
|
38
|
+
'sim-tritan': [1.255528, -0.076749, -0.178779, 0, 0, -0.078411, 0.930809, 0.147602, 0, 0, 0.004733, 0.691367, 0.303900, 0, 0, 0, 0, 0, 1, 0],
|
|
39
|
+
'fix-protan': [1, 0, 0, 0, 0, 0.478897, 0.476911, 0.044192, 0, 0, 0.597282, -0.688692, 1.091410, 0, 0, 0, 0, 0, 1, 0],
|
|
40
|
+
'fix-deuter': [1, 0, 0, 0, 0, 0.162790, 0.725047, 0.112165, 0, 0, 0.454695, -0.645392, 1.190697, 0, 0, 0, 0, 0, 1, 0],
|
|
41
|
+
'fix-tritan': [1, 0, 0, 0, 0, -0.100459, 1.122915, -0.022457, 0, 0, -0.183603, -0.637643, 1.821245, 0, 0, 0, 0, 0, 1, 0],
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Modo → id do `<filter>` SVG. Os ids são API PÚBLICA deste módulo: `VIZ_FILTER` (render/viz-modes) pede o
|
|
45
|
+
* filtro pelo `url(#…)`, e é este mapa que promete que o filtro com aquele id vai existir no documento.
|
|
46
|
+
* Renomear um id aqui sem renomear lá apaga a canvas (referência de filtro inexistente não renderiza).
|
|
47
|
+
*/
|
|
48
|
+
export const CVD_SVG_ID = {
|
|
49
|
+
'sim-protan': 'cvd-protan', 'sim-deuter': 'cvd-deuter', 'sim-tritan': 'cvd-tritan',
|
|
50
|
+
'fix-protan': 'cvd-fix-protan', 'fix-deuter': 'cvd-fix-deuter', 'fix-tritan': 'cvd-fix-tritan',
|
|
51
|
+
};
|
|
52
|
+
/** Atributo `values` de um `<feColorMatrix>`: as 4 linhas separadas por espaço duplo (legibilidade). PURA. */
|
|
53
|
+
export function cvdMatrixValues(k) {
|
|
54
|
+
const m = CVD_MATRIX[k];
|
|
55
|
+
return [0, 5, 10, 15].map((i) => m.slice(i, i + 5).join(' ')).join(' ');
|
|
56
|
+
}
|
|
57
|
+
const SVG_NS = 'http://www.w3.org/2000/svg';
|
|
58
|
+
/**
|
|
59
|
+
* Gera os seis `<filter>` DENTRO do `<defs id="cvd-defs">` do index.html, a partir de `CVD_MATRIX`.
|
|
60
|
+
*
|
|
61
|
+
* Por que no boot e não escrito à mão no HTML: era exatamente a segunda cópia dos números. Construído com
|
|
62
|
+
* `createElementNS` (e não `innerHTML`) porque `<filter>`/`<feColorMatrix>` só funcionam no namespace SVG —
|
|
63
|
+
* com innerHTML o resultado depende do algoritmo de fragmento do navegador.
|
|
64
|
+
*
|
|
65
|
+
* Idempotente: esvazia o host antes de preencher, então chamar duas vezes não duplica ids (id duplicado faria
|
|
66
|
+
* o navegador escolher o primeiro — falha silenciosa outra vez).
|
|
67
|
+
*
|
|
68
|
+
* @returns quantos filtros foram instalados (0 = host ausente; o chamador decide se isso é fatal).
|
|
69
|
+
*/
|
|
70
|
+
export function installCvdFilters(host) {
|
|
71
|
+
if (!host)
|
|
72
|
+
return 0;
|
|
73
|
+
const doc = host.ownerDocument;
|
|
74
|
+
if (!doc)
|
|
75
|
+
return 0;
|
|
76
|
+
while (host.firstChild)
|
|
77
|
+
host.removeChild(host.firstChild);
|
|
78
|
+
let n = 0;
|
|
79
|
+
for (const k of CVD_KEYS) {
|
|
80
|
+
const f = doc.createElementNS(SVG_NS, 'filter');
|
|
81
|
+
f.setAttribute('id', CVD_SVG_ID[k]);
|
|
82
|
+
f.setAttribute('color-interpolation-filters', 'sRGB'); // idem PIXI: os dois caminhos operam em sRGB
|
|
83
|
+
const fe = doc.createElementNS(SVG_NS, 'feColorMatrix');
|
|
84
|
+
fe.setAttribute('type', 'matrix');
|
|
85
|
+
fe.setAttribute('values', cvdMatrixValues(k));
|
|
86
|
+
f.appendChild(fe);
|
|
87
|
+
host.appendChild(f);
|
|
88
|
+
n++;
|
|
89
|
+
}
|
|
90
|
+
return n;
|
|
91
|
+
}
|