@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,171 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // render/weather — visual CLIMA: rain intensity ramp, thunder cadence/flash, and the rain-drop layer drawn in
3
+ // screen-space on top of everything. Extracted from game.js's updateWeather/drawWeather (behavior-preserving,
4
+ // formulas verbatim). `weatherLayer` (the PIXI.Graphics) and `stage` are CREATED in game.js — the layer's z-order
5
+ // is soldered into the render-graph assembly there, same precedent as render/scene-sky.ts — and are INJECTED via
6
+ // initWeather(); only the logic moves. `_rainLevel` is the bridge to platform/audio-ambient.ts (thunder/rain
7
+ // track follow the visual): exposed here ONLY via getRainLevel(), never the raw mutable value, exactly like the
8
+ // game.js `get rainLevel(){return _rainLevel;}` it replaces. `cenario` is read as a live binding from
9
+ // core/state.ts (shared state home); `rnd` from core/rng.ts's DECORATION stream — same pattern as render/fx.ts.
10
+ // A corrente da DECORACAO, nao a do jogo: uma particula sorteada aqui nao pode mover o sorteio
11
+ // das moedas. Ver o cabecalho de core/rng.ts (issue #107).
12
+ import { rngDecoracao } from '../core/rng.js';
13
+ const rnd = rngDecoracao.rnd;
14
+ /**
15
+ * L5 (rotina do José): tempo bom nos primeiros 30s; depois LOOP de 60s = garoa 5s → chuva 5s → garoa 5s → bom 45s.
16
+ * Skipped when scene decor is reduced-motion.
17
+ *
18
+ * QUEM TEM CHUVA É DADO DO TEMA (`CENARIOS[...].chuva`), e não uma condição escrita aqui. Era
19
+ * `cenario === 'cidade'` — verdade enquanto a Cidade era o único tema com chuva, e mentira no dia em que o Dev
20
+ * pediu chuva na Floresta. O defeito de uma condição dessas não é estar errada, é ser INVISÍVEL de onde a
21
+ * pessoa procura: quem abre a tabela de cenários para dar chuva a um tema novo não encontra nada para mudar.
22
+ *
23
+ * @param sec elapsed weather-clock time, in seconds
24
+ * @param temChuva true when the active theme declares `chuva`
25
+ * @param reduceDecor true when reduced-motion scene decor (rm.decor) is on
26
+ * @returns target rain level: 0 (dry), 0.35 (garoa) or 1 (chuva)
27
+ */
28
+ export function rainLevelTarget(sec, temChuva, reduceDecor) {
29
+ if (!(sec >= 30 && !reduceDecor && temChuva))
30
+ return 0;
31
+ const c = (sec - 30) % 60;
32
+ return c < 5 ? 0.35 : c < 10 ? 1 : c < 15 ? 0.35 : 0;
33
+ }
34
+ /** Onde estamos DENTRO do ciclo de 60s, em segundos. 0 = a primeira gota; 59 = um segundo antes dela. */
35
+ export function faseDoClima(sec) { return (((sec - 30) % 60) + 60) % 60; }
36
+ /**
37
+ * AGLOMERAÇÃO DAS NUVENS: 0 = espalhadas e altas, com o céu aparecendo entre elas; 1 = fechadas numa manta.
38
+ *
39
+ * 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
40
+ * do sol aparece nas frestas. Quem faz isso ler como causa e não como coincidência é a ANTECEDÊNCIA — a manta
41
+ * fecha OITO SEGUNDOS ANTES da primeira gota. Se fechasse junto, a chuva pareceria vir do nada e as nuvens
42
+ * pareceriam reagir a ela; fechando antes, é a nuvem que traz a chuva, que é o que uma criança já sabe.
43
+ *
44
+ * Anda no MESMO ciclo da chuva (`faseDoClima`), e não num relógio próprio, porque dois relógios independentes
45
+ * desandam um do outro — não de imediato, mas depois de alguns minutos, e aí ninguém liga o defeito à causa.
46
+ * Devolve uma rampa contínua: não precisa de suavização depois.
47
+ */
48
+ export function aglomeracaoAlvo(sec, temChuva, reduceDecor) {
49
+ if (!(sec >= 22 && !reduceDecor && temChuva))
50
+ return 0; // 22s = exatamente a fase 52, onde a rampa começa
51
+ const f = faseDoClima(sec);
52
+ if (f >= 52)
53
+ return (f - 52) / 8; // 8s juntando, antes da primeira gota
54
+ if (f < 12)
55
+ return 1; // fechado durante a garoa e a chuva
56
+ if (f < 22)
57
+ return 1 - (f - 12) / 10; // 10s abrindo — é aqui que o pôr do sol reaparece
58
+ return 0;
59
+ }
60
+ /** Ramps `current` toward `target` by at most `step`, snapping to `target` once within 0.02 (no "quase seco" flicker). */
61
+ export function rampRainLevel(current, target, step) {
62
+ let next = current + Math.max(-step, Math.min(step, target - current));
63
+ if (Math.abs(target - next) < 0.02)
64
+ next = target;
65
+ return next;
66
+ }
67
+ export function stepThunder(rainLevel, thunderCD, flash, rndFn, onThunder) {
68
+ if (rainLevel > 0.45) {
69
+ thunderCD--;
70
+ if (thunderCD <= 0) {
71
+ thunderCD = 200 + Math.floor(rndFn() * 420);
72
+ const inten = 0.35 + rndFn() * 0.65;
73
+ flash = Math.max(flash, inten);
74
+ onThunder(inten);
75
+ }
76
+ }
77
+ if (flash > 0)
78
+ flash = Math.max(0, flash - 0.05);
79
+ return { thunderCD, flash };
80
+ }
81
+ /** Seeds `n` rain drops at random positions across the WxH screen (lazy — built once on first rain). */
82
+ export function makeRainDrops(n, W, H, rndFn) {
83
+ const drops = [];
84
+ for (let i = 0; i < n; i++)
85
+ drops.push({ x: rndFn() * W, y: rndFn() * H, len: 6 + rndFn() * 9, spd: 8 + rndFn() * 7 });
86
+ return drops;
87
+ }
88
+ /** Advances one drop by its own speed; wraps to the top (new random x) past H. GAG: drops freeze when `moving` is false (pause). */
89
+ export function stepRainDrop(d, W, H, moving, rndFn) {
90
+ if (!moving)
91
+ return;
92
+ d.y += d.spd;
93
+ d.x -= d.spd * 0.35;
94
+ if (d.y > H) {
95
+ d.y = -d.len;
96
+ d.x = rndFn() * W;
97
+ }
98
+ if (d.x < 0)
99
+ d.x += W;
100
+ }
101
+ let _weatherLayer = null;
102
+ let _stage = null;
103
+ let _screen = null;
104
+ let _getRm = null;
105
+ let _thunder = null;
106
+ let _temChuva = null;
107
+ let _mundoRodando = () => true;
108
+ let _rainLevel = 0, _weatherT = 0, _flash = 0, _thunderCD = 240, _aglomeracao = 0;
109
+ let _rainDrops = null;
110
+ export function initWeather(ctx) {
111
+ _weatherLayer = ctx.weatherLayer;
112
+ _stage = ctx.stage;
113
+ _screen = ctx.screen;
114
+ _getRm = ctx.getRm;
115
+ _thunder = ctx.thunder;
116
+ _temChuva = ctx.temChuva;
117
+ _mundoRodando = ctx.mundoRodando;
118
+ }
119
+ /** Per-frame clima update (game.js calls this only while `phase==='playing'`, same as before). */
120
+ export function updateWeather() {
121
+ _weatherT++;
122
+ const sec = _weatherT / 60;
123
+ const rm = _getRm ? _getRm() : {};
124
+ const temChuva = _temChuva ? _temChuva() : false;
125
+ const target = rainLevelTarget(sec, temChuva, !!rm.decor);
126
+ _rainLevel = rampRainLevel(_rainLevel, target, 1 / 30); // rampa ~1s
127
+ _aglomeracao = aglomeracaoAlvo(sec, temChuva, !!rm.decor); // já é rampa: nada a suavizar aqui
128
+ const step = stepThunder(_rainLevel, _thunderCD, _flash, rnd, (inten) => { if (_thunder)
129
+ _thunder(inten); });
130
+ _thunderCD = step.thunderCD;
131
+ _flash = step.flash;
132
+ }
133
+ /** Redraws the rain/flash overlay (chuva/clarão em tela-espaço, sobre tudo). */
134
+ export function drawWeather() {
135
+ if (!_weatherLayer)
136
+ return;
137
+ const g = _weatherLayer;
138
+ if (_stage && g.parent === _stage)
139
+ _stage.setChildIndex(g, _stage.children.length - 1); // mantido no topo em draw
140
+ g.clear();
141
+ const W = _screen ? _screen.width : 0, H = _screen ? _screen.height : 0;
142
+ if (_rainLevel <= 0 && _flash <= 0)
143
+ return;
144
+ if (_rainLevel > 0) {
145
+ g.beginFill(0x0a0e1a, _rainLevel * 0.34);
146
+ g.drawRect(0, 0, W, H);
147
+ g.endFill(); // céu mais escuro
148
+ if (!_rainDrops)
149
+ _rainDrops = makeRainDrops(110, W, H, rnd);
150
+ const mv = _mundoRodando(); // GAG: gotas congelam na pausa
151
+ g.lineStyle(1, 0xaebfe0, 0.5 * _rainLevel);
152
+ for (const d of _rainDrops) {
153
+ stepRainDrop(d, W, H, mv, rnd);
154
+ g.moveTo(d.x, d.y);
155
+ g.lineTo(d.x - 2, d.y + d.len);
156
+ }
157
+ g.lineStyle(0);
158
+ }
159
+ if (_flash > 0) {
160
+ g.beginFill(0xe4ecff, _flash * 0.55);
161
+ g.drawRect(0, 0, W, H);
162
+ g.endFill();
163
+ } // clarão do relâmpago
164
+ }
165
+ /** The clima→áudio bridge: platform/audio-ambient.ts reads this (never the raw value) so its rain track follows the visual. */
166
+ export function getRainLevel() { return _rainLevel; }
167
+ /** A ponte clima→céu: render/scene-sky lê isto para juntar e separar as nuvens no compasso da chuva. */
168
+ export function getAglomeracao() { return _aglomeracao; }
169
+ /** Weather-clock read/write — the game.js `window.__incl.weatherT` debug hook fast-forwards the cycle via this. */
170
+ export function getWeatherT() { return _weatherT; }
171
+ export function setWeatherT(v) { _weatherT = v; }
@@ -0,0 +1,24 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ import type { PlayerView } from '../core/entity.js';
3
+ /** Minimal PIXI.Graphics surface these draws use (structural → module stays PIXI-free for node tests). */
4
+ export interface DrawGraphics {
5
+ lineStyle(width: number, color?: number): unknown;
6
+ moveTo(x: number, y: number): unknown;
7
+ lineTo(x: number, y: number): unknown;
8
+ beginFill(color: number): unknown;
9
+ endFill(): unknown;
10
+ drawCircle(x: number, y: number, r: number): unknown;
11
+ }
12
+ /** Cadeira e bengala: onde o jogador está, para que lado olha, e em que modo de visão desenhar. */
13
+ type DrawablePlayer = PlayerView<'facing' | 'x' | 'y' | 'viz'>;
14
+ /** Cane color: green for low-vision, white otherwise (blind). */
15
+ export declare function caneColor(pl: {
16
+ viz: string;
17
+ }): number;
18
+ /** Rigid half-block cane (~8px): front-hand extension, fixed to the body (does not swing on its own). */
19
+ export declare function drawCane(g: DrawGraphics, pl: DrawablePlayer): void;
20
+ /** Running cane (item): a wheel at the tip, constant ground contact (high performance). */
21
+ export declare function drawRunCane(g: DrawGraphics, pl: DrawablePlayer): void;
22
+ /** The wheelchair (big spoked wheel + backrest + footrest), drawn under the seated player. */
23
+ export declare function drawChair(g: DrawGraphics, pl: DrawablePlayer): void;
24
+ export {};
@@ -0,0 +1,63 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // render/wheelchair-sprites — the a11y mobility props (Estágio 4, Tier 2): the white/green cane (blind /
3
+ // low-vision), the running cane (item), and the wheelchair. Stateless PIXI drawing (verbatim from game.js):
4
+ // each takes a Graphics `g` + the player. The layers (caneLayer/chairLayer) stay in game.js and are passed in.
5
+ // `caneColor` (pure) moves here too — it was only used by the cane draws. See docs/5-Refactoring/plano-modularizacao-mapa.md.
6
+ import { VIZ_BY_KEY } from './viz-modes.js';
7
+ /** Cane color: green for low-vision, white otherwise (blind). */
8
+ export function caneColor(pl) {
9
+ const m = VIZ_BY_KEY[pl.viz];
10
+ return m && m.kind === 'lowvision' ? 0x35d06a : 0xf2f2f2;
11
+ }
12
+ /** Rigid half-block cane (~8px): front-hand extension, fixed to the body (does not swing on its own). */
13
+ export function drawCane(g, pl) {
14
+ const dir = pl.facing < 0 ? -1 : 1, C = caneColor(pl);
15
+ const hx = pl.x + dir * 4, hy = pl.y - 9, tx = pl.x + dir * 8, ty = pl.y - 1; // punho na mão → ponteira no chão à frente
16
+ g.lineStyle(2, C);
17
+ g.moveTo(hx, hy);
18
+ g.lineTo(tx, ty); // haste curta (branca=cego / verde=baixa visão)
19
+ g.lineStyle(1, 0xd23b3b);
20
+ g.moveTo(tx - dir * 2, ty - 2);
21
+ g.lineTo(tx, ty); // faixa vermelha (ponta)
22
+ g.lineStyle(0);
23
+ g.beginFill(C);
24
+ g.drawCircle(tx, ty, 1.3);
25
+ g.endFill(); // ponteira
26
+ }
27
+ /** Running cane (item): a wheel at the tip, constant ground contact (high performance). */
28
+ export function drawRunCane(g, pl) {
29
+ const dir = pl.facing < 0 ? -1 : 1, C = caneColor(pl);
30
+ const hx = pl.x + dir * 4, hy = pl.y - 9, wx = pl.x + dir * 10, wy = pl.y - 2; // haste até o eixo da roda, à frente
31
+ g.lineStyle(2, C);
32
+ g.moveTo(hx, hy);
33
+ g.lineTo(wx, wy - 1);
34
+ g.lineStyle(1.5, C);
35
+ g.drawCircle(wx, wy, 2.4); // RODA na ponta (contato constante)
36
+ g.lineStyle(1, 0xd23b3b);
37
+ g.moveTo(wx - 2.4, wy);
38
+ g.lineTo(wx + 2.4, wy);
39
+ g.lineStyle(0); // eixo/faixa
40
+ }
41
+ /** The wheelchair (big spoked wheel + backrest + footrest), drawn under the seated player. */
42
+ export function drawChair(g, pl) {
43
+ const cx = pl.x, base = pl.y, f = pl.facing < 0 ? -1 : 1, MET = 0x4a586e, HUB = 0x1c2230;
44
+ g.lineStyle(2, MET);
45
+ g.drawCircle(cx, base - 6, 7); // roda grande
46
+ g.lineStyle(1, MET);
47
+ for (let a = 0; a < 6; a++) {
48
+ const an = a * Math.PI / 3;
49
+ g.moveTo(cx, base - 6);
50
+ g.lineTo(cx + Math.cos(an) * 6, base - 6 + Math.sin(an) * 6);
51
+ } // raios
52
+ g.lineStyle(0);
53
+ g.beginFill(HUB);
54
+ g.drawCircle(cx, base - 6, 2);
55
+ g.endFill(); // cubo
56
+ g.lineStyle(3, MET);
57
+ g.moveTo(cx - f * 5, base - 6);
58
+ g.lineTo(cx - f * 5, base - 22);
59
+ g.lineTo(cx - f * 2, base - 24); // encosto
60
+ g.moveTo(cx + f * 4, base - 4);
61
+ g.lineTo(cx + f * 9, base - 2); // apoio de pés
62
+ g.lineStyle(0);
63
+ }
@@ -0,0 +1,18 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ /** Inject the loaded level grid + dimensions (from game.js, after the map fetch). */
3
+ export declare function initWorldTex(ctx: {
4
+ world: number[][];
5
+ W: number;
6
+ H: number;
7
+ }): void;
8
+ /** Ground/platform tiles that receive the theme tileset. */
9
+ export declare const isGroundType: (t: number) => boolean;
10
+ /** Optional theme tileset: `fill` (interior) + `surface` (top). */
11
+ export interface Tileset {
12
+ fill: CanvasImageSource;
13
+ surface: CanvasImageSource;
14
+ }
15
+ /** Build the NORMAL world canvas: theme tileset on ground (if given), else the v3 per-tile drawings. */
16
+ export declare function worldCanvas(tiles?: Tileset | null): HTMLCanvasElement;
17
+ /** The NORMAL world texture (PIXI) from the current level. */
18
+ export declare function worldToTexture(tiles?: Tileset | null): unknown;
@@ -0,0 +1,87 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // render/world-tex — the NORMAL world tile texture builder (Estágio 4, Tier 2). Draws the whole level to a
3
+ // canvas (theme tileset on ground, else the v3 per-tile art). Verbatim from game.js. The loaded level grid is
4
+ // runtime state (fetched at boot) → injected via initWorldTex, like core/coins. The high-contrast variant
5
+ // (worldToTextureDirect) + the viz selector (worldTexFor) + animated water/lava (stepTileFx) stay in game.js.
6
+ // See docs/5-Refactoring/plano-modularizacao-mapa.md.
7
+ import { makeCanvas, tex } from './canvas.js';
8
+ import { TILE, TILE_COLOR } from '../core/constants.js';
9
+ import { isSolidType } from '../core/collision.js';
10
+ let WORLD = [];
11
+ let W = 0, H = 0, PXW = 0, PXH = 0;
12
+ /** Inject the loaded level grid + dimensions (from game.js, after the map fetch). */
13
+ export function initWorldTex(ctx) {
14
+ WORLD = ctx.world;
15
+ W = ctx.W;
16
+ H = ctx.H;
17
+ PXW = W * TILE;
18
+ PXH = H * TILE;
19
+ }
20
+ /** Ground/platform tiles that receive the theme tileset. */
21
+ export const isGroundType = (t) => t === 2 || t === 6;
22
+ /** Build the NORMAL world canvas: theme tileset on ground (if given), else the v3 per-tile drawings. */
23
+ export function worldCanvas(tiles) {
24
+ const cv = makeCanvas(PXW, PXH), c = cv.getContext('2d');
25
+ const solidAt = (x, y) => y >= 0 && y < H && x >= 0 && x < W && isSolidType(WORLD[y][x]);
26
+ for (let y = 0; y < H; y++)
27
+ for (let x = 0; x < W; x++) {
28
+ const t = WORLD[y][x];
29
+ if (t === 0 || t === 1)
30
+ continue; // ar/interior: transparente → o parallax aparece pela área jogável
31
+ if (tiles && isGroundType(t)) { // tileset: superfície (topo claro) se há ar acima, senão preenchimento
32
+ c.drawImage(solidAt(x, y - 1) ? tiles.fill : tiles.surface, x * TILE, y * TILE);
33
+ continue;
34
+ }
35
+ // DESENHOS DA V3 (drawTile); água/lava animadas ficam no stepTileFx (game.js)
36
+ const X = x * TILE, Y = y * TILE;
37
+ if (t === 2) {
38
+ c.fillStyle = '#555';
39
+ c.fillRect(X, Y, TILE, TILE); // pedra + pontos escuros
40
+ c.fillStyle = '#3a3a3a';
41
+ c.fillRect(X + 2, Y + 3, 1, 1);
42
+ c.fillRect(X + 9, Y + 5, 1, 1);
43
+ c.fillRect(X + 5, Y + 11, 1, 1);
44
+ c.fillRect(X + 12, Y + 9, 1, 1);
45
+ }
46
+ else if (t === 6) {
47
+ c.fillStyle = '#666';
48
+ c.fillRect(X, Y, TILE, TILE); // parede dura + linhas
49
+ c.fillStyle = '#444';
50
+ c.fillRect(X, Y, TILE, 1);
51
+ c.fillRect(X, Y + TILE - 1, TILE, 1);
52
+ }
53
+ else if (t === 4) {
54
+ c.fillStyle = '#777'; // escada VAZADA: trilhos + degraus
55
+ c.fillRect(X + 3, Y, 2, TILE);
56
+ c.fillRect(X + 11, Y, 2, TILE);
57
+ c.fillRect(X + 3, Y + 2, 10, 2);
58
+ c.fillRect(X + 3, Y + 8, 10, 2);
59
+ c.fillRect(X + 3, Y + 14, 10, 2);
60
+ }
61
+ else if (t === 5) {
62
+ c.fillStyle = '#E373FA';
63
+ c.fillRect(X, Y, TILE, TILE); // trampolim SÓLIDO (bloco inteiro)
64
+ c.fillStyle = '#fff';
65
+ c.fillRect(X + 1, Y + 2, TILE - 2, 1);
66
+ c.fillStyle = '#9a3fb0';
67
+ c.fillRect(X, Y + TILE - 2, TILE, 2);
68
+ }
69
+ else if (t === 3) {
70
+ c.fillStyle = 'rgba(121,220,242,0.4)';
71
+ c.fillRect(X, Y, TILE, TILE);
72
+ } // água translúcida
73
+ else if (t === 9) {
74
+ c.fillStyle = '#C93232';
75
+ c.fillRect(X, Y, TILE, TILE);
76
+ } // lava (tracinhos no stepTileFx)
77
+ else {
78
+ c.fillStyle = TILE_COLOR[t] || '#202';
79
+ c.fillRect(X, Y, TILE, TILE);
80
+ }
81
+ }
82
+ return cv;
83
+ }
84
+ /** The NORMAL world texture (PIXI) from the current level. */
85
+ export function worldToTexture(tiles) {
86
+ return tex(worldCanvas(tiles));
87
+ }
@@ -0,0 +1,258 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ import { type ActivityDef, type ActivityCat } from '../educational/activities-registry.js';
3
+ import type { TitleMenuId } from './title.js';
4
+ export type { ActivityCat, GameMode } from '../educational/activities-registry.js';
5
+ export { activityCategory, modeForCategory } from '../educational/activities-registry.js';
6
+ /** The five fraction notations the "Fração" submenu toggles. */
7
+ export type FracNotKey = 'v' | 'd' | 'dec' | 'pct' | 'mix';
8
+ /** On/off state per notation, stored as 0/1 (game.js persisted numbers, not booleans — kept verbatim). */
9
+ export type FracNot = Record<FracNotKey, number>;
10
+ /** The 6 directional/confirm flags the title menu reacts to. Single definition in input/edges. */
11
+ import type { NavKeys } from '../input/edges.js';
12
+ import type { DomQuery } from '../core/dom-query.js';
13
+ export type { NavKeys } from '../input/edges.js';
14
+ /** One scenario as the "Cenário" submenu needs it — id + display name only (CENARIOS' texture data stays put). */
15
+ export interface CenarioOption {
16
+ readonly id: string;
17
+ readonly nome: string;
18
+ }
19
+ /** Minimal DOM-selector shape (matches ui/dom.ts's `$`). */
20
+ export type { DomQuery } from '../core/dom-query.js';
21
+ export interface ActivitiesMenuCtx {
22
+ /**
23
+ * O ID DO JOGO, para escopar as duas chaves que pertencem a ESTA partida (`tabsel`, `fracnot`).
24
+ *
25
+ * ⚠️ Entra pelo ctx e não por constante porque a engine não pode tê-lo (ADR-0080): este menu serve
26
+ * qualquer jogo do catálogo, e duas partidas diferentes que escolhessem a mesma tabuada no mesmo perfil de
27
+ * navegador escreviam uma por cima da outra. Quem sabe o id é o jogo, e ele o passa aqui.
28
+ */
29
+ gameId: string;
30
+ /** Quantos jogadores/telas. Estado de RODADA (ADR-0038): vem da instância que a raiz possui.
31
+ * Era `numPlayers`, um `let` de `core/state` importado como binding vivo — e um `let` de módulo
32
+ * é compartilhado por qualquer segundo jogo que a mesma página carregue (D13 do `demos`). */
33
+ getNumPlayers: () => number;
34
+ /** Os jogadores. Estado de RODADA, pelo mesmo motivo. `readonly unknown[]` porque cada consumidor
35
+ * estreita para a SUA fatia — o tipo real é do jogo, não da engine (ADR-0033). */
36
+ getPlayers: () => readonly unknown[];
37
+ /** DOM selector (ui/dom.ts `$`) — injected so the node project can hand over fake elements. */
38
+ $: DomQuery;
39
+ /** `document.activeElement`, injected: navTitle needs the focused button and node has no document. */
40
+ getActiveElement: () => Element | null;
41
+ /** Polite screen-reader announcement (core/a11y-sr.ts `srSay`). Injected: it reaches `document` when called. */
42
+ srSay: (text: string) => void;
43
+ /** Assertive announcement (core/a11y-sr.ts `srAlert`) — the three refusals of this menu use it. */
44
+ srAlert: (text: string) => void;
45
+ /** ui/title.ts's `show()` — game.js already owns the initTitle({ $ }) instance; we reuse it, never rebuild it. */
46
+ titleShow: (which: TitleMenuId) => void;
47
+ /** The scenarios of the "Cenário" submenu, in menu order. Source is game.js's CENARIOS, whose other fields
48
+ * (sky/cloud/hills/decor) are parallax TEXTURE data — that belongs to the render slice, not to a menu. */
49
+ cenarios: readonly CenarioOption[];
50
+ /** Applies the chosen scenario (game.js: validates, persists via core/state, rebuilds the parallax textures). */
51
+ setCenario: (theme: string) => void;
52
+ /** A ATIVIDADE ESCOLHIDA, por injeção. É estado do JOGO (`game/state`), e este módulo é engine — o gate
53
+ * de fronteira proíbe a importação, então a raiz de composição entrega. */
54
+ getActivityId: () => string;
55
+ setActivityId: (id: string) => void;
56
+ /** game.js's setQuizLevel(n, announce): persists via core/state and refreshes the `.pm-nivel` pause labels.
57
+ * Stays in game.js because the pause menu — not this menu — owns those labels. */
58
+ setQuizLevel: (n: number, announce: boolean) => void;
59
+ /** Coarse-pointer detection (game.js): on mobile the round is forced to a single screen + fullscreen. */
60
+ isMobile: () => boolean;
61
+ /** "Do N viewports still fit this window?" — game.js's fitsN, which mirrors layout()'s arithmetic. */
62
+ fitsN: (n: number) => boolean;
63
+ /** Rebuilds the player list + viewports for N screens (game.js). Also restarts the round internally. */
64
+ setNumPlayers: (n: number) => void;
65
+ /** Restarts the round in place (game.js) — the branch taken when the screen count did not change. */
66
+ restartGame: () => void;
67
+ /** Phase transition (game.js setPhase, which also shows/hides overlays and the HUD). */
68
+ setPhase: (p: 'title' | 'playing' | 'paused') => void;
69
+ /** Start-hints dismissal (game.js). Today a no-op stub kept for its call sites — see the extraction notes. */
70
+ hideTips: () => void;
71
+ /** Requests fullscreen on the document element (game.js keeps the vendor-prefix try/catch verbatim). */
72
+ enterFullscreen: () => void;
73
+ }
74
+ export interface ActivitiesMenuApi {
75
+ /** Category of the CURRENT activity ('ludico' when unknown) — read by the quiz/win flow in game.js. */
76
+ actCat: () => ActivityCat;
77
+ /** Validates + commits an activity id: state, quiz level (literacy) and MODE. */
78
+ setActivity: (id: string) => void;
79
+ /** Step 1 of starting: remembers the pick, computes the "Voltar" target, opens the scenario submenu. */
80
+ startActivity: (id: string) => void;
81
+ /** Step 2: commits the pending activity and actually starts the round. */
82
+ reallyStart: () => void;
83
+ /** (Re)writes the markup of the alf/mat/fr/tab/cen submenus. */
84
+ buildTitleMenus: () => void;
85
+ /** The buttons of the ONE visible title submenu, in DOM order. */
86
+ titleButtons: () => HTMLElement[];
87
+ /** Arrow/confirm/back traversal of the visible submenu (keyboard + gamepad). */
88
+ navTitle: (k: NavKeys) => void;
89
+ /** Tabuada numbers, LIVE array — mutated in place (splice/push), never reassigned, exactly as in game.js. */
90
+ readonly tabSel: number[];
91
+ /** Fraction notations, LIVE object — mutated in place, never reassigned (window.__incl reads it). */
92
+ readonly fracNot: FracNot;
93
+ /** Where "Voltar" on the scenario submenu goes right now (the `_cenBack` latch). */
94
+ getCenBack: () => TitleMenuId;
95
+ /** The player count staged on the splash (only reallyStart applies it). */
96
+ getPendingPlayers: () => number;
97
+ }
98
+ /** The five literacy levels of the Ferreiro psychogenesis, by number. Domain content (pt-BR) — read by educators. */
99
+ export declare const QL_NAME: Readonly<Record<number, string>>;
100
+ /** Which literacy activity trains which of the 5 levels — setActivity reuses the psychogenesis ladder. */
101
+ export declare const ALF_LEVEL: Readonly<Record<string, number>>;
102
+ /** One row of the per-screen pause menu. `letra` marks the label that ABC/abc rewrites live. */
103
+ export interface PauseBtnDef {
104
+ readonly act: string;
105
+ /**
106
+ * Rótulo CRU, e ele só é usado quando o botão tem rótulo dinâmico (`letra`/`nivel`).
107
+ *
108
+ * Virou opcional, e o motivo é o gate de i18n: `pmBtnMarkup` resolve todo botão comum por `t('pause.' +
109
+ * act)`, então o texto aqui nunca ia para tela nenhuma — era português cru dentro de um módulo de ENGINE,
110
+ * parado, esperando alguém confiar nele. Nenhuma entrada de hoje marca `letra` ou `nivel`, então nenhuma
111
+ * precisa dele; a chave `pause.*` é a fonte, e ela existe nos três idiomas.
112
+ */
113
+ readonly lbl?: string;
114
+ readonly letra?: boolean;
115
+ readonly nivel?: boolean;
116
+ }
117
+ /** The pause menu's items — MENU DATA, so it lives with the other menu tables. The pause slice (ui/pause-icons)
118
+ * receives it through its own ctx instead of re-declaring it; nobody owns two copies of a list of buttons. */
119
+ export declare const PM_BTNS: readonly PauseBtnDef[];
120
+ /**
121
+ * O SUBMENU DE OPÇÕES — os sete painéis que saíram da lista raiz, na ordem que o Dev ditou.
122
+ *
123
+ * A saída vem PRIMEIRO aqui também, pela mesma razão que `resume` vem primeiro lá: a regra do ADR-0044 é
124
+ * sobre menus, não sobre um menu. Um submenu de onde não se sai é a mesma armadilha, um nível abaixo.
125
+ *
126
+ * 'letra' virou 'caa' (ADR-0028): era um CICLO de duas posições cujo rótulo mudava junto (`letra: true`), e
127
+ * virou a porta de um menu. Sem ciclo não há rótulo dinâmico, então ele volta a ser traduzível como os
128
+ * irmãos — o `letra: true` existia só para o i18n não sobrescrever o ABC/abc que o ciclo escrevia.
129
+ */
130
+ export declare const PM_OPTIONS_BTNS: readonly PauseBtnDef[];
131
+ /** i18n KEY of the spoken/aria name of each fraction notation. Keys, not text: a module-level const is
132
+ * evaluated once at import and would freeze the language at boot (see the note in input/devices). */
133
+ export declare const FNOT_LBL: Readonly<Record<FracNotKey, string>>;
134
+ /** Visible label = the notation ITSELF (x/y), so the five toggles fit side by side; aria-label keeps the word. */
135
+ export declare const FNOT_SYM: Readonly<Record<FracNotKey, string>>;
136
+ /** i18n KEY of the footer text (same style as the pause menu) shown on focus/hover of a notation toggle. */
137
+ export declare const FNOT_DESC: Readonly<Record<FracNotKey, string>>;
138
+ /** The notation keys in menu order — also the defaults' key order (`v` on, rest off). */
139
+ export declare const FNOT_KEYS: readonly FracNotKey[];
140
+ /** Ids of the literacy submenu, in menu order. */
141
+ export declare const ALF_MENU_IDS: readonly string[];
142
+ /** Ids of the math submenu (the "Fração" button is not an activity — it opens another submenu). */
143
+ export declare const MAT_MENU_IDS: readonly string[];
144
+ /** Ids of the fractions submenu, in menu order. */
145
+ export declare const FR_MENU_IDS: readonly string[];
146
+ /** The 6 title submenus, in the order game.js has always scanned them for the visible one. */
147
+ export { TITLE_MENU_IDS_ORDERED as TITLE_MENU_ORDER } from './title.js';
148
+ /** The two rows of the tabuada number picker (0–5 then 6–10). */
149
+ export declare const TAB_ROWS: readonly (readonly number[])[];
150
+ /** Default tabuada selection when storage is empty (or holds something unusable). */
151
+ export declare const TAB_SEL_DEFAULT: readonly number[];
152
+ /** Unknown/absent id → the default activity. game.js's `if(!hasActivity(id))id=DEFAULT_ACTIVITY_ID`. */
153
+ export declare function normalizeActivityId(id: string): string;
154
+ /**
155
+ * Where "Voltar" on the scenario submenu must return to — a pure mapping from the activity that opened it:
156
+ * number-picking activities (Tabuada/Divisão) came through `tm-tab`, fraction activities through `tm-fr`,
157
+ * and otherwise the category's own submenu; free play came straight off `tm-main`.
158
+ * VERBATIM caveat: game.js dereferences `getActivity(id)` without a guard, so an unknown id throws here too.
159
+ */
160
+ export declare function cenBackMenuFor(id: string): TitleMenuId;
161
+ /**
162
+ * Reads the persisted tabuada selection. VERBATIM port, quirks included: the filter is `n>=0 && n<=10`, which
163
+ * JavaScript evaluates after coercion — so `"3"`, `true` and `null` all survive it while `'x'` and `{}` do not.
164
+ * Anything that is not a non-empty array falls back to [2,3,4,5]; an array that filters down to EMPTY stays
165
+ * empty (the "Jogar" button then refuses with an srAlert, which is how game.js has always behaved).
166
+ */
167
+ export declare function sanitizeTabSel(raw: unknown): number[];
168
+ /**
169
+ * Reads the persisted fraction notations. Defaults `{v:1,d:0,dec:0,pct:0,mix:0}`; only the five known keys are
170
+ * copied over (any extra key in storage is ignored) and each is coerced to 0/1; if the result would leave every
171
+ * notation off, `v` is forced back on — the menu's invariant "at least one notation is always lit".
172
+ */
173
+ export declare function sanitizeFracNot(raw: unknown): FracNot;
174
+ /** How many notations are currently on — the guard behind "Deixe ao menos uma notação ligada." */
175
+ export declare function fracNotOnCount(f: FracNot): number;
176
+ /** True when turning `k` OFF is allowed (it is not the last one lit). */
177
+ export declare function canToggleFracNot(f: FracNot, k: FracNotKey): boolean;
178
+ /** Player count staged on the splash: 1..4, never outside. */
179
+ export declare function clampPendingPlayers(n: number): number;
180
+ /**
181
+ * Next focused index for arrow traversal. VERBATIM quirk: when nothing in the submenu has focus (`cur < 0`),
182
+ * BOTH directions land on index 0 — pressing "up" from nowhere does not wrap to the last button.
183
+ *
184
+ * A CONTA DO ANEL saiu daqui e passou a ser `passoNoAnel` (ui/menu-nav), que é a mesma do menu de pausa. Eram
185
+ * duas escritas da mesma volta — `(cur + d + len) % len` aqui, a de lá — e o ADR-0044 §5 pede UMA resposta
186
+ * para "o que acontece no fim da lista" no jogo inteiro. Duas cópias respondem igual até o dia em que uma
187
+ * delas é corrigida sozinha.
188
+ */
189
+ export declare function nextTitleIndex(cur: number, len: number, k: NavKeys): number;
190
+ /**
191
+ * O NOME de uma atividade, no idioma vigente.
192
+ *
193
+ * A chave, quando existe, vence o texto cru — e a ausência dela é significado, não esquecimento: as cinco
194
+ * atividades de alfabetização não declaram chave porque a palavra e a sílaba SÃO a matéria da atividade
195
+ * (pilar 3 do ADR-0010). Todo o resto declara, matemática inclusive: `2 + 3` independe de língua.
196
+ *
197
+ * Resolve AQUI, no ponto de exibição, e não no catálogo — `educational/activities-registry` é DADO e não
198
+ * importa nada; e uma `const` de módulo com `t()` já resolvido congelaria o idioma no boot.
199
+ */
200
+ export declare function nomeDaAtividade(a: ActivityDef | null | undefined): string;
201
+ /** A descrição de rodapé de uma atividade, pela mesma regra do nome. */
202
+ export declare function descricaoDaAtividade(a: ActivityDef | null | undefined): string;
203
+ /** Footer description for a focused/hovered title button: notation text wins, else the activity's `d`.
204
+ * The notation half resolves its key here; the activity's `d` comes from educational/activities-registry
205
+ * and is still pt-BR, for the same curriculum-boundary reason as the activity `nome` (ADR-0032). */
206
+ export declare function titleDescFor(actId: string | undefined, fnotKey: string | undefined): string;
207
+ /** One activity button, with the optional example sub-label (literacy). */
208
+ export declare function activityBtnHtml(id: string): string;
209
+ /** The "Voltar" ghost button of a submenu. */
210
+ export declare function backBtnHtml(to: TitleMenuId): string;
211
+ /** The live-region footer that shows the focused minigame's description. */
212
+ export declare const TM_DESC_HTML = "<div class=\"tm-desc\" aria-live=\"polite\"></div>";
213
+ /** A submenu heading. O parâmetro chamava-se `t` e sombreava a função de tradução — quarta colisão desse
214
+ * nome no projeto; recebe o texto JÁ resolvido, e agora se chama `titulo` para dizê-lo. */
215
+ export declare function tmTitleHtml(titulo: string): string;
216
+ export declare function alfMenuHtml(): string;
217
+ export declare function matMenuHtml(): string;
218
+ /**
219
+ * AS CINCO NOTAÇÕES — ajustes, e não itens de menu (pedido do Dev).
220
+ *
221
+ * Elas eram `.title-btn`, a MESMA classe dos itens que ABREM uma atividade, empilhadas logo acima deles: para
222
+ * quem enxerga, cinco coisas com cara de "entrar" que na verdade LIGAM e DESLIGAM. Para quem navega por
223
+ * escuta é pior — o cursor atravessa dez itens em que os cinco primeiros respondem "ligado/desligado" e os
224
+ * cinco últimos começam uma partida, sem nada entre eles dizendo que a regra mudou. É a mesma doença que o
225
+ * item 5 do ADR-0044 curou no cartão de pausa: dois modelos de interação numa tela, sem fronteira.
226
+ *
227
+ * Três coisas mudam: MOLDURA com nome (`fieldset`/`legend`, a fronteira que o leitor anuncia ao entrar),
228
+ * MARCA visível na frente (`☑`/`☐`, para o estado não depender de cor — WCAG 1.4.1) e PAPEL de caixa de
229
+ * seleção (`role="checkbox"` + `aria-checked`), porque a pergunta aqui é "está marcada?" e não "confirmar?".
230
+ *
231
+ * ⚠️ CONTINUAM SENDO `<button>`, e é deliberado: `titleButtons()` varre `button` para montar o anel de
232
+ * navegação. Um `<input type="checkbox">` seria mais correto no papel e tiraria as cinco do alcance do
233
+ * direcional — quem mais precisa delas é justamente quem só tem ele.
234
+ */
235
+ export declare function fracNotsHtml(f: FracNot): string;
236
+ export declare function frMenuHtml(f: FracNot): string;
237
+ export declare function tabRowHtml(row: readonly number[], sel: readonly number[]): string;
238
+ export declare function tabMenuHtml(sel: readonly number[]): string;
239
+ export declare function cenMenuHtml(cenarios: readonly CenarioOption[]): string;
240
+ /** token found in a label → the letters it hides. */
241
+ export declare const ABBR_MID: Readonly<Record<string, string>>;
242
+ /** The three fixed pieces of an abbreviated label, or null when the label carries no known token. */
243
+ export interface AbbrParts {
244
+ readonly pre: string;
245
+ readonly hid: string;
246
+ readonly suf: string;
247
+ }
248
+ /** Splits a label around its abbreviation token. `pre` keeps the first letter, `suf` starts at the last one. */
249
+ export declare function abbrParts(txt: string): AbbrParts | null;
250
+ /** Label with `k` of the hidden letters revealed; the remaining count stands in for the rest ("A12e" at k=0). */
251
+ export declare function abbrText(p: AbbrParts, k: number): string;
252
+ /**
253
+ * Wires one button's expand/collapse animation. Idempotent (`data-abbrDone`) and a no-op for labels without a
254
+ * token — which, today, is EVERY button in the app: no label in index.html or PM_BTNS contains "A12e"/"S11e"
255
+ * any more (they were spelled out). Ported verbatim rather than deleted; see the extraction report.
256
+ */
257
+ export declare function attachAbbr(b: HTMLElement | null | undefined): void;
258
+ export declare function initActivitiesMenu(ctx: ActivitiesMenuCtx): ActivitiesMenuApi;