@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.
Files changed (267) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +107 -0
  3. package/app/css/style.css +622 -0
  4. package/app/public/vendor/fonts/andika-400-ext.woff2 +0 -0
  5. package/app/public/vendor/fonts/andika-400.woff2 +0 -0
  6. package/app/public/vendor/fonts/andika-700-ext.woff2 +0 -0
  7. package/app/public/vendor/fonts/andika-700.woff2 +0 -0
  8. package/app/public/vendor/fonts/atkinson-400-ext.woff2 +0 -0
  9. package/app/public/vendor/fonts/atkinson-400.woff2 +0 -0
  10. package/app/public/vendor/fonts/atkinson-700-ext.woff2 +0 -0
  11. package/app/public/vendor/fonts/atkinson-700.woff2 +0 -0
  12. package/app/public/vendor/fonts/atkinson-mono-400-ext.woff2 +0 -0
  13. package/app/public/vendor/fonts/atkinson-mono-400.woff2 +0 -0
  14. package/app/public/vendor/fonts/comicneue-400.woff2 +0 -0
  15. package/app/public/vendor/fonts/comicneue-700.woff2 +0 -0
  16. package/app/public/vendor/fonts/greatvibes-400.woff2 +0 -0
  17. package/app/public/vendor/fonts/inter-400.woff2 +0 -0
  18. package/app/public/vendor/fonts/inter-700.woff2 +0 -0
  19. package/app/public/vendor/fonts/lato-400.woff2 +0 -0
  20. package/app/public/vendor/fonts/lato-700.woff2 +0 -0
  21. package/app/public/vendor/fonts/lexend-400-ext.woff2 +0 -0
  22. package/app/public/vendor/fonts/lexend-400.woff2 +0 -0
  23. package/app/public/vendor/fonts/lexend-700-ext.woff2 +0 -0
  24. package/app/public/vendor/fonts/lexend-700.woff2 +0 -0
  25. package/app/public/vendor/fonts/literata-400.woff2 +0 -0
  26. package/app/public/vendor/fonts/literata-700.woff2 +0 -0
  27. package/app/public/vendor/fonts/newsreader-400.woff2 +0 -0
  28. package/app/public/vendor/fonts/newsreader-700.woff2 +0 -0
  29. package/app/public/vendor/fonts/opensans-400.woff2 +0 -0
  30. package/app/public/vendor/fonts/opensans-700.woff2 +0 -0
  31. package/app/public/vendor/fonts/pinyon-400.woff2 +0 -0
  32. package/app/public/vendor/fonts/quattro-400.woff2 +0 -0
  33. package/app/public/vendor/fonts/quattro-700.woff2 +0 -0
  34. package/app/public/vendor/fonts/sourcesans-400.woff2 +0 -0
  35. package/app/public/vendor/fonts/sourcesans-700.woff2 +0 -0
  36. package/app/public/vendor/fonts/sourceserif-400.woff2 +0 -0
  37. package/app/public/vendor/fonts/sourceserif-700.woff2 +0 -0
  38. package/app/public/vendor/fonts/ufcook-700.woff2 +0 -0
  39. package/app/public/vendor/fonts/ufmag-400.woff2 +0 -0
  40. package/app/public/vendor/fonts.css +88 -0
  41. package/dist-pkg/boot/create-game.d.ts +159 -0
  42. package/dist-pkg/boot/create-game.js +266 -0
  43. package/dist-pkg/core/a11y-sr.d.ts +4 -0
  44. package/dist-pkg/core/a11y-sr.js +20 -0
  45. package/dist-pkg/core/actions.d.ts +100 -0
  46. package/dist-pkg/core/actions.js +149 -0
  47. package/dist-pkg/core/anel.d.ts +17 -0
  48. package/dist-pkg/core/anel.js +31 -0
  49. package/dist-pkg/core/collision.d.ts +21 -0
  50. package/dist-pkg/core/collision.js +62 -0
  51. package/dist-pkg/core/constants.d.ts +84 -0
  52. package/dist-pkg/core/constants.js +64 -0
  53. package/dist-pkg/core/contract.d.ts +227 -0
  54. package/dist-pkg/core/contract.js +184 -0
  55. package/dist-pkg/core/dom-query.d.ts +9 -0
  56. package/dist-pkg/core/dom-query.js +25 -0
  57. package/dist-pkg/core/entity.d.ts +150 -0
  58. package/dist-pkg/core/entity.js +38 -0
  59. package/dist-pkg/core/escape-html.d.ts +16 -0
  60. package/dist-pkg/core/escape-html.js +32 -0
  61. package/dist-pkg/core/i18n.d.ts +71 -0
  62. package/dist-pkg/core/i18n.js +236 -0
  63. package/dist-pkg/core/layers.d.ts +39 -0
  64. package/dist-pkg/core/layers.js +71 -0
  65. package/dist-pkg/core/letter-grid.d.ts +55 -0
  66. package/dist-pkg/core/letter-grid.js +116 -0
  67. package/dist-pkg/core/loop.d.ts +17 -0
  68. package/dist-pkg/core/loop.js +36 -0
  69. package/dist-pkg/core/password.d.ts +34 -0
  70. package/dist-pkg/core/password.js +205 -0
  71. package/dist-pkg/core/rng.d.ts +20 -0
  72. package/dist-pkg/core/rng.js +61 -0
  73. package/dist-pkg/core/rotulo-acessivel.d.ts +17 -0
  74. package/dist-pkg/core/rotulo-acessivel.js +41 -0
  75. package/dist-pkg/core/run-state.d.ts +115 -0
  76. package/dist-pkg/core/run-state.js +40 -0
  77. package/dist-pkg/core/scenes.d.ts +68 -0
  78. package/dist-pkg/core/scenes.js +64 -0
  79. package/dist-pkg/core/screens.d.ts +16 -0
  80. package/dist-pkg/core/screens.js +28 -0
  81. package/dist-pkg/core/state.d.ts +138 -0
  82. package/dist-pkg/core/state.js +290 -0
  83. package/dist-pkg/core/tiles.d.ts +12 -0
  84. package/dist-pkg/core/tiles.js +61 -0
  85. package/dist-pkg/core/world.d.ts +4 -0
  86. package/dist-pkg/core/world.js +58 -0
  87. package/dist-pkg/educational/activities-registry.d.ts +47 -0
  88. package/dist-pkg/educational/activities-registry.js +95 -0
  89. package/dist-pkg/i18n/en.d.ts +3 -0
  90. package/dist-pkg/i18n/en.js +560 -0
  91. package/dist-pkg/i18n/es.d.ts +3 -0
  92. package/dist-pkg/i18n/es.js +558 -0
  93. package/dist-pkg/i18n/pt.d.ts +3 -0
  94. package/dist-pkg/i18n/pt.js +618 -0
  95. package/dist-pkg/input/default-bindings.d.ts +24 -0
  96. package/dist-pkg/input/default-bindings.js +122 -0
  97. package/dist-pkg/input/devices.d.ts +15 -0
  98. package/dist-pkg/input/devices.js +31 -0
  99. package/dist-pkg/input/edges.d.ts +58 -0
  100. package/dist-pkg/input/edges.js +49 -0
  101. package/dist-pkg/input/gamepad.d.ts +203 -0
  102. package/dist-pkg/input/gamepad.js +531 -0
  103. package/dist-pkg/input/keyboard-runtime.d.ts +61 -0
  104. package/dist-pkg/input/keyboard-runtime.js +83 -0
  105. package/dist-pkg/input/keyboard.d.ts +45 -0
  106. package/dist-pkg/input/keyboard.js +80 -0
  107. package/dist-pkg/input/keydown.d.ts +270 -0
  108. package/dist-pkg/input/keydown.js +381 -0
  109. package/dist-pkg/input/latch.d.ts +30 -0
  110. package/dist-pkg/input/latch.js +63 -0
  111. package/dist-pkg/input/pointer-space.d.ts +40 -0
  112. package/dist-pkg/input/pointer-space.js +51 -0
  113. package/dist-pkg/input/state.d.ts +11 -0
  114. package/dist-pkg/input/state.js +12 -0
  115. package/dist-pkg/input/touch-bindings.d.ts +210 -0
  116. package/dist-pkg/input/touch-bindings.js +365 -0
  117. package/dist-pkg/input/touch.d.ts +173 -0
  118. package/dist-pkg/input/touch.js +314 -0
  119. package/dist-pkg/input/transports.d.ts +90 -0
  120. package/dist-pkg/input/transports.js +96 -0
  121. package/dist-pkg/input/vocabulary-migration.d.ts +58 -0
  122. package/dist-pkg/input/vocabulary-migration.js +125 -0
  123. package/dist-pkg/platform/audio-ambient.d.ts +27 -0
  124. package/dist-pkg/platform/audio-ambient.js +89 -0
  125. package/dist-pkg/platform/audio-earcons.d.ts +24 -0
  126. package/dist-pkg/platform/audio-earcons.js +62 -0
  127. package/dist-pkg/platform/audio-jingles.d.ts +17 -0
  128. package/dist-pkg/platform/audio-jingles.js +54 -0
  129. package/dist-pkg/platform/audio-mixer.d.ts +14 -0
  130. package/dist-pkg/platform/audio-mixer.js +62 -0
  131. package/dist-pkg/platform/audio-nav.d.ts +51 -0
  132. package/dist-pkg/platform/audio-nav.js +99 -0
  133. package/dist-pkg/platform/audio-sonar.d.ts +71 -0
  134. package/dist-pkg/platform/audio-sonar.js +173 -0
  135. package/dist-pkg/platform/audio.d.ts +32 -0
  136. package/dist-pkg/platform/audio.js +191 -0
  137. package/dist-pkg/platform/interruptible-speech.d.ts +19 -0
  138. package/dist-pkg/platform/interruptible-speech.js +79 -0
  139. package/dist-pkg/platform/speech.d.ts +2 -0
  140. package/dist-pkg/platform/speech.js +35 -0
  141. package/dist-pkg/platform/storage.d.ts +87 -0
  142. package/dist-pkg/platform/storage.js +148 -0
  143. package/dist-pkg/platform/tts.d.ts +33 -0
  144. package/dist-pkg/platform/tts.js +142 -0
  145. package/dist-pkg/render/camera.d.ts +58 -0
  146. package/dist-pkg/render/camera.js +105 -0
  147. package/dist-pkg/render/canvas.d.ts +13 -0
  148. package/dist-pkg/render/canvas.js +26 -0
  149. package/dist-pkg/render/cenario-data.d.ts +127 -0
  150. package/dist-pkg/render/cenario-data.js +145 -0
  151. package/dist-pkg/render/city-tex.d.ts +63 -0
  152. package/dist-pkg/render/city-tex.js +228 -0
  153. package/dist-pkg/render/city-tiles.d.ts +11 -0
  154. package/dist-pkg/render/city-tiles.js +140 -0
  155. package/dist-pkg/render/crt.d.ts +19 -0
  156. package/dist-pkg/render/crt.js +92 -0
  157. package/dist-pkg/render/cvd-matrices.d.ts +35 -0
  158. package/dist-pkg/render/cvd-matrices.js +91 -0
  159. package/dist-pkg/render/draw.d.ts +149 -0
  160. package/dist-pkg/render/draw.js +221 -0
  161. package/dist-pkg/render/fx.d.ts +63 -0
  162. package/dist-pkg/render/fx.js +123 -0
  163. package/dist-pkg/render/hc-role-data.d.ts +14 -0
  164. package/dist-pkg/render/hc-role-data.js +24 -0
  165. package/dist-pkg/render/high-contrast.d.ts +109 -0
  166. package/dist-pkg/render/high-contrast.js +254 -0
  167. package/dist-pkg/render/lq-filter.d.ts +37 -0
  168. package/dist-pkg/render/lq-filter.js +94 -0
  169. package/dist-pkg/render/minimap.d.ts +11 -0
  170. package/dist-pkg/render/minimap.js +94 -0
  171. package/dist-pkg/render/parallax.d.ts +85 -0
  172. package/dist-pkg/render/parallax.js +158 -0
  173. package/dist-pkg/render/player-anim.d.ts +71 -0
  174. package/dist-pkg/render/player-anim.js +152 -0
  175. package/dist-pkg/render/port.d.ts +143 -0
  176. package/dist-pkg/render/port.js +29 -0
  177. package/dist-pkg/render/recycling-tex.d.ts +48 -0
  178. package/dist-pkg/render/recycling-tex.js +164 -0
  179. package/dist-pkg/render/scene-city.d.ts +77 -0
  180. package/dist-pkg/render/scene-city.js +181 -0
  181. package/dist-pkg/render/scene-parallax.d.ts +72 -0
  182. package/dist-pkg/render/scene-parallax.js +426 -0
  183. package/dist-pkg/render/scene-sky.d.ts +126 -0
  184. package/dist-pkg/render/scene-sky.js +295 -0
  185. package/dist-pkg/render/screen-pipeline.d.ts +134 -0
  186. package/dist-pkg/render/screen-pipeline.js +177 -0
  187. package/dist-pkg/render/set-cenario.d.ts +54 -0
  188. package/dist-pkg/render/set-cenario.js +103 -0
  189. package/dist-pkg/render/sprite-fx.d.ts +3 -0
  190. package/dist-pkg/render/sprite-fx.js +40 -0
  191. package/dist-pkg/render/textures.d.ts +75 -0
  192. package/dist-pkg/render/textures.js +240 -0
  193. package/dist-pkg/render/title-scene.d.ts +46 -0
  194. package/dist-pkg/render/title-scene.js +75 -0
  195. package/dist-pkg/render/viewports.d.ts +52 -0
  196. package/dist-pkg/render/viewports.js +185 -0
  197. package/dist-pkg/render/viz-axes.d.ts +98 -0
  198. package/dist-pkg/render/viz-axes.js +184 -0
  199. package/dist-pkg/render/viz-modes.d.ts +61 -0
  200. package/dist-pkg/render/viz-modes.js +74 -0
  201. package/dist-pkg/render/viz-setters.d.ts +130 -0
  202. package/dist-pkg/render/viz-setters.js +244 -0
  203. package/dist-pkg/render/weather.d.ts +96 -0
  204. package/dist-pkg/render/weather.js +171 -0
  205. package/dist-pkg/render/wheelchair-sprites.d.ts +24 -0
  206. package/dist-pkg/render/wheelchair-sprites.js +63 -0
  207. package/dist-pkg/render/world-tex.d.ts +18 -0
  208. package/dist-pkg/render/world-tex.js +87 -0
  209. package/dist-pkg/ui/activities-menu.d.ts +258 -0
  210. package/dist-pkg/ui/activities-menu.js +648 -0
  211. package/dist-pkg/ui/caa-sets.d.ts +39 -0
  212. package/dist-pkg/ui/caa-sets.js +65 -0
  213. package/dist-pkg/ui/changed-mark.d.ts +15 -0
  214. package/dist-pkg/ui/changed-mark.js +96 -0
  215. package/dist-pkg/ui/debug-panel.d.ts +67 -0
  216. package/dist-pkg/ui/debug-panel.js +173 -0
  217. package/dist-pkg/ui/dom.d.ts +24 -0
  218. package/dist-pkg/ui/dom.js +47 -0
  219. package/dist-pkg/ui/focus-trap.d.ts +71 -0
  220. package/dist-pkg/ui/focus-trap.js +99 -0
  221. package/dist-pkg/ui/fonts.d.ts +41 -0
  222. package/dist-pkg/ui/fonts.js +59 -0
  223. package/dist-pkg/ui/hud.d.ts +137 -0
  224. package/dist-pkg/ui/hud.js +229 -0
  225. package/dist-pkg/ui/item-announcement.d.ts +19 -0
  226. package/dist-pkg/ui/item-announcement.js +36 -0
  227. package/dist-pkg/ui/layout.d.ts +6 -0
  228. package/dist-pkg/ui/layout.js +54 -0
  229. package/dist-pkg/ui/loop-crash.d.ts +19 -0
  230. package/dist-pkg/ui/loop-crash.js +60 -0
  231. package/dist-pkg/ui/map-hub.d.ts +56 -0
  232. package/dist-pkg/ui/map-hub.js +138 -0
  233. package/dist-pkg/ui/menu-nav.d.ts +141 -0
  234. package/dist-pkg/ui/menu-nav.js +390 -0
  235. package/dist-pkg/ui/pause-icons.d.ts +327 -0
  236. package/dist-pkg/ui/pause-icons.js +620 -0
  237. package/dist-pkg/ui/reach-notice.d.ts +34 -0
  238. package/dist-pkg/ui/reach-notice.js +71 -0
  239. package/dist-pkg/ui/settings-audio.d.ts +139 -0
  240. package/dist-pkg/ui/settings-audio.js +544 -0
  241. package/dist-pkg/ui/settings-caa.d.ts +45 -0
  242. package/dist-pkg/ui/settings-caa.js +137 -0
  243. package/dist-pkg/ui/settings-controls.d.ts +100 -0
  244. package/dist-pkg/ui/settings-controls.js +152 -0
  245. package/dist-pkg/ui/settings-empathy.d.ts +62 -0
  246. package/dist-pkg/ui/settings-empathy.js +137 -0
  247. package/dist-pkg/ui/settings-motion.d.ts +95 -0
  248. package/dist-pkg/ui/settings-motion.js +282 -0
  249. package/dist-pkg/ui/settings-motor.d.ts +77 -0
  250. package/dist-pkg/ui/settings-motor.js +215 -0
  251. package/dist-pkg/ui/settings-panel.d.ts +76 -0
  252. package/dist-pkg/ui/settings-panel.js +187 -0
  253. package/dist-pkg/ui/settings-typo.d.ts +74 -0
  254. package/dist-pkg/ui/settings-typo.js +150 -0
  255. package/dist-pkg/ui/settings-visual.d.ts +111 -0
  256. package/dist-pkg/ui/settings-visual.js +252 -0
  257. package/dist-pkg/ui/shell.d.ts +239 -0
  258. package/dist-pkg/ui/shell.js +430 -0
  259. package/dist-pkg/ui/title.d.ts +36 -0
  260. package/dist-pkg/ui/title.js +60 -0
  261. package/dist-pkg/ui/vlibras.d.ts +11 -0
  262. package/dist-pkg/ui/vlibras.js +87 -0
  263. package/dist-pkg/ui/webcam.d.ts +7 -0
  264. package/dist-pkg/ui/webcam.js +88 -0
  265. package/docs/CREDITS.md +64 -0
  266. package/docs/LICENSES.md +132 -0
  267. package/package.json +115 -0
