@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,295 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// render/scene-sky — céu de tela: deriva de nuvens (math pura + bug #21) + trilha viva da v3 (nuvens/pássaros do mundo,
|
|
3
|
+
// estrelas/névoa, grama/minhocas/vagalumes/borboletas). Extraído do game.js (#43). As 6 CAMADAS (skyLayer/starsG/
|
|
4
|
+
// skyDecoG/fogG/grassG/themeFxG) continuam sendo CRIADAS no game.js — a ordem-z delas está soldada na montagem do
|
|
5
|
+
// render-graph (parallax/worldSprite/lifeLayer/carLayer) — e são INJETADAS aqui; movemos só a LÓGICA. Fórmulas copiadas
|
|
6
|
+
// verbatim da v3.1.100. Injeção por closure. Ver docs/5-Refactoring/plano-modularizacao-mapa.md (#43).
|
|
7
|
+
/**
|
|
8
|
+
* Posição horizontal de uma nuvem à deriva, com wrap SUB-PIXEL e pelo CORPO INTEIRO. Corrige #21:
|
|
9
|
+
* (a) NÃO arredonda → deriva suave mesmo a <1px/frame; (b) só reentra quando a nuvem inteira saiu.
|
|
10
|
+
* @param phase distância de deriva acumulada (float; cresce → direita, decresce → esquerda)
|
|
11
|
+
* @param enterAt x de (re)entrada — passe `-larguraDaNuvem` p/ reentrar TOTALMENTE fora da tela
|
|
12
|
+
* @param span período do wrap — passe `larguraDaTela + larguraDaNuvem` p/ só reentrar depois de sair inteira
|
|
13
|
+
*/
|
|
14
|
+
export function cloudWrapX(phase, enterAt, span) {
|
|
15
|
+
return ((phase % span) + span) % span + enterAt;
|
|
16
|
+
}
|
|
17
|
+
/** Largura e altura do cúmulo base, antes de `esc`. Quem mede cobertura precisa das duas. */
|
|
18
|
+
export const NUVEM_W = 26, NUVEM_H = 13;
|
|
19
|
+
/** Quantas fileiras a manta fechada tem. É o que a faz cobrir do topo ao horizonte em vez de só o topo. */
|
|
20
|
+
const FILEIRAS = 3;
|
|
21
|
+
/**
|
|
22
|
+
* Onde estão as `n` nuvens de tela neste instante.
|
|
23
|
+
*
|
|
24
|
+
* Os fatores 37, 13, 7 e 5 são PRIMOS COM 3 de propósito: a fileira é `i % 3`, e qualquer variação tirada de
|
|
25
|
+
* `i` com um fator múltiplo de 3 sairia CONSTANTE dentro de uma fileira — a fileira 0, que é a que se vê no
|
|
26
|
+
* tempo bom, teria todas as nuvens do mesmo tamanho, à mesma velocidade e na mesma altura. Já aconteceu.
|
|
27
|
+
*
|
|
28
|
+
* @param t relógio de efeitos (0 congela a deriva — movimento reduzido)
|
|
29
|
+
* @param vw largura do viewport
|
|
30
|
+
* @param n quantas nuvens no total (a fileira 0, ~n/3, é a que aparece no tempo bom)
|
|
31
|
+
* @param junta 0 = poucas e altas (o pôr do sol aparece) · 1 = as três fileiras cobrindo o céu inteiro
|
|
32
|
+
*/
|
|
33
|
+
export function nuvensDeTela(t, vw, n, junta) {
|
|
34
|
+
const j = Math.max(0, Math.min(1, junta)), nuvens = [];
|
|
35
|
+
for (let i = 0; i < n; i++) {
|
|
36
|
+
const sp = 0.05 + ((i * 7) % 4) * 0.021; // velocidades diferentes: elas se alcançam e se abrem
|
|
37
|
+
const esc = (1.15 + ((i * 37) % 5) * 0.18) * (1 + 0.6 * j); // crescem ao fechar até uma encostar na outra
|
|
38
|
+
const larg = NUVEM_W * esc;
|
|
39
|
+
const x = cloudWrapX(t * sp + (i * (vw + larg)) / Math.max(1, n), -larg, vw + larg);
|
|
40
|
+
const aberto = 4 + ((i * 37) % 26); // altura de repouso: a faixa alta do céu
|
|
41
|
+
const fechado = (i % FILEIRAS) * 27 + ((i * 13) % 7); // altura de manta: a fileira desta nuvem
|
|
42
|
+
// A fileira 0 é permanente; as outras entram entre 0,15 e 0,65 de aglomeração — antes da chuva, portanto.
|
|
43
|
+
const surge = i % FILEIRAS === 0 ? 1 : Math.max(0, Math.min(1, (j - 0.15) / 0.5));
|
|
44
|
+
nuvens.push({ x, y: (1 - j) * aberto + j * fechado, esc, alpha: (0.72 + 0.28 * j) * surge });
|
|
45
|
+
}
|
|
46
|
+
return nuvens;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Um cúmulo em retângulos: laje de base e três lóbulos de alturas diferentes. Retângulos e não arcos porque é
|
|
50
|
+
* o que o `Gfx` do PIXI oferece aqui e porque é o que combina com o resto — a nuvem é pixel art, não um
|
|
51
|
+
* desenho vetorial reduzido. `[x, y, w, h]` no espaço da nuvem base (NUVEM_W×NUVEM_H), multiplicado por `esc`.
|
|
52
|
+
*/
|
|
53
|
+
const CUMULO = [
|
|
54
|
+
[0, 8, 26, 4], [2, 5, 9, 3], [3, 3, 6, 2], [4, 2, 4, 1],
|
|
55
|
+
[9, 4, 9, 4], [11, 2, 6, 2], [12, 1, 4, 1],
|
|
56
|
+
[17, 5, 7, 3], [18, 3, 4, 2],
|
|
57
|
+
];
|
|
58
|
+
/** A sombra sob a base — uma linha só. Na Floresta ela é alaranjada: a luz baixa bate por baixo. */
|
|
59
|
+
const CUMULO_SOMBRA = [1, 12, 24, 1];
|
|
60
|
+
export function createSceneSky(ctx) {
|
|
61
|
+
const clouds = [];
|
|
62
|
+
let birds = [];
|
|
63
|
+
let _birdT = 500;
|
|
64
|
+
// seedClouds v3: 7 nuvens à deriva no céu do MUNDO (skyLayer)
|
|
65
|
+
for (let i = 0; i < 7; i++) {
|
|
66
|
+
const s = ctx.criarSprite(ctx.CLOUD_TEX[i % 2]);
|
|
67
|
+
s.alpha = 0.5 + ((i * 37) % 30) / 100;
|
|
68
|
+
s.x = (i * 173) % ctx.WORLD_PX_W;
|
|
69
|
+
s.y = 8 + ((i * 61) % Math.floor(ctx.WORLD_PX_H * 0.30));
|
|
70
|
+
s._v = 0.02 + ((i * 13) % 10) / 300;
|
|
71
|
+
ctx.skyLayer.addChild(s);
|
|
72
|
+
clouds.push(s);
|
|
73
|
+
}
|
|
74
|
+
function stepSky(dt) {
|
|
75
|
+
if (ctx.getRm().decor) {
|
|
76
|
+
if (birds.length) {
|
|
77
|
+
birds.forEach((b) => { ctx.skyLayer.removeChild(b.s); b.s.destroy(); });
|
|
78
|
+
birds = [];
|
|
79
|
+
}
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
for (const c of clouds) {
|
|
83
|
+
c.x += (c._v || 0) * dt;
|
|
84
|
+
if (c.x > ctx.WORLD_PX_W + 50)
|
|
85
|
+
c.x = -50;
|
|
86
|
+
}
|
|
87
|
+
if (birds.length < 3 && ++_birdT >= 520) {
|
|
88
|
+
_birdT = ctx.randInt(0, 300);
|
|
89
|
+
const dir = ctx.rnd() < 0.5 ? 1 : -1;
|
|
90
|
+
const s = ctx.criarSprite(ctx.BIRD_TEX[0]);
|
|
91
|
+
s.scale.x = dir;
|
|
92
|
+
s.x = dir > 0 ? -10 : ctx.WORLD_PX_W + 10;
|
|
93
|
+
s.y = 12 + ctx.rnd() * ctx.WORLD_PX_H * 0.25;
|
|
94
|
+
ctx.skyLayer.addChild(s);
|
|
95
|
+
birds.push({ s, dir, f: 0, t: 0 });
|
|
96
|
+
}
|
|
97
|
+
for (let i = birds.length - 1; i >= 0; i--) {
|
|
98
|
+
const b = birds[i];
|
|
99
|
+
b.s.x += b.dir * 0.7 * dt;
|
|
100
|
+
b.t += dt;
|
|
101
|
+
if (b.t >= 8) {
|
|
102
|
+
b.t = 0;
|
|
103
|
+
b.f = 1 - b.f;
|
|
104
|
+
b.s.texture = ctx.BIRD_TEX[b.f];
|
|
105
|
+
}
|
|
106
|
+
if (b.s.x < -16 || b.s.x > ctx.WORLD_PX_W + 16) {
|
|
107
|
+
ctx.skyLayer.removeChild(b.s);
|
|
108
|
+
b.s.destroy();
|
|
109
|
+
birds.splice(i, 1);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function drawV3Cloud(g, x, y, col) {
|
|
114
|
+
g.beginFill(ctx.hexN(col[0])).drawRect(x, y + 6, 24, 6).drawRect(x + 2, y + 4, 8, 4).drawRect(x + 3, y + 2, 6, 2)
|
|
115
|
+
.drawRect(x + 8, y + 2, 12, 6).drawRect(x + 10, y, 8, 2).drawRect(x + 16, y + 4, 8, 4).endFill();
|
|
116
|
+
g.beginFill(ctx.hexN(col[1])).drawRect(x + 1, y + 11, 22, 1).endFill();
|
|
117
|
+
}
|
|
118
|
+
function drawCumulo(g, nv, col) {
|
|
119
|
+
const e = nv.esc, R = (r) => {
|
|
120
|
+
g.drawRect(Math.round(nv.x + r[0] * e), Math.round(nv.y + r[1] * e), Math.round(r[2] * e), Math.round(r[3] * e));
|
|
121
|
+
};
|
|
122
|
+
g.beginFill(ctx.hexN(col[0]), nv.alpha);
|
|
123
|
+
for (const r of CUMULO)
|
|
124
|
+
R(r);
|
|
125
|
+
g.endFill();
|
|
126
|
+
g.beginFill(ctx.hexN(col[1]), nv.alpha);
|
|
127
|
+
R(CUMULO_SOMBRA);
|
|
128
|
+
g.endFill();
|
|
129
|
+
}
|
|
130
|
+
function drawV3Grass(g, tx, ty, fl, t) {
|
|
131
|
+
const TILE = ctx.TILE, x = tx * TILE, y = ty * TILE, wind = ctx.getRm().decor ? 0 : Math.sin(t * 0.045 + tx * 0.6);
|
|
132
|
+
g.beginFill(ctx.hexN(fl.base)).drawRect(x, y, TILE, 2).endFill();
|
|
133
|
+
g.beginFill(ctx.hexN(fl.top)).drawRect(x, y, TILE, 1).endFill();
|
|
134
|
+
const seed = (tx * 1103515245 + 12345) >>> 0;
|
|
135
|
+
for (let i = 0; i < 6; i++) {
|
|
136
|
+
const bx = (seed >> (i * 4)) & 15, bh = 2 + ((seed >> (i * 4 + 4)) & 1);
|
|
137
|
+
g.beginFill(ctx.hexN((i & 1) ? fl.bDk : fl.bLt));
|
|
138
|
+
for (let h = 0; h < bh; h++) {
|
|
139
|
+
const dx = Math.round(wind * (h / Math.max(1, bh - 1)) * 1.5);
|
|
140
|
+
g.drawRect(x + bx + dx, y - 1 - h, 1, 1);
|
|
141
|
+
}
|
|
142
|
+
g.endFill();
|
|
143
|
+
}
|
|
144
|
+
if ((seed >> 9) % 7 === 0) {
|
|
145
|
+
const fx = x + 4 + (seed % 7), sh = 4, cy = y - sh - 2;
|
|
146
|
+
g.beginFill(ctx.hexN(fl.bDk));
|
|
147
|
+
let topDx = 0;
|
|
148
|
+
for (let h = 0; h < sh; h++) {
|
|
149
|
+
topDx = Math.round(wind * (h / sh) * 2.4);
|
|
150
|
+
g.drawRect(fx + topDx, y - 1 - h, 1, 1);
|
|
151
|
+
}
|
|
152
|
+
g.endFill();
|
|
153
|
+
const bxC = fx + topDx;
|
|
154
|
+
g.beginFill(ctx.hexN(fl.petals[(seed >>> 17) % fl.petals.length])).drawRect(bxC - 1, cy + 1, 3, 1).drawRect(bxC, cy, 1, 3).endFill(); // >>> (v3 usava >> e o canvas engolia o índice; PIXI lança)
|
|
155
|
+
g.beginFill(ctx.hexN(fl.center)).drawRect(bxC, cy + 1, 1, 1).endFill();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function stepV3Decor() {
|
|
159
|
+
const T = ctx.CENARIOS[ctx.getCenario()] || {};
|
|
160
|
+
ctx.starsG.clear();
|
|
161
|
+
ctx.skyDecoG.clear();
|
|
162
|
+
ctx.nuvemG.clear();
|
|
163
|
+
ctx.fogG.clear();
|
|
164
|
+
ctx.grassG.clear();
|
|
165
|
+
ctx.themeFxG.clear();
|
|
166
|
+
ctx.themeFxBackG.clear();
|
|
167
|
+
// A pergunta é "este tema tem decoração viva?", e ela se responde OLHANDO A DECORAÇÃO. Antes era
|
|
168
|
+
// `!T.v3`, que respondia "este tema não é a Cidade" — o mesmo resultado por acidente, enquanto a Cidade
|
|
169
|
+
// era o único tema sem céu vivo. Um tema de fundo raster COM borboletas quebrava aquilo; este não.
|
|
170
|
+
if (!T.decor?.length || ctx.DIRECT_CFG[ctx.getVizMode()])
|
|
171
|
+
return; // alto contraste dispensa decor
|
|
172
|
+
const t = ctx.getFxClock(), d = T.decor || [], vw = ctx.LOGICAL_W, vh = ctx.LOGICAL_H, reduzido = !!ctx.getRm().decor;
|
|
173
|
+
// TELA: estrelas (sparkles v3) — cintilam a ~0,3Hz
|
|
174
|
+
if (!reduzido && d.includes('sparkles')) {
|
|
175
|
+
const top = Math.max(1, Math.floor(vh * 0.7));
|
|
176
|
+
for (let i = 0; i < 22; i++) {
|
|
177
|
+
const a = 0.3 + 0.35 * Math.sin(t * 0.03 + i * 1.3);
|
|
178
|
+
if (a <= 0.05)
|
|
179
|
+
continue;
|
|
180
|
+
ctx.starsG.beginFill(0xffffff, a).drawRect((i * 73) % vw, (i * 49) % top, 1, 1).endFill();
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
// TELA: nuvens. Dois caminhos, e a diferença é DADO, não gosto:
|
|
184
|
+
// · tema com `nuvens: N` (hoje só a Floresta) — manta viva de N cúmulos que fecha e abre com a chuva;
|
|
185
|
+
// · os outros três — as 3 lajes da v3, derivas 0.08/0.05/0.11, VERBATIM. Não passaram por aqui de graça:
|
|
186
|
+
// o Dev pediu o céu novo para a Floresta, e mudar a aparência dos outros três de carona seria alargar
|
|
187
|
+
// o pedido por conta própria. Quando ele quiser, é trocar um número na tabela de cenários.
|
|
188
|
+
if (d.includes('nuvens') && T.cloud && T.nuvens) {
|
|
189
|
+
const col = T.cloud, junta = reduzido ? 0 : ctx.getAglomeracao();
|
|
190
|
+
// `nuvemG` e não `skyDecoG`: a manta fica ATRÁS das bandas de morro (ver a nota no ctx).
|
|
191
|
+
for (const nv of nuvensDeTela(reduzido ? 0 : t, vw, T.nuvens, junta))
|
|
192
|
+
drawCumulo(ctx.nuvemG, nv, col);
|
|
193
|
+
}
|
|
194
|
+
else if (d.includes('nuvens') && T.cloud) {
|
|
195
|
+
const col = T.cloud, defs = [{ y: 6, sp: 0.08, off: 0 }, { y: 20, sp: 0.05, off: 130 }, { y: 12, sp: 0.11, off: 250 }];
|
|
196
|
+
for (const c0 of defs) {
|
|
197
|
+
const x = cloudWrapX((reduzido ? 0 : t) * c0.sp + c0.off, -44, vw + 64);
|
|
198
|
+
drawV3Cloud(ctx.skyDecoG, x, c0.y, col);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (!reduzido && d.includes('passaros')) {
|
|
202
|
+
ctx.skyDecoG.beginFill(0x282837, 0.7);
|
|
203
|
+
for (let i = 0; i < 3; i++) {
|
|
204
|
+
const bx = ((t * (0.25 + i * 0.07) + i * 90) % (vw + 20)) - 10, by = 14 + i * 9 + Math.sin(t * 0.04 + i) * 2, f = Math.sin(t * 0.2 + i) > 0 ? 1 : 2;
|
|
205
|
+
const X = Math.round(bx), Y = Math.round(by);
|
|
206
|
+
ctx.skyDecoG.drawRect(X - 2, Y + f, 2, 1).drawRect(X + 1, Y + f, 2, 1).drawRect(X, Y, 1, 1);
|
|
207
|
+
}
|
|
208
|
+
ctx.skyDecoG.endFill();
|
|
209
|
+
}
|
|
210
|
+
// TELA: névoa do amanhecer (3 camadas onduladas) — v3 exato, na FRENTE
|
|
211
|
+
if (d.includes('nevoa')) {
|
|
212
|
+
const base = Math.floor(vh * 0.72);
|
|
213
|
+
const Ls = [{ y: base, drift: 0.30, amp: 3, op: 0.06, freq: 0.080 }, { y: base + 6, drift: 0.55, amp: 4, op: 0.08, freq: 0.060 }, { y: base + 13, drift: 0.85, amp: 5, op: 0.10, freq: 0.050 }];
|
|
214
|
+
for (const L of Ls) {
|
|
215
|
+
ctx.fogG.beginFill(0xdee2ec, L.op);
|
|
216
|
+
for (let x = 0; x < vw; x += 2) {
|
|
217
|
+
const top = L.y + Math.round(L.amp * Math.sin((x + (reduzido ? 0 : t) * L.drift) * L.freq));
|
|
218
|
+
ctx.fogG.drawRect(x, top, 2, vh - top);
|
|
219
|
+
}
|
|
220
|
+
ctx.fogG.endFill();
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
// MUNDO (culled por jogador, dedupe): grama em TODA superfície + minhocas/vagalumes/borboletas
|
|
224
|
+
const fl = ctx.THEME_FLORA[ctx.getCenario()], seen = new Set();
|
|
225
|
+
for (const pl of ctx.getPlayers()) {
|
|
226
|
+
if (pl.quit)
|
|
227
|
+
continue;
|
|
228
|
+
const camX = Math.max(0, Math.min(pl.x - vw / 2, ctx.WORLD_PX_W - vw)), camY = Math.max(0, Math.min((pl.y - ctx.BOX.h / 2) - vh / 2, ctx.WORLD_PX_H - vh));
|
|
229
|
+
// Margem de recorte = 3 tiles em TODAS as direções: cobre a flutuação da fauna (borboleta sobe ~34px ≈ 2 tiles,
|
|
230
|
+
// deriva ~19px) E reduz pop-in do decor na beirada ao mover a câmera → mais fluido (José). (#69)
|
|
231
|
+
const tx0 = Math.max(0, Math.floor(camX / ctx.TILE) - 3), tx1 = Math.min(ctx.WORLD_W - 1, Math.floor((camX + vw) / ctx.TILE) + 3);
|
|
232
|
+
const ty0 = Math.max(0, Math.floor(camY / ctx.TILE) - 3), ty1 = Math.min(ctx.WORLD_H - 1, Math.floor((camY + vh) / ctx.TILE) + 3);
|
|
233
|
+
for (let tx = tx0; tx <= tx1; tx++) {
|
|
234
|
+
for (let ty = ty0; ty <= ty1; ty++) {
|
|
235
|
+
const st = ctx.tileAt(tx, ty); // flora/fauna SÓ em PEDRA(2)/parede(6) — nunca em trampolim(5), lava(9) ou escada(4). #69
|
|
236
|
+
if (!((st === 2 || st === 6) && !ctx.solidAt(tx, ty - 1) && ctx.tileAt(tx, ty - 1) !== 3 && ctx.tileAt(tx, ty - 1) !== 9))
|
|
237
|
+
continue;
|
|
238
|
+
const k = tx + ',' + ty;
|
|
239
|
+
if (seen.has('g' + k))
|
|
240
|
+
continue;
|
|
241
|
+
seen.add('g' + k);
|
|
242
|
+
// densidade: só uma FRAÇÃO das superfícies recebe flora (getGrassDensity), escolhidas por hash+semente da fase.
|
|
243
|
+
if (fl && ((tx * 668265263 ^ ty * 2246822519 ^ ctx.getDecorSeed()) >>> 0) % 1000 < ctx.getGrassDensity() * 1000)
|
|
244
|
+
drawV3Grass(ctx.grassG, tx, ty, fl, t);
|
|
245
|
+
// #69: fauna distribuída POR SUPERFÍCIE (hash de tx,ty), não "1 por coluna no topo" — aparece em todas as
|
|
246
|
+
// alturas, inclusive perto do player em corredores baixos / junto à lava (antes ancorava só na superfície + alta).
|
|
247
|
+
if (d.includes('minhocas') && ((tx * 668265263 ^ ty * 374761393) >>> 0) % 6 === 0 && !seen.has('w' + k)) {
|
|
248
|
+
seen.add('w' + k); // ~1/6 das superfícies
|
|
249
|
+
ctx.themeFxG.beginFill(0xc47b8a);
|
|
250
|
+
const bx = tx * ctx.TILE + 5, by = ty * ctx.TILE + 4;
|
|
251
|
+
for (let s2 = 0; s2 < 5; s2++)
|
|
252
|
+
ctx.themeFxG.drawRect(bx + s2, by + Math.round(reduzido ? 0 : Math.sin(t * 0.15 + tx * 0.7 + s2 * 0.8)), 1, 1);
|
|
253
|
+
ctx.themeFxG.endFill();
|
|
254
|
+
}
|
|
255
|
+
const bh = (tx * 374761393 ^ ty * 668265263) >>> 0; // hash por superfície (cor + lado + gate)
|
|
256
|
+
if (!reduzido && d.includes('borboletas') && bh % 7 === 0 && !seen.has('b' + k)) {
|
|
257
|
+
seen.add('b' + k); // ~1/7 das superfícies
|
|
258
|
+
const cols = [0xff8c42, 0xffd166, 0xef476f, 0xfca5d4, 0xf4a261, 0xe9c46a];
|
|
259
|
+
const groundY = ty * ctx.TILE, wx = tx * ctx.TILE + 8 + 14 * Math.sin(t * 0.015 + tx + ty) + 5 * Math.cos(t * 0.04 + tx);
|
|
260
|
+
const rise = 8 + 26 * (0.5 + 0.5 * Math.sin(t * 0.012 + tx * 1.3 + ty)), wy = groundY - rise + 4 * Math.sin(t * 0.05 + tx);
|
|
261
|
+
const flap = Math.abs(Math.sin(t * 0.4 + tx * 0.7 + ty)), w = 1 + Math.round(flap * 2), X = Math.round(wx), Y = Math.round(wy);
|
|
262
|
+
const bfg = (bh & 1) ? ctx.themeFxG : ctx.themeFxBackG; // metade ATRÁS / metade À FRENTE do player — #69
|
|
263
|
+
bfg.beginFill(cols[bh % cols.length]).drawRect(X - w, Y, w, 2).drawRect(X + 2, Y, w, 2).endFill();
|
|
264
|
+
bfg.beginFill(0x3a2a1a).drawRect(X, Y, 2, 2).endFill();
|
|
265
|
+
}
|
|
266
|
+
// (sem break: grama em TODAS as superfícies expostas da coluna, não só a mais alta — senão some perto do player. #69)
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
if (!reduzido && d.includes('vagalumes')) {
|
|
270
|
+
const cell = 64; // drawFireflies v3: grade hash fixa no mundo, só no ar
|
|
271
|
+
const cx0 = Math.floor(camX / cell) - 1, cx1 = Math.floor((camX + vw) / cell) + 1, cy0 = Math.floor(camY / cell) - 1, cy1 = Math.floor((camY + vh) / cell) + 1;
|
|
272
|
+
for (let cyi = cy0; cyi <= cy1; cyi++)
|
|
273
|
+
for (let cxi = cx0; cxi <= cx1; cxi++) {
|
|
274
|
+
const h = ((cxi * 73856093) ^ (cyi * 19349663)) >>> 0;
|
|
275
|
+
if (h % 3 !== 0)
|
|
276
|
+
continue;
|
|
277
|
+
const k = 'f' + cxi + ',' + cyi;
|
|
278
|
+
if (seen.has(k))
|
|
279
|
+
continue;
|
|
280
|
+
seen.add(k);
|
|
281
|
+
const ph = ((h >> 4) % 628) / 100;
|
|
282
|
+
const wx = cxi * cell + 8 + (h % (cell - 16)) + 8 * Math.sin(t * 0.013 + ph), wy = cyi * cell + 8 + ((h >> 9) % (cell - 16)) + 6 * Math.cos(t * 0.017 + ph * 1.3);
|
|
283
|
+
const a = 0.45 + 0.45 * Math.sin(t * 0.05 + ph);
|
|
284
|
+
if (a <= 0.06)
|
|
285
|
+
continue;
|
|
286
|
+
if (ctx.solidAt(Math.floor(wx / ctx.TILE), Math.floor(wy / ctx.TILE)))
|
|
287
|
+
continue;
|
|
288
|
+
ctx.themeFxBackG.beginFill(0xfff096, a * 0.35).drawRect(wx - 1, wy - 1, 3, 3).endFill(); // vaga-lumes ao FUNDO (atrás do player) — #69
|
|
289
|
+
ctx.themeFxBackG.beginFill(0xffffbe, a).drawRect(wx, wy, 1, 1).endFill();
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return { stepSky, stepV3Decor, getClouds: () => clouds, getBirds: () => birds };
|
|
295
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
import type { CriarSprite, CriarDesenho, ComFiltro, DesenhoComCirculo } from './port.js';
|
|
3
|
+
/** Retângulo em pixels lógicos. */
|
|
4
|
+
export interface Rect {
|
|
5
|
+
x: number;
|
|
6
|
+
y: number;
|
|
7
|
+
w: number;
|
|
8
|
+
h: number;
|
|
9
|
+
}
|
|
10
|
+
/** Onde uma tela fica e o que se desenha em volta dela. */
|
|
11
|
+
export interface ViewportPlacement {
|
|
12
|
+
/** Canto superior esquerdo do sprite da tela, no canvas. */
|
|
13
|
+
x: number;
|
|
14
|
+
y: number;
|
|
15
|
+
/** Retângulo da moldura (meio pixel para dentro, 1px menor nos dois lados — ver o cabeçalho). */
|
|
16
|
+
frame: Rect;
|
|
17
|
+
/** Âncora da bolinha indicadora do modo de visão (canto superior DIREITO da tela, para dentro). */
|
|
18
|
+
dot: {
|
|
19
|
+
x: number;
|
|
20
|
+
y: number;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/** O plano completo de render para `n` jogadores. */
|
|
24
|
+
export interface ScreenPlan {
|
|
25
|
+
/** `true` = caminho de TELA ÚNICA: a câmera vai direto no stage, sem render-texture nenhuma. */
|
|
26
|
+
single: boolean;
|
|
27
|
+
cols: number;
|
|
28
|
+
rows: number;
|
|
29
|
+
/** Tamanho a que o renderer é redimensionado. */
|
|
30
|
+
canvas: {
|
|
31
|
+
w: number;
|
|
32
|
+
h: number;
|
|
33
|
+
};
|
|
34
|
+
/** Uma entrada por tela — VAZIO no caminho de tela única, que não tem viewport. */
|
|
35
|
+
viewports: ViewportPlacement[];
|
|
36
|
+
}
|
|
37
|
+
/** Cor da moldura de tela (o mesmo cinza-azulado da borda do canvas em tela única). */
|
|
38
|
+
export declare const FRAME_COLOR = 13489906;
|
|
39
|
+
/** Opacidade da moldura. */
|
|
40
|
+
export declare const FRAME_ALPHA = 0.95;
|
|
41
|
+
/** Espessura da moldura, em pixels LÓGICOS (escala junto com o canvas). */
|
|
42
|
+
export declare const FRAME_WIDTH = 1;
|
|
43
|
+
/** Recuo da bolinha indicadora a partir da borda direita da tela. */
|
|
44
|
+
export declare const DOT_INSET_X = 9;
|
|
45
|
+
/** Recuo da bolinha indicadora a partir do topo da tela. */
|
|
46
|
+
export declare const DOT_INSET_Y = 9;
|
|
47
|
+
/**
|
|
48
|
+
* A geometria de render para `n` jogadores, sem tocar em nada.
|
|
49
|
+
*
|
|
50
|
+
* `n <= 1` devolve `single: true` e NENHUM viewport: em tela única a câmera é filha do stage e o PIXI desenha
|
|
51
|
+
* direto no canvas — não há render-texture, nem moldura, nem bolinha (o indicador de modo de visão de tela
|
|
52
|
+
* única é outro, no DOM).
|
|
53
|
+
*
|
|
54
|
+
* `w`/`h` são parâmetros com o pixel canônico (320×180, ADR-0010) por padrão — não para o jogo trocá-los, mas
|
|
55
|
+
* para a conta ser conferível com números pequenos no teste.
|
|
56
|
+
*/
|
|
57
|
+
export declare function planScreens(n: number, w?: number, h?: number): ScreenPlan;
|
|
58
|
+
/** O que se toca de um `PIXI.BaseTexture`: só o modo de escala (NEAREST = pixel art sem borrar). */
|
|
59
|
+
interface BaseTextureLike {
|
|
60
|
+
scaleMode: number;
|
|
61
|
+
}
|
|
62
|
+
/** O que se toca de uma `PIXI.RenderTexture`: o baseTexture e o descarte (`true` = destrói a base junto). */
|
|
63
|
+
export interface RenderTextureLike {
|
|
64
|
+
baseTexture: BaseTextureLike;
|
|
65
|
+
destroy(destroyBase?: boolean): void;
|
|
66
|
+
}
|
|
67
|
+
/** `PIXI.RenderTexture` como FÁBRICA — é `RenderTexture.create({width,height})`, não um construtor. */
|
|
68
|
+
export interface RenderTextureFactory {
|
|
69
|
+
create(opts: {
|
|
70
|
+
width: number;
|
|
71
|
+
height: number;
|
|
72
|
+
}): RenderTextureLike;
|
|
73
|
+
}
|
|
74
|
+
/** O mínimo de um objeto de cena posicionável e descartável. */
|
|
75
|
+
export interface DisplayLike {
|
|
76
|
+
x: number;
|
|
77
|
+
y: number;
|
|
78
|
+
visible: boolean;
|
|
79
|
+
destroy(): void;
|
|
80
|
+
}
|
|
81
|
+
/** `PIXI.Sprite` — aqui ele só nasce de uma render-texture e é posicionado.
|
|
82
|
+
* O `filters` NÃO é tocado aqui: quem põe filtro de GPU por tela é `render/viz-setters`, e é para lá que
|
|
83
|
+
* estes sprites vão (`getVpSpr`). A INVERSÃO DA FATIA MÍNIMA de novo — em posição de entrega, a fatia que
|
|
84
|
+
* vale é a de quem RECEBE, não a de quem lê. Ver o cabeçalho de `render/port`. */
|
|
85
|
+
export type SpriteLike = DisplayLike & ComFiltro;
|
|
86
|
+
/** `PIXI.Graphics` — a moldura desenha; as bolinhas só são posicionadas (quem as pinta é viz-setters).
|
|
87
|
+
* E é por serem pintadas LÁ que a declaração inclui o desenho inteiro: as bolinhas saem daqui por
|
|
88
|
+
* `getVpDots` e o `viz-setters` chama `clear`/`beginFill`/`drawCircle` nelas. */
|
|
89
|
+
export interface GraphicsLike extends DisplayLike, DesenhoComCirculo {
|
|
90
|
+
}
|
|
91
|
+
/** `PIXI.Container` no papel de pai de cena. */
|
|
92
|
+
export interface ContainerLike {
|
|
93
|
+
addChild(child: unknown): unknown;
|
|
94
|
+
addChildAt(child: unknown, index: number): unknown;
|
|
95
|
+
removeChild(child: unknown): unknown;
|
|
96
|
+
}
|
|
97
|
+
/** A câmera, vista daqui: só a sua PATERNIDADE importa (filha do stage ou órfã). */
|
|
98
|
+
export interface CameraLike {
|
|
99
|
+
parent: ContainerLike | null;
|
|
100
|
+
}
|
|
101
|
+
/** `app.renderer` — daqui só se redimensiona o canvas. */
|
|
102
|
+
export interface ResizableRenderer {
|
|
103
|
+
resize(w: number, h: number): void;
|
|
104
|
+
}
|
|
105
|
+
export interface ScreenPipelineCtx {
|
|
106
|
+
RenderTexture: RenderTextureFactory;
|
|
107
|
+
/** Fábricas, não construtores (Fase D): `new (texture: unknown)` não recebe o `PIXI.Sprite` real, cujo
|
|
108
|
+
* construtor só aceita `Texture`. Ver `CriarSprite` no cabeçalho de `render/port`. */
|
|
109
|
+
criarSprite: CriarSprite<SpriteLike>;
|
|
110
|
+
criarDesenho: CriarDesenho<GraphicsLike>;
|
|
111
|
+
NEAREST: number;
|
|
112
|
+
stage: ContainerLike;
|
|
113
|
+
renderer: ResizableRenderer;
|
|
114
|
+
camera: CameraLike;
|
|
115
|
+
getNumPlayers: () => number;
|
|
116
|
+
getVpTex: () => RenderTextureLike[];
|
|
117
|
+
setVpTex: (a: RenderTextureLike[]) => void;
|
|
118
|
+
getVpSpr: () => SpriteLike[];
|
|
119
|
+
setVpSpr: (a: SpriteLike[]) => void;
|
|
120
|
+
getVpFrames: () => GraphicsLike | null;
|
|
121
|
+
setVpFrames: (g: GraphicsLike | null) => void;
|
|
122
|
+
getVpDots: () => GraphicsLike[];
|
|
123
|
+
setVpDots: (a: GraphicsLike[]) => void;
|
|
124
|
+
setMinimapVisible: (on: boolean) => void;
|
|
125
|
+
buildGameHud: () => void;
|
|
126
|
+
applyVpFilters: () => void;
|
|
127
|
+
updateVpDots: () => void;
|
|
128
|
+
}
|
|
129
|
+
export interface ScreenPipelineApi {
|
|
130
|
+
/** Remonta o pipeline de render para o nº de jogadores ATUAL. Idempotente: destrói o conjunto anterior. */
|
|
131
|
+
configureRender(): void;
|
|
132
|
+
}
|
|
133
|
+
export declare function initScreenPipeline(ctx: ScreenPipelineCtx): ScreenPipelineApi;
|
|
134
|
+
export {};
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// render/screen-pipeline — QUANTAS TELAS EXISTEM, ONDE ELAS FICAM, E O QUE CADA UMA GANHA DE OBJETO PIXI.
|
|
3
|
+
//
|
|
4
|
+
// Este módulo é o antigo `configureRender()` do main.js. Ele roda toda vez que o número de jogadores muda
|
|
5
|
+
// (`setNumPlayers`) e no reinício da rodada (`restartGame`), e é a única função do jogo que troca a TOPOLOGIA
|
|
6
|
+
// do render: de "uma câmera desenhada direto no stage" para "N render-textures, uma por jogador, cada uma
|
|
7
|
+
// virando um sprite posicionado na grade" — e de volta.
|
|
8
|
+
//
|
|
9
|
+
// ======================= É ENGINE, E NÃO DESTE JOGO =======================
|
|
10
|
+
// "N jogadores em telas SEPARADAS, sem split-screen" é o pilar 7 do ADR-0010: vale para os 35+ jogos da
|
|
11
|
+
// coleção EdSP, não só para *The Inclusionist*. Um segundo jogo reusaria isto INTEIRO — nada aqui sabe o que
|
|
12
|
+
// é uma moeda, um power-up, um portão ou uma área secreta. O que o módulo conhece é: um stage, um renderer,
|
|
13
|
+
// uma câmera, e a conta de `core/screens.ts`. Por isso ele mora em `render/`, ao lado de `render/viewports.ts`,
|
|
14
|
+
// e por isso todo o resto (o HUD, os filtros de a11y, as bolinhas) entra por INJEÇÃO, como callback — quem
|
|
15
|
+
// troca o pipeline não precisa saber o que mais o jogo quer refazer quando o pipeline troca.
|
|
16
|
+
//
|
|
17
|
+
// ======================= POR QUE UM ARQUIVO IRMÃO, E NÃO DENTRO DE render/viewports.ts =======================
|
|
18
|
+
// Os dois falam de "viewport", mas respondem a perguntas diferentes, e a diferença é justamente a fronteira:
|
|
19
|
+
// · `render/viewports.ts` é a FÁBRICA DE IMAGEM — dado um MODO de visão acessível, como o pixel sai
|
|
20
|
+
// (matriz de daltonismo, névoa de catarata, contorno do alto contraste). Ele CONSOME `vpTex` por getter e
|
|
21
|
+
// nunca o cria. É uma pergunta sobre COR e FILTRO.
|
|
22
|
+
// · aqui é a TOPOLOGIA — quantas render-textures existem, de que tamanho é o canvas, onde cada sprite fica,
|
|
23
|
+
// onde a moldura é traçada, onde a bolinha indicadora é ancorada. É uma pergunta sobre GEOMETRIA e CICLO
|
|
24
|
+
// DE VIDA (destruir o conjunto anterior antes de montar o novo).
|
|
25
|
+
// Fundir os dois daria um módulo cujo `ctx` teria ~25 campos e cujo nome só poderia ser "coisas de viewport",
|
|
26
|
+
// que é um tema, não uma responsabilidade — exatamente o argumento do cabeçalho de `input/touch-bindings.ts`.
|
|
27
|
+
// Há ainda um motivo mecânico: `viewports.ts` é quem LÊ `vpTex` (`getVpTex()`); este módulo é quem o
|
|
28
|
+
// REATRIBUI. Deixar leitor e escritor no mesmo arquivo apagaria a única pista de que aquele array troca de
|
|
29
|
+
// identidade a cada mudança de nº de telas — e é essa troca que obriga metade dos getters do projeto.
|
|
30
|
+
// O elo real entre os dois é estreito e explícito: `applyVpFilters()` e `updateVpDots()`, dois callbacks de
|
|
31
|
+
// ré-aplicação chamados no fim do caminho multi-tela.
|
|
32
|
+
//
|
|
33
|
+
// ======================= A PARTE PURA: PLANEJAR ≠ CONSTRUIR =======================
|
|
34
|
+
// O bloco original trançava a conta da grade com as chamadas do PIXI. Aqui a conta saiu inteira para
|
|
35
|
+
// `planScreens(n)`, que devolve, sem tocar em nada: o tamanho do canvas, e para cada tela a sua posição, o
|
|
36
|
+
// retângulo da moldura e a âncora da bolinha. `configureRender()` vira execução de um plano.
|
|
37
|
+
// É `planScreens` que carrega as três regras que ninguém enxergava no meio dos `new PIXI.Sprite`:
|
|
38
|
+
// · a grade vem de `core/screens.ts` (`screenGrid`) — a MESMA fonte do layout, do HUD e do CRT. A cópia que
|
|
39
|
+
// vivia aqui era a quinta, e era a DIVERGENTE (`cols = n<=2 ? n : 2`, sem a guarda de `n<=1`);
|
|
40
|
+
// · com 3 telas, a terceira é CENTRALIZADA na linha de baixo — `x = (LOGICAL_W*cols - LOGICAL_W)/2`;
|
|
41
|
+
// · a moldura é traçada em meio pixel (`+0.5`) e 1px menor nos dois lados, senão a linha de 1px lógico sai
|
|
42
|
+
// borrada ou comida pela borda do canvas quando a escala inteira multiplica tudo.
|
|
43
|
+
//
|
|
44
|
+
// ======================= O QUE FICOU DE FORA =======================
|
|
45
|
+
// · `buildGameHud`, `applyVpFilters`, `updateVpDots` e `setMinimapVisible` são CALLBACKS. O HUD é DOM
|
|
46
|
+
// sobreposto (ui/hud.ts), os filtros são política de a11y (render/viz-setters.ts) e o minimapa é outro
|
|
47
|
+
// subsistema; este módulo só sabe QUANDO eles precisam rodar de novo, não COMO.
|
|
48
|
+
// · quem cria `camera` e solda o z-order dela continua sendo o main.js. Daqui só se move a câmera entre
|
|
49
|
+
// "filha do stage" (tela única) e "órfã, desenhada à mão nas render-textures" (multi-tela).
|
|
50
|
+
// · o LAÇO de render por viewport (quem chama `renderer.render(camera,{renderTexture:vpTex[i]})` a cada
|
|
51
|
+
// quadro) é de `render/draw.ts`. Aqui se monta a infraestrutura; lá ela é usada.
|
|
52
|
+
//
|
|
53
|
+
// ======================= ARMADILHAS DE ORDEM DE BOOT =======================
|
|
54
|
+
// · `applyVpFilters` e `updateVpDots` são `const` desestruturados de `initVizSetters`, ~300 linhas ABAIXO do
|
|
55
|
+
// ponto onde este init cabe. Por isso entram no `ctx` como SETAS PREGUIÇOSAS (`() => applyVpFilters()`):
|
|
56
|
+
// passá-los por valor derrubaria o boot na TDZ. Isso é seguro porque `configureRender` NUNCA é chamado
|
|
57
|
+
// durante o boot — só por `setNumPlayers`/`restartGame` (game/session.ts), que só rodam a partir de um
|
|
58
|
+
// clique ou do início de partida, muito depois do módulo inteiro ter avaliado.
|
|
59
|
+
// · `vpTex`/`vpSpr`/`vpFrames`/`vpDots` são `let` do main.js que ESTE módulo reatribui, e que outros três
|
|
60
|
+
// módulos leem por getter (`viewports`, `viz-setters`, `draw`). Por isso o `ctx` traz o par getter+setter
|
|
61
|
+
// de cada um, em vez de o módulo ser dono dos arrays: se ele fosse dono, o main.js teria de trocar os
|
|
62
|
+
// quatro getters que já distribuiu, e a religação deixaria de ser local.
|
|
63
|
+
// · SEM I/O no import: nada de `document`, nada de canvas no corpo. `planScreens` é aritmética pura e roda no
|
|
64
|
+
// project `node`; tudo que toca PIXI só acontece dentro de `configureRender()`.
|
|
65
|
+
//
|
|
66
|
+
// PIXI entra por INTERFACE ESTRUTURAL, e não por `import * as PIXI` — mesmo precedente de render/viewports,
|
|
67
|
+
// render/scene-sky e game/traffic. Aqui o motivo é mais forte do que o de costume: este é o módulo que mais
|
|
68
|
+
// mexe em tipos concretos do engine (`RenderTexture`, `Sprite`, `Graphics`, `SCALE_MODES`), e é exatamente
|
|
69
|
+
// isso que o obrigaria a arrastar 445KB de PixiJS para dentro do project `node` do Vitest se importasse.
|
|
70
|
+
import { LOGICAL_W, LOGICAL_H } from '../core/constants.js';
|
|
71
|
+
import { screenGrid } from '../core/screens.js';
|
|
72
|
+
/** Cor da moldura de tela (o mesmo cinza-azulado da borda do canvas em tela única). */
|
|
73
|
+
export const FRAME_COLOR = 0xcdd6f2;
|
|
74
|
+
/** Opacidade da moldura. */
|
|
75
|
+
export const FRAME_ALPHA = 0.95;
|
|
76
|
+
/** Espessura da moldura, em pixels LÓGICOS (escala junto com o canvas). */
|
|
77
|
+
export const FRAME_WIDTH = 1;
|
|
78
|
+
/** Recuo da bolinha indicadora a partir da borda direita da tela. */
|
|
79
|
+
export const DOT_INSET_X = 9;
|
|
80
|
+
/** Recuo da bolinha indicadora a partir do topo da tela. */
|
|
81
|
+
export const DOT_INSET_Y = 9;
|
|
82
|
+
/**
|
|
83
|
+
* A geometria de render para `n` jogadores, sem tocar em nada.
|
|
84
|
+
*
|
|
85
|
+
* `n <= 1` devolve `single: true` e NENHUM viewport: em tela única a câmera é filha do stage e o PIXI desenha
|
|
86
|
+
* direto no canvas — não há render-texture, nem moldura, nem bolinha (o indicador de modo de visão de tela
|
|
87
|
+
* única é outro, no DOM).
|
|
88
|
+
*
|
|
89
|
+
* `w`/`h` são parâmetros com o pixel canônico (320×180, ADR-0010) por padrão — não para o jogo trocá-los, mas
|
|
90
|
+
* para a conta ser conferível com números pequenos no teste.
|
|
91
|
+
*/
|
|
92
|
+
export function planScreens(n, w = LOGICAL_W, h = LOGICAL_H) {
|
|
93
|
+
const { cols, rows } = screenGrid(n); // fonte única: core/screens.ts (era a 5ª cópia, e a divergente)
|
|
94
|
+
if (n <= 1)
|
|
95
|
+
return { single: true, cols, rows, canvas: { w, h }, viewports: [] };
|
|
96
|
+
const viewports = [];
|
|
97
|
+
for (let i = 0; i < n; i++) {
|
|
98
|
+
let x = (i % cols) * w;
|
|
99
|
+
const y = Math.floor(i / cols) * h;
|
|
100
|
+
if (n === 3 && i === 2)
|
|
101
|
+
x = (w * cols - w) / 2; // 3 telas: a 3ª centralizada na linha de baixo
|
|
102
|
+
viewports.push({
|
|
103
|
+
x, y,
|
|
104
|
+
frame: { x: x + 0.5, y: y + 0.5, w: w - 1, h: h - 1 },
|
|
105
|
+
dot: { x: x + w - DOT_INSET_X, y: y + DOT_INSET_Y },
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
return { single: false, cols, rows, canvas: { w: w * cols, h: h * rows }, viewports };
|
|
109
|
+
}
|
|
110
|
+
export function initScreenPipeline(ctx) {
|
|
111
|
+
function configureRender() {
|
|
112
|
+
// 1) descarte do conjunto anterior — SEMPRE, inclusive ao voltar para tela única, senão as render-textures
|
|
113
|
+
// da configuração passada ficariam na GPU sem ninguém para desenhá-las.
|
|
114
|
+
ctx.getVpSpr().forEach((s) => s.destroy());
|
|
115
|
+
ctx.setVpSpr([]);
|
|
116
|
+
ctx.getVpTex().forEach((t) => t.destroy(true));
|
|
117
|
+
ctx.setVpTex([]);
|
|
118
|
+
const frames = ctx.getVpFrames();
|
|
119
|
+
if (frames) {
|
|
120
|
+
frames.destroy();
|
|
121
|
+
ctx.setVpFrames(null);
|
|
122
|
+
}
|
|
123
|
+
ctx.getVpDots().forEach((g) => g.destroy());
|
|
124
|
+
ctx.setVpDots([]);
|
|
125
|
+
const plan = planScreens(ctx.getNumPlayers());
|
|
126
|
+
// 2) tela única: a câmera volta a ser filha do stage (no fundo de tudo) e o PIXI desenha direto no canvas.
|
|
127
|
+
if (plan.single) {
|
|
128
|
+
if (ctx.camera.parent !== ctx.stage)
|
|
129
|
+
ctx.stage.addChildAt(ctx.camera, 0);
|
|
130
|
+
ctx.setMinimapVisible(true);
|
|
131
|
+
ctx.renderer.resize(plan.canvas.w, plan.canvas.h);
|
|
132
|
+
ctx.buildGameHud();
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
// 3) multi-tela: a câmera SAI da cena — passa a ser desenhada à mão, uma vez por viewport, dentro da
|
|
136
|
+
// render-texture de cada jogador (o laço em si é de render/draw.ts).
|
|
137
|
+
if (ctx.camera.parent)
|
|
138
|
+
ctx.camera.parent.removeChild(ctx.camera);
|
|
139
|
+
ctx.setMinimapVisible(false); // minimapa é de tela única (não cabe replicado em 4 telas de 320×180)
|
|
140
|
+
ctx.renderer.resize(plan.canvas.w, plan.canvas.h);
|
|
141
|
+
const vpTex = [], vpSpr = [];
|
|
142
|
+
for (const vp of plan.viewports) {
|
|
143
|
+
const rt = ctx.RenderTexture.create({ width: LOGICAL_W, height: LOGICAL_H });
|
|
144
|
+
rt.baseTexture.scaleMode = ctx.NEAREST; // pixel art: ampliar sem interpolar
|
|
145
|
+
const s = ctx.criarSprite(rt);
|
|
146
|
+
s.x = vp.x;
|
|
147
|
+
s.y = vp.y;
|
|
148
|
+
ctx.stage.addChild(s);
|
|
149
|
+
vpTex.push(rt);
|
|
150
|
+
vpSpr.push(s);
|
|
151
|
+
}
|
|
152
|
+
ctx.setVpTex(vpTex);
|
|
153
|
+
ctx.setVpSpr(vpSpr);
|
|
154
|
+
// moldura: uma linha por tela, num único Graphics (separa e enquadra como a borda da tela única)
|
|
155
|
+
const vpFrames = ctx.criarDesenho();
|
|
156
|
+
for (const vp of plan.viewports) {
|
|
157
|
+
vpFrames.lineStyle(FRAME_WIDTH, FRAME_COLOR, FRAME_ALPHA);
|
|
158
|
+
vpFrames.drawRect(vp.frame.x, vp.frame.y, vp.frame.w, vp.frame.h);
|
|
159
|
+
}
|
|
160
|
+
ctx.stage.addChild(vpFrames);
|
|
161
|
+
ctx.setVpFrames(vpFrames);
|
|
162
|
+
// bolinhas indicadoras do modo de visão: ACIMA de tudo e FORA da render-texture, portanto fora do filtro
|
|
163
|
+
// do viewport — é por isso que elas continuam visíveis no modo cegueira (ver updateVpDots em viz-setters).
|
|
164
|
+
ctx.setVpDots(plan.viewports.map((vp) => {
|
|
165
|
+
const g = ctx.criarDesenho();
|
|
166
|
+
g.x = vp.dot.x;
|
|
167
|
+
g.y = vp.dot.y;
|
|
168
|
+
g.visible = false;
|
|
169
|
+
ctx.stage.addChild(g);
|
|
170
|
+
return g;
|
|
171
|
+
}));
|
|
172
|
+
ctx.buildGameHud();
|
|
173
|
+
ctx.applyVpFilters();
|
|
174
|
+
ctx.updateVpDots();
|
|
175
|
+
}
|
|
176
|
+
return { configureRender };
|
|
177
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
import { type CenarioTema } from './cenario-data.js';
|
|
3
|
+
import type { Tileset } from './world-tex.js';
|
|
4
|
+
/** `HTMLImageElement`, reduzido ao que o carregamento usa. */
|
|
5
|
+
/** O tileset de um tema: interior + topo. Corresponde ao `Tileset` de render/world-tex. */
|
|
6
|
+
/**
|
|
7
|
+
* Os dois tiles do tema. `Tileset` vem de `render/world-tex`, que é o dono — ele os desenha, e por isso
|
|
8
|
+
* sabe que são `CanvasImageSource`. A versão que estava aqui dizia `unknown` nos dois campos: uma
|
|
9
|
+
* descrição que apenas SE PARECE com a real, que é o que o ADR-0039 proíbe. O `unknown` não protegia
|
|
10
|
+
* nada — só empurrava a conversão para quem recebesse.
|
|
11
|
+
*/
|
|
12
|
+
export type TilesDoTema = Tileset;
|
|
13
|
+
/** `worldSprite` — só a troca de textura. */
|
|
14
|
+
interface SpriteComTextura {
|
|
15
|
+
texture: unknown;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Baixa `tile_fill.png` + `tile_surface.png` do tema. Resolve com os dois, ou com `null` se QUALQUER um falhar.
|
|
19
|
+
*
|
|
20
|
+
* Verbatim do `loadTileImages` do monólito, `fail` inclusive: quando uma das imagens erra, o `res(null)` sai na
|
|
21
|
+
* hora e a outra, ao chegar, encontra `fail` ligado e não resolve por cima. A promessa nunca REJEITA — tema sem
|
|
22
|
+
* arte é um caminho normal (a Cidade não tem tileset próprio), não um erro, e uma rejeição aqui derrubaria a
|
|
23
|
+
* troca de cenário inteira. Nunca pendura: os dois lados de cada imagem estão cobertos.
|
|
24
|
+
*/
|
|
25
|
+
export interface SetCenarioCtx {
|
|
26
|
+
setCenarioValue: (theme: string) => void;
|
|
27
|
+
getCenario: () => string;
|
|
28
|
+
aplicarTemaParallax: (theme: string, T: CenarioTema) => void;
|
|
29
|
+
/**
|
|
30
|
+
* Os tiles do tema, SÍNCRONOS. Eram dois PNG baixados por `carregarTilesDoTema`; hoje a Cidade os desenha
|
|
31
|
+
* (render/city-tiles) e os outros temas devolvem `null`, que é o caminho dos blocos v3 — o mesmo `null` de
|
|
32
|
+
* antes, agora imediato em vez de prometido.
|
|
33
|
+
*/
|
|
34
|
+
getTiles: (tema: string) => TilesDoTema | null;
|
|
35
|
+
/** Builder da canvas do nível. `HTMLCanvasElement` e não `unknown`: é o que o `render/world-tex`
|
|
36
|
+
* devolve, e o `unknown` só adiava a conversão até o `main.ts`, onde ela virava erro (ADR-0039). */
|
|
37
|
+
worldCanvas: (tiles: TilesDoTema | null) => HTMLCanvasElement;
|
|
38
|
+
tex: (canvas: HTMLCanvasElement) => unknown;
|
|
39
|
+
clearWorldTexCache: () => void;
|
|
40
|
+
/** Escreve `worldCanvasNormal`/`worldTexNormal` (os dois `let` do composition root), sempre em par. A
|
|
41
|
+
* TEXTURA segue `unknown` de propósito — é objeto do PixiJS, e este módulo roda no project `node`. */
|
|
42
|
+
setWorldTextures: (canvas: HTMLCanvasElement, textura: unknown) => void;
|
|
43
|
+
isVizReady: () => boolean;
|
|
44
|
+
reapplyVizAll: () => void;
|
|
45
|
+
getWorldSprite: () => SpriteComTextura | null | undefined;
|
|
46
|
+
isVidaReady: () => boolean;
|
|
47
|
+
applyCenarioVida: () => void;
|
|
48
|
+
}
|
|
49
|
+
export interface SetCenarioApi {
|
|
50
|
+
/** Troca o cenário. Valida, persiste, repinta o fundo, refaz o mundo e avisa quem depende. */
|
|
51
|
+
setCenario(theme: string): void;
|
|
52
|
+
}
|
|
53
|
+
export declare function createSetCenario(ctx: SetCenarioCtx): SetCenarioApi;
|
|
54
|
+
export {};
|