@@ -0,0 +1,74 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ export const VIZ_MODES = [
3
+ { key: 'normal', kind: 'normal', nome: 'viz.normal', desc: 'viz.desc.normal' },
4
+ { key: 'hc-direto', kind: 'hcnew', nome: 'viz.hc-direto', desc: 'viz.desc.hc-direto' },
5
+ { key: 'hc-direto-45', kind: 'hcnew', nome: 'viz.hc-direto-45', desc: 'viz.desc.hc-direto-45' },
6
+ { key: 'hc-direto-7', kind: 'hcnew', nome: 'viz.hc-direto-7', desc: 'viz.desc.hc-direto-7' },
7
+ { key: 'sim-deuter', sim: true, kind: 'filter', nome: 'viz.sim-deuter', desc: 'viz.desc.sim-deuter' },
8
+ { key: 'sim-protan', sim: true, kind: 'filter', nome: 'viz.sim-protan', desc: 'viz.desc.sim-protan' },
9
+ { key: 'sim-tritan', sim: true, kind: 'filter', nome: 'viz.sim-tritan', desc: 'viz.desc.sim-tritan' },
10
+ { key: 'fix-protan', kind: 'filter', nome: 'viz.fix-protan', desc: 'viz.desc.fix-protan' },
11
+ { key: 'fix-deuter', kind: 'filter', nome: 'viz.fix-deuter', desc: 'viz.desc.fix-deuter' },
12
+ { key: 'fix-tritan', kind: 'filter', nome: 'viz.fix-tritan', desc: 'viz.desc.fix-tritan' },
13
+ { key: 'lv-blur', sim: true, kind: 'lowvision', lv: 'blur', nome: 'viz.lv-blur', desc: 'viz.desc.lv-blur' },
14
+ { key: 'lv-haze', sim: true, kind: 'lowvision', lv: 'haze', nome: 'viz.lv-haze', desc: 'viz.desc.lv-haze' },
15
+ { key: 'lv-tunnel', sim: true, kind: 'lowvision', lv: 'tunnel', nome: 'viz.lv-tunnel', desc: 'viz.desc.lv-tunnel' },
16
+ { key: 'lv-macular', sim: true, kind: 'lowvision', lv: 'macular', nome: 'viz.lv-macular', desc: 'viz.desc.lv-macular' },
17
+ { key: 'lv-diabetic', sim: true, kind: 'lowvision', lv: 'diabetic', nome: 'viz.lv-diabetic', desc: 'viz.desc.lv-diabetic' },
18
+ { key: 'blind', sim: true, kind: 'blind', nome: 'viz.blind', desc: 'viz.desc.blind' },
19
+ ];
20
+ export const VIZ_BY_KEY = Object.fromEntries(VIZ_MODES.map((m) => [m.key, m]));
21
+ export const VIZ_FILTER = { 'sim-deuter': 'url(#cvd-deuter)', 'sim-protan': 'url(#cvd-protan)', 'sim-tritan': 'url(#cvd-tritan)',
22
+ 'fix-protan': 'url(#cvd-fix-protan)', 'fix-deuter': 'url(#cvd-fix-deuter)', 'fix-tritan': 'url(#cvd-fix-tritan)',
23
+ 'lv-blur': 'blur(2.4px)', 'lv-haze': 'contrast(.58) brightness(1.14) blur(.6px)', 'lv-tunnel': 'blur(.5px)', 'lv-macular': '', 'lv-diabetic': 'blur(.8px)', 'blind': 'brightness(0)' };
24
+ export const VIZ_CYCLE = VIZ_MODES.map((m) => m.key);
25
+ /**
26
+ * Este modo simula uma deficiência? Chave desconhecida (ou vazia) → false, porque a pergunta que o chamador
27
+ * está fazendo é "posso desligar isto sem tirar nada de ninguém?", e a resposta honesta diante do
28
+ * desconhecido é não.
29
+ */
30
+ export function simulatesDisability(key) {
31
+ return VIZ_BY_KEY[key]?.sim === true;
32
+ }
33
+ /**
34
+ * As CORREÇÕES de daltonismo: daltonizam a tela para quem TEM a condição. Derivadas do catálogo, nunca
35
+ * listadas à mão — são exatamente os filtros que não simulam.
36
+ *
37
+ * Existem como lista própria porque moram no menu de ACESSIBILIDADE VISUAL, e não no de empatia (decisão do
38
+ * Dev, issue #60). Ficaram anos no menu errado por um motivo que este arquivo agora conserta: `kind` não
39
+ * distinguia simular de corrigir, então o painel de empatia, que se recorta por `kind:'filter'`, arrastava as
40
+ * três junto. Dois públicos opostos na mesma lista — quem quer sentir como é ser daltônico e quem é.
41
+ */
42
+ export const VIZ_CORRECTIONS = VIZ_MODES.filter((m) => m.kind === 'filter' && !m.sim);
43
+ /* ===================== AS DUAS PILHAS COM UM NOME SÓ (achado 8, item 19) ===================== */
44
+ //
45
+ // O segundo consumidor mediu, e a medição é o motivo desta seção existir:
46
+ //
47
+ // "O ALTO CONTRASTE NÃO VIAJA, e a razão é estrutural, não um defeito. Os modos `hcnew` REPINTAM
48
+ // TEXTURAS de tile na PIXI; um quiz não tem tiles, e não há o que repintar. Ou seja: o que o menu chama
49
+ // de 'acessibilidade visual' são DUAS pilhas com um nome só — uma de DOM/CSS (filtros de daltonismo,
50
+ // tipografia, caixa alta) que serve a qualquer jogo; uma de CANVAS (renderização direta, contornos,
51
+ // cores de papel) que só existe onde há mundo. O painel as apresenta numa lista única de 7 modos… A
52
+ // divisão do passo 5 precisa cortar AQUI."
53
+ //
54
+ // Este é o corte, e ele é de DADO e não de arquivo — os arquivos já estavam separados (`render/cvd-matrices`
55
+ // é DOM puro; `render/high-contrast` importa `core/collision.tileAt`). O que faltava era a tabela DIZER a
56
+ // qual pilha cada modo pertence, para que ninguém mais reconstruísse a resposta de cabeça.
57
+ //
58
+ // E era reconstruída: o segundo consumidor escrevia
59
+ // `VIZ_MODES.filter((m) => m.kind === 'normal' || (m.kind === 'filter' && !simulatesDisability(m.key)))`
60
+ // — uma expressão que mistura DUAS perguntas diferentes ("precisa de canvas?" e "isto simula deficiência?")
61
+ // e que todo consumidor futuro teria de reinventar, com a chance de acertar uma e errar a outra.
62
+ /** Este modo precisa de um CANVAS de mundo para existir? Só os `hcnew` precisam: eles repintam texturas. */
63
+ export function needsCanvas(key) {
64
+ return VIZ_BY_KEY[key]?.kind === 'hcnew';
65
+ }
66
+ /**
67
+ * Os modos que funcionam em QUALQUER jogo — os que se aplicam como filtro de CSS sobre um elemento.
68
+ *
69
+ * Derivada, nunca listada à mão: um modo novo entra na pilha certa por causa do `kind` que ele declara, e não
70
+ * porque alguém lembrou de acrescentá-lo aqui. É a mesma regra de `VIZ_CORRECTIONS`.
71
+ */
72
+ export const VIZ_DOM_ONLY = VIZ_MODES.filter((m) => !needsCanvas(m.key));
73
+ /** Os modos que EXIGEM mundo. Complemento exato de `VIZ_DOM_ONLY` — juntos, os 16, sem sobra nem repetição. */
74
+ export const VIZ_CANVAS_ONLY = VIZ_MODES.filter((m) => needsCanvas(m.key));
@@ -0,0 +1,130 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ import { type VizMode } from './viz-modes.js';
3
+ import type { DomQuery } from '../core/dom-query.js';
4
+ import type { ComFiltro, ComTextura, Visivel, DesenhoComCirculo, AplicarFiltroCss, AlcanceDoFiltro, AplicarAltoContrasteNoDom } from './port.js';
5
+ /**
6
+ * Até onde o filtro deste modo vai. MELHORIA alcança os menus; EMPATIA fica no mundo (ver `AlcanceDoFiltro`).
7
+ * Derivado de `sim`, do catálogo — a mesma flag que já distingue os dois, e não uma segunda lista para
8
+ * alguém esquecer de atualizar.
9
+ */
10
+ export declare function alcanceDoModo(mode: string): AlcanceDoFiltro;
11
+ /** Modo por chave, com o fallback do original: chave desconhecida (ou nula) CAI em `normal`. */
12
+ export declare function resolveViz(key: string | null | undefined): VizMode;
13
+ /** É um dos três níveis de Renderização Direta (alto contraste)? Mesmo teste do original: `!!DIRECT_CFG[mode]`. */
14
+ export declare function isDirectMode(mode: string): boolean;
15
+ /** Filtro CSS da canvas no SOLO: simulação/correção de visão + realce L→Q, compostos e sem vazios. */
16
+ export declare function cssFilterFor(mode: string, lq: string): string;
17
+ /** Bolinha indicadora de um viewport: só cegueira (branca) e baixa visão (verde) acendem. */
18
+ export declare function vizDotFor(m: VizMode | undefined): {
19
+ visible: boolean;
20
+ fill: number | null;
21
+ };
22
+ /** Bolinha GLOBAL (#viz-indicator) para um `kind`: visível, classes e rótulo de leitor de tela. */
23
+ export declare function vizIndicatorFor(kind: string): {
24
+ on: boolean;
25
+ blind: boolean;
26
+ low: boolean;
27
+ label: string;
28
+ };
29
+ /** Classe do overlay DOM de baixa visão (`lv-haze`/`lv-tunnel`/…); '' para qualquer outro kind. */
30
+ export declare function lvOverlayClassFor(m: VizMode): string;
31
+ /** HTML do grupo de rádios de modos visuais (uma linha por modo; o atual marcado). Verbatim do game.js. */
32
+ export declare function vizGroupHtml(modes: readonly VizMode[], cur: string): string;
33
+ /** Fala do leitor de tela ao escolher um modo: prefixa "Jogador N:" só em multi-tela. */
34
+ export declare function vizGroupSay(numPlayers: number, sel: number, nome: string): string;
35
+ type Filtered = ComFiltro;
36
+ type Textured = ComTextura;
37
+ /** PIXI.Graphics da bolinha por viewport — só o que updateVpDots realmente usa. */
38
+ type DotGfx = Visivel & DesenhoComCirculo;
39
+ interface ClassListHost {
40
+ classList: {
41
+ toggle(token: string, force?: boolean): unknown;
42
+ remove(...tokens: string[]): unknown;
43
+ };
44
+ }
45
+ interface Pl {
46
+ viz: string;
47
+ sprite?: Textured | null;
48
+ _tx?: unknown;
49
+ }
50
+ interface Pu {
51
+ kind: string;
52
+ sprite?: Textured | null;
53
+ }
54
+ export interface VizSettersCtx {
55
+ /** O seletor do jogo (#viz-overlay, #viz-indicator, listas do painel). `DomQuery` de `core/dom-query`
56
+ * desde 2026-08-26: a versão local era NÃO-GENÉRICA, e uma função genérica atribuída a uma
57
+ * assinatura não-genérica é instanciada pela RESTRIÇÃO. O `El` estrutural continua abaixo — ele é a
58
+ * fatia que este módulo LÊ, e `HTMLElement` a satisfaz. */
59
+ $: DomQuery;
60
+ body: ClassListHost;
61
+ srSay: (s: string) => void;
62
+ aplicarFiltroCss: AplicarFiltroCss;
63
+ /** Alto contraste no DOM — o filtro não o alcança porque ele não É filtro. Issue #83. */
64
+ aplicarAltoContrasteNoDom: AplicarAltoContrasteNoDom;
65
+ camera: Filtered;
66
+ worldSprite: Textured;
67
+ parallaxLayers: Textured[];
68
+ decoSprites: Textured[];
69
+ getVpSpr: () => Filtered[];
70
+ getVpDots: () => DotGfx[];
71
+ /**
72
+ * Os sprites dos ITENS declarados (o array vive no jogo e pode ser recriado — por isso getter).
73
+ *
74
+ * Era `getCoinSprites`, e o nome dizia o que eles são. Mesma fatia que `render/draw` recebe desde o corte
75
+ * das entidades declaradas: uma lista de sprites, sem o desenho saber o que cada um representa.
76
+ */
77
+ getItemSprites: () => (Textured | null | undefined)[];
78
+ /**
79
+ * Como o JOGO chama os itens dele no cache de recoloração de `render/high-contrast`.
80
+ *
81
+ * Era `spriteTexFor('coin', mode)` — a string cravada aqui dentro. O cache já não tinha forma de moeda
82
+ * (ele chaveia por `(id, modo)`); quem ainda nomeava uma era este módulo. Repare no vizinho de baixo: os
83
+ * power-ups sempre carregaram o próprio `kind`, e a engine só o repassa. Os itens não carregavam nada, e
84
+ * por isso o nome tinha de estar em algum lugar — agora está do lado de quem o escolheu.
85
+ */
86
+ itemTexId: string;
87
+ getPowerups: () => readonly Pu[];
88
+ getPlayers: () => Pl[];
89
+ getNumPlayers: () => number;
90
+ getSelVizPlayer: () => number;
91
+ setSelVizPlayer: (i: number) => void;
92
+ getSharedViz: () => string | null;
93
+ setSharedViz: (mode: string) => void;
94
+ invalidateSharedViz: () => void;
95
+ parallaxTexFor: (i: number, mode: string) => unknown;
96
+ treeTexFor: (mode: string) => unknown;
97
+ playerVizTex: (base: unknown, mode: string) => unknown;
98
+ pixiFilterFor: (mode: string) => unknown;
99
+ clearPlayerDirectCache: () => void;
100
+ setFrontDim: (on: boolean) => void;
101
+ rebuildExtras: () => void;
102
+ rebuildCoins: () => void;
103
+ setModoCego: (on: boolean) => void;
104
+ hideTouchControls: (reason?: string) => void;
105
+ reflectVizButtons: () => void;
106
+ renderVisualPanel: () => void;
107
+ renderEmpathyPanel: () => void;
108
+ }
109
+ export interface VizSettersApi {
110
+ /** Multi-tela: aplica as texturas ESTÁTICAS do modo (memoizado) + a do quadro atual de cada jogador. */
111
+ applySharedTextures(mode: string): void;
112
+ /** Bolinhas indicadoras por viewport (fora do filtro do viewport — visíveis mesmo em cegueira). */
113
+ updateVpDots(): void;
114
+ /** Filtro PIXI de cada viewport = modo do jogador daquela tela. */
115
+ applyVpFilters(): void;
116
+ /** Troca o modo de UM jogador: persiste, invalida o render estático e reaplica pelo caminho certo. */
117
+ setPlayerViz(i: number, mode: string): void;
118
+ /** Caminho SOLO: filtro CSS na canvas + texturas globais + overlay DOM + bolinha. */
119
+ applyVizGlobal(mode: string): void;
120
+ /** Reaplica tudo depois de uma mudança estrutural (cenário, nº de telas). */
121
+ reapplyVizAll(): void;
122
+ /** Bolinha global (#viz-indicator) para um `kind`. */
123
+ updateVizIndicator(kind: string): void;
124
+ /** Invalida os caches de textura direta (mundo/moeda/power-up/jogador) e re-renderiza. */
125
+ rebakeDirect(): void;
126
+ /** Grupo de rádios de modos visuais nos painéis (visual/empatia). */
127
+ renderVizGroup(listSel: string, tabsSel: string, modes: readonly VizMode[]): void;
128
+ }
129
+ export declare function initVizSetters(ctx: VizSettersCtx): VizSettersApi;
130
+ export {};
@@ -0,0 +1,244 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // render/viz-setters — APLICAÇÃO dos modos de visão acessível (daltonismo, baixa visão, cegueira, alto
3
+ // contraste), por jogador e globalmente. Extraído verbatim do game.js. É a camada de POLÍTICA — "qual modo
4
+ // vale onde" — e não a de FÁBRICA: quem constrói pixel (pixiFilterFor / parallaxTexFor / playerVizTex /
5
+ // lvOverlayTex / renderVpOverlay) fica no game.js e sai depois em `render/viewports` (grupo B); esses quatro
6
+ // entram AQUI por injeção. Ver docs/5-Refactoring/plano-modularizacao-mapa.md (A16/B2).
7
+ //
8
+ // Fronteiras que este módulo NÃO reabre:
9
+ // · `_lastSharedViz` FICA no game.js — não é cache de visão, é o registro de qual modo o pipeline de render
10
+ // ESTÁTICO aplicou por último, escrito de cinco lugares (rebuildCoins/rebuildExtras/applySharedTextures/
11
+ // setPlayerViz/reapplyVizAll). Entra por `getSharedViz`/`setSharedViz`/`invalidateSharedViz`.
12
+ // · `selVizPlayer` idem: quatro escritores (renderVizGroup, aba de jogador, ui/settings-visual, menu de
13
+ // pausa) → get/set por ctx, o `let` continua no game.js até o D1.
14
+ // · `_playerDirect` é `let` do game.js reatribuído por estas funções → clear por ctx
15
+ // (binding importado não pode ser reatribuído).
16
+ //
17
+ // As camadas e sprites PIXI (camera/worldSprite/parallaxLayers/decoSprites/vpSpr/vpDots) são CRIADOS no
18
+ // game.js — a ordem de addChild é a ordem de desenho — e entram injetados por interface ESTRUTURAL, para o
19
+ // módulo rodar no project `node` sem importar PIXI. Mesmo precedente de render/scene-sky e game/traffic.
20
+ // SEM I/O no import: initVizSetters(ctx) só fecha closures, não chama nada.
21
+ import { VIZ_MODES, VIZ_BY_KEY, VIZ_FILTER, simulatesDisability } from './viz-modes.js';
22
+ import { t } from '../core/i18n.js'; // VIZ_MODES guarda CHAVE i18n desde o item 14; quem exibe resolve
23
+ import { DIRECT_CFG, worldTexFor, spriteTexFor, clearWorldTexCache, clearSpriteTexCache } from './high-contrast.js';
24
+ import { pupTexFor, resetPupTexCache } from './textures.js';
25
+ import { lqFilter } from './lq-filter.js';
26
+ import { setVizModeValue } from '../core/state.js';
27
+ import * as store from '../platform/storage.js';
28
+ /* ===================== PURO (sem PIXI, sem DOM) — o que rende teste de verdade ===================== */
29
+ /**
30
+ * Até onde o filtro deste modo vai. MELHORIA alcança os menus; EMPATIA fica no mundo (ver `AlcanceDoFiltro`).
31
+ * Derivado de `sim`, do catálogo — a mesma flag que já distingue os dois, e não uma segunda lista para
32
+ * alguém esquecer de atualizar.
33
+ */
34
+ export function alcanceDoModo(mode) {
35
+ return simulatesDisability(mode) ? 'mundo' : 'mundo-e-menus';
36
+ }
37
+ /** Modo por chave, com o fallback do original: chave desconhecida (ou nula) CAI em `normal`. */
38
+ export function resolveViz(key) {
39
+ return VIZ_BY_KEY[key] || VIZ_BY_KEY.normal;
40
+ }
41
+ /** É um dos três níveis de Renderização Direta (alto contraste)? Mesmo teste do original: `!!DIRECT_CFG[mode]`. */
42
+ export function isDirectMode(mode) { return !!DIRECT_CFG[mode]; }
43
+ /** Filtro CSS da canvas no SOLO: simulação/correção de visão + realce L→Q, compostos e sem vazios. */
44
+ export function cssFilterFor(mode, lq) {
45
+ return [VIZ_FILTER[mode] || '', lq].filter(Boolean).join(' ');
46
+ }
47
+ /** Bolinha indicadora de um viewport: só cegueira (branca) e baixa visão (verde) acendem. */
48
+ export function vizDotFor(m) {
49
+ const on = !!m && (m.kind === 'blind' || m.kind === 'lowvision');
50
+ return { visible: on, fill: on ? (m.kind === 'blind' ? 0xffffff : 0x36d36a) : null };
51
+ }
52
+ /** Bolinha GLOBAL (#viz-indicator) para um `kind`: visível, classes e rótulo de leitor de tela. */
53
+ export function vizIndicatorFor(kind) {
54
+ return {
55
+ on: kind === 'blind' || kind === 'lowvision',
56
+ blind: kind === 'blind', low: kind === 'lowvision',
57
+ label: (kind === 'blind' ? 'Modo cegueira total' : 'Modo baixa visão') + '. Toque duas vezes para voltar às cores normais.',
58
+ };
59
+ }
60
+ /** Classe do overlay DOM de baixa visão (`lv-haze`/`lv-tunnel`/…); '' para qualquer outro kind. */
61
+ export function lvOverlayClassFor(m) {
62
+ return m.kind === 'lowvision' ? 'lv-' + m.lv : '';
63
+ }
64
+ /** HTML do grupo de rádios de modos visuais (uma linha por modo; o atual marcado). Verbatim do game.js. */
65
+ export function vizGroupHtml(modes, cur) {
66
+ return modes.map((m) => {
67
+ const sel = m.key === cur;
68
+ return `<div class="ctrl-row"><span><strong>${t(m.nome)}</strong><br><span class="opt-hint" style="margin:0">${t(m.desc)}</span></span>`
69
+ + `<button class="mode-btn${sel ? ' is-on' : ''}" role="radio" aria-checked="${sel}" data-viz="${m.key}" type="button">${sel ? '✓ Selecionado' : 'Selecionar'}</button></div>`;
70
+ }).join('');
71
+ }
72
+ /** Fala do leitor de tela ao escolher um modo: prefixa "Jogador N:" só em multi-tela. */
73
+ export function vizGroupSay(numPlayers, sel, nome) {
74
+ return (numPlayers > 1 ? 'Jogador ' + (sel + 1) + ': ' : '') + nome + '.';
75
+ }
76
+ export function initVizSetters(ctx) {
77
+ // estáticos (mundo/parallax/moedas/itens) só re-aplicam quando o modo muda (_lastSharedViz mora no game.js)
78
+ function applySharedTextures(mode) {
79
+ if (mode !== ctx.getSharedViz()) {
80
+ ctx.setSharedViz(mode);
81
+ ctx.setFrontDim(!!DIRECT_CFG[mode]); // HC: carros/placas/semáforo (frente) escurecem como fundo
82
+ ctx.worldSprite.texture = worldTexFor(mode);
83
+ ctx.parallaxLayers.forEach((ts, j) => { ts.texture = ctx.parallaxTexFor(j, mode); });
84
+ ctx.decoSprites.forEach((s) => { s.texture = ctx.treeTexFor(mode); });
85
+ for (const s of ctx.getItemSprites()) {
86
+ if (s)
87
+ s.texture = spriteTexFor(ctx.itemTexId, mode);
88
+ }
89
+ for (const pu of ctx.getPowerups()) {
90
+ if (pu.sprite)
91
+ pu.sprite.texture = pupTexFor(pu.kind, mode);
92
+ }
93
+ }
94
+ for (const pl of ctx.getPlayers()) {
95
+ if (pl.sprite && pl._tx)
96
+ pl.sprite.texture = ctx.playerVizTex(pl._tx, mode);
97
+ } // player muda de quadro toda frame
98
+ }
99
+ // bolinhas indicadoras por viewport (sobre os sprites de saída → NÃO sofrem o filtro do viewport, ex. cegueira)
100
+ function updateVpDots() {
101
+ const dots = ctx.getVpDots(), players = ctx.getPlayers();
102
+ for (let i = 0; i < dots.length; i++) {
103
+ const g = dots[i], m = VIZ_BY_KEY[(players[i] && players[i].viz)];
104
+ if (!g)
105
+ continue;
106
+ const d = vizDotFor(m);
107
+ g.visible = d.visible;
108
+ if (d.visible) {
109
+ g.clear();
110
+ g.lineStyle(1, 0x000000, .6);
111
+ g.beginFill(d.fill);
112
+ g.drawCircle(0, 0, 5);
113
+ g.endFill();
114
+ }
115
+ }
116
+ }
117
+ function applyVpFilters() {
118
+ const spr = ctx.getVpSpr(), players = ctx.getPlayers();
119
+ for (let i = 0; i < ctx.getNumPlayers(); i++) {
120
+ if (spr[i])
121
+ spr[i].filters = ctx.pixiFilterFor(players[i].viz);
122
+ }
123
+ }
124
+ function setPlayerViz(i, mode) {
125
+ const m = resolveViz(mode);
126
+ ctx.getPlayers()[i].viz = m.key;
127
+ store.set(store.KEYS.vizP(i), m.key);
128
+ ctx.invalidateSharedViz();
129
+ if (m.kind === 'blind')
130
+ ctx.setModoCego(true); // empatia cegueira total liga o modo cego (áudio) por padrão
131
+ if (ctx.getNumPlayers() <= 1 && i === 0) {
132
+ applyVizGlobal(m.key);
133
+ }
134
+ else {
135
+ applyVpFilters();
136
+ updateVpDots();
137
+ }
138
+ ctx.reflectVizButtons();
139
+ ctx.renderVisualPanel();
140
+ ctx.renderEmpathyPanel();
141
+ }
142
+ function applyVizGlobal(mode) {
143
+ const m = resolveViz(mode);
144
+ mode = m.key;
145
+ setVizModeValue(mode); // core/state: valor + persistência (incl_viz) + evento
146
+ // ANTES daqui saía também `ctx.setHcMode(m.kind === 'hcnew')`, alimentando um `let hcMode` no game.js cujo
147
+ // único leitor era o gancho window.__incl. Era `vizMode` reescrito com outro nome: derivar de VIZ_BY_KEY
148
+ // custa uma comparação e não pode divergir. (O inicializador daquele `let` usava OUTRA fórmula,
149
+ // `vizMode!=='normal'`, e discordava do setter — sem efeito, porque applyVizGlobal roda no boot antes de
150
+ // o gancho existir, mas é o sintoma clássico de cópia de estado.)
151
+ ctx.aplicarFiltroCss(cssFilterFor(mode, lqFilter()), alcanceDoModo(mode));
152
+ ctx.aplicarAltoContrasteNoDom(m.kind === 'hcnew'); // não é filtro: ver `AplicarAltoContrasteNoDom` // sim. daltonismo/baixa-visão/cegueira + realce L/Q compostos
153
+ ctx.camera.filters = (m.kind === 'hcnew') ? ctx.pixiFilterFor(mode) : null; // solo: alto contraste experimental = filtro GPU na câmera
154
+ ctx.setFrontDim(!!DIRECT_CFG[mode]); // HC: frente (carros/placas/semáforo) escurece como fundo
155
+ ctx.worldSprite.texture = worldTexFor(mode); // alto contraste direto = Renderização Direta · resto=normal
156
+ ctx.parallaxLayers.forEach((ts, i) => { ts.texture = ctx.parallaxTexFor(i, mode); });
157
+ ctx.decoSprites.forEach((s) => { s.texture = ctx.treeTexFor(mode); });
158
+ ctx.rebuildExtras();
159
+ ctx.rebuildCoins();
160
+ // baixa visão = névoa+manchas (overlay) + bolinha verde; cegueira = tela preta (filtro) + esconde controles + bolinha branca
161
+ ctx.body.classList.toggle('lowvision-mode', m.kind === 'lowvision');
162
+ ctx.body.classList.toggle('blind-mode', m.kind === 'blind');
163
+ const ov = ctx.$('#viz-overlay');
164
+ if (ov) {
165
+ ov.hidden = (m.kind !== 'lowvision');
166
+ ov.className = lvOverlayClassFor(m);
167
+ }
168
+ if (m.kind === 'blind') {
169
+ ctx.hideTouchControls('cegueira');
170
+ }
171
+ updateVizIndicator(m.kind);
172
+ ctx.reflectVizButtons(); // (a guarda `typeof ...==='function'` do original morreu: era declaração de função, sempre verdadeira)
173
+ ctx.renderVisualPanel();
174
+ ctx.renderEmpathyPanel();
175
+ }
176
+ // bolinha indicadora (canto sup. dir.): branca=cegueira, verde=baixa visão; toque/clique 2× volta ao normal
177
+ function updateVizIndicator(kind) {
178
+ const el = ctx.$('#viz-indicator');
179
+ if (!el)
180
+ return;
181
+ const s = vizIndicatorFor(kind);
182
+ el.hidden = !s.on;
183
+ el.classList.toggle('blind', s.blind);
184
+ el.classList.toggle('low', s.low);
185
+ el.setAttribute('aria-label', s.label);
186
+ }
187
+ // MP: filtro CSS/overlay/bolinha globais OFF (por viewport agora)
188
+ function reapplyVizAll() {
189
+ ctx.invalidateSharedViz();
190
+ if (ctx.getNumPlayers() <= 1) {
191
+ applyVizGlobal(ctx.getPlayers()[0].viz);
192
+ }
193
+ else {
194
+ ctx.aplicarFiltroCss(lqFilter(), 'mundo-e-menus'); // realce L/Q é melhoria: alcança o menu
195
+ ctx.camera.filters = null;
196
+ ctx.body.classList.remove('lowvision-mode', 'blind-mode');
197
+ const ov = ctx.$('#viz-overlay');
198
+ if (ov)
199
+ ov.hidden = true;
200
+ updateVizIndicator('normal');
201
+ applyVpFilters();
202
+ }
203
+ }
204
+ // invalida os caches de textura direta (mundo depende de bg; sprites de fg) e re-renderiza
205
+ function rebakeDirect() {
206
+ clearWorldTexCache();
207
+ clearSpriteTexCache();
208
+ resetPupTexCache();
209
+ ctx.clearPlayerDirectCache();
210
+ ctx.invalidateSharedViz();
211
+ if (ctx.getNumPlayers() <= 1)
212
+ applyVizGlobal(ctx.getPlayers()[0].viz);
213
+ else
214
+ applyVpFilters();
215
+ }
216
+ function renderVizGroup(listSel, tabsSel, modes) {
217
+ const el = ctx.$(listSel);
218
+ if (!el)
219
+ return;
220
+ if (ctx.getSelVizPlayer() >= ctx.getNumPlayers())
221
+ ctx.setSelVizPlayer(0);
222
+ const tabs = ctx.$(tabsSel);
223
+ if (tabs) {
224
+ tabs.hidden = true; // E3: sem abas — cada jogador edita só o seu
225
+ tabs.innerHTML = '';
226
+ // NOTA (verbatim do original): o innerHTML acima já esvaziou `tabs`, então este querySelectorAll não
227
+ // acha nada e o listener nunca é ligado. Preservado como estava — ver relatório da extração.
228
+ tabs.querySelectorAll('button[data-vp]').forEach((b) => b.addEventListener('click', () => {
229
+ ctx.setSelVizPlayer(+b.dataset.vp);
230
+ ctx.renderVisualPanel();
231
+ ctx.renderEmpathyPanel();
232
+ }));
233
+ }
234
+ const players = ctx.getPlayers(), sel = ctx.getSelVizPlayer();
235
+ const cur = players[sel] ? players[sel].viz : 'normal';
236
+ el.innerHTML = vizGroupHtml(modes, cur);
237
+ el.querySelectorAll('button[data-viz]').forEach((btn) => btn.addEventListener('click', () => {
238
+ const key = btn.dataset.viz;
239
+ setPlayerViz(ctx.getSelVizPlayer(), key);
240
+ ctx.srSay(vizGroupSay(ctx.getNumPlayers(), ctx.getSelVizPlayer(), t(VIZ_MODES.find((m) => m.key === key).nome)));
241
+ }));
242
+ }
243
+ return { applySharedTextures, updateVpDots, applyVpFilters, setPlayerViz, applyVizGlobal, reapplyVizAll, updateVizIndicator, rebakeDirect, renderVizGroup };
244
+ }
@@ -0,0 +1,96 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ import type { DesenhoComLinha } from './port.js';
3
+ /** A single rain drop's screen-space position + falling speed. */
4
+ export interface RainDrop {
5
+ x: number;
6
+ y: number;
7
+ len: number;
8
+ spd: number;
9
+ }
10
+ /**
11
+ * L5 (rotina do José): tempo bom nos primeiros 30s; depois LOOP de 60s = garoa 5s → chuva 5s → garoa 5s → bom 45s.
12
+ * Skipped when scene decor is reduced-motion.
13
+ *
14
+ * QUEM TEM CHUVA É DADO DO TEMA (`CENARIOS[...].chuva`), e não uma condição escrita aqui. Era
15
+ * `cenario === 'cidade'` — verdade enquanto a Cidade era o único tema com chuva, e mentira no dia em que o Dev
16
+ * pediu chuva na Floresta. O defeito de uma condição dessas não é estar errada, é ser INVISÍVEL de onde a
17
+ * pessoa procura: quem abre a tabela de cenários para dar chuva a um tema novo não encontra nada para mudar.
18
+ *
19
+ * @param sec elapsed weather-clock time, in seconds
20
+ * @param temChuva true when the active theme declares `chuva`
21
+ * @param reduceDecor true when reduced-motion scene decor (rm.decor) is on
22
+ * @returns target rain level: 0 (dry), 0.35 (garoa) or 1 (chuva)
23
+ */
24
+ export declare function rainLevelTarget(sec: number, temChuva: boolean, reduceDecor: boolean): number;
25
+ /** Onde estamos DENTRO do ciclo de 60s, em segundos. 0 = a primeira gota; 59 = um segundo antes dela. */
26
+ export declare function faseDoClima(sec: number): number;
27
+ /**
28
+ * AGLOMERAÇÃO DAS NUVENS: 0 = espalhadas e altas, com o céu aparecendo entre elas; 1 = fechadas numa manta.
29
+ *
30
+ * O pedido do Dev foi de um MOVIMENTO, não de um estado: as nuvens se juntam, chove, elas se separam e o pôr
31
+ * do sol aparece nas frestas. Quem faz isso ler como causa e não como coincidência é a ANTECEDÊNCIA — a manta
32
+ * fecha OITO SEGUNDOS ANTES da primeira gota. Se fechasse junto, a chuva pareceria vir do nada e as nuvens
33
+ * pareceriam reagir a ela; fechando antes, é a nuvem que traz a chuva, que é o que uma criança já sabe.
34
+ *
35
+ * Anda no MESMO ciclo da chuva (`faseDoClima`), e não num relógio próprio, porque dois relógios independentes
36
+ * desandam um do outro — não de imediato, mas depois de alguns minutos, e aí ninguém liga o defeito à causa.
37
+ * Devolve uma rampa contínua: não precisa de suavização depois.
38
+ */
39
+ export declare function aglomeracaoAlvo(sec: number, temChuva: boolean, reduceDecor: boolean): number;
40
+ /** Ramps `current` toward `target` by at most `step`, snapping to `target` once within 0.02 (no "quase seco" flicker). */
41
+ export declare function rampRainLevel(current: number, target: number, step: number): number;
42
+ /** Thunder cooldown/flash step (só na chuva forte, > 0.45). Calls `onThunder(inten)` — e.g. platform/audio-ambient's `thunder`. */
43
+ export interface ThunderStep {
44
+ thunderCD: number;
45
+ flash: number;
46
+ }
47
+ export declare function stepThunder(rainLevel: number, thunderCD: number, flash: number, rndFn: () => number, onThunder: (inten: number) => void): ThunderStep;
48
+ /** Seeds `n` rain drops at random positions across the WxH screen (lazy — built once on first rain). */
49
+ export declare function makeRainDrops(n: number, W: number, H: number, rndFn: () => number): RainDrop[];
50
+ /** Advances one drop by its own speed; wraps to the top (new random x) past H. GAG: drops freeze when `moving` is false (pause). */
51
+ export declare function stepRainDrop(d: RainDrop, W: number, H: number, moving: boolean, rndFn: () => number): void;
52
+ type Gfx = DesenhoComLinha & {
53
+ parent: unknown;
54
+ };
55
+ interface StageLike {
56
+ children: {
57
+ length: number;
58
+ };
59
+ setChildIndex(child: unknown, index: number): void;
60
+ }
61
+ export interface WeatherCtx {
62
+ /** The PIXI.Graphics layer this module draws into (created + z-ordered in game.js, kept on top of the stage). */
63
+ weatherLayer: Gfx;
64
+ /** The PIXI.Container `weatherLayer` lives on — used only to re-pin it as the topmost child every draw. */
65
+ stage: StageLike;
66
+ /** Live screen size (pass `app.screen`; width/height read fresh on every draw()). */
67
+ screen: {
68
+ width: number;
69
+ height: number;
70
+ };
71
+ /** Reduced-motion flags; only `.decor` (scene decor/animation) matters here. */
72
+ getRm: () => {
73
+ decor?: boolean;
74
+ };
75
+ /** Called when thunder should rumble (platform/audio-ambient's `ambient.thunder`). */
76
+ thunder: (inten: number) => void;
77
+ /** Chove no cenário atual? Uma PERGUNTA e não o id do cenário: quem tem o catálogo responde. */
78
+ temChuva: () => boolean;
79
+ /** O mundo está rodando? Um BOOLEANO e não a fase — a engine não conhece o vocabulário de cenas deste
80
+ * jogo (ADR-0030 C3). Era `phase === 'playing'`, importado de `core/state`. É o que congela as gotas na
81
+ * pausa: elas param no ar em vez de continuarem caindo por trás do menu. */
82
+ mundoRodando: () => boolean;
83
+ }
84
+ export declare function initWeather(ctx: WeatherCtx): void;
85
+ /** Per-frame clima update (game.js calls this only while `phase==='playing'`, same as before). */
86
+ export declare function updateWeather(): void;
87
+ /** Redraws the rain/flash overlay (chuva/clarão em tela-espaço, sobre tudo). */
88
+ export declare function drawWeather(): void;
89
+ /** The clima→áudio bridge: platform/audio-ambient.ts reads this (never the raw value) so its rain track follows the visual. */
90
+ export declare function getRainLevel(): number;
91
+ /** A ponte clima→céu: render/scene-sky lê isto para juntar e separar as nuvens no compasso da chuva. */
92
+ export declare function getAglomeracao(): number;
93
+ /** Weather-clock read/write — the game.js `window.__incl.weatherT` debug hook fast-forwards the cycle via this. */
94
+ export declare function getWeatherT(): number;
95
+ export declare function setWeatherT(v: number): void;
96
+ export {};