@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,88 @@
1
+ /* SPDX-License-Identifier: OFL-1.1
2
+ * Fontes oficiais do EdSP — self-hosted (PWA offline). Todas SIL OFL 1.1.
3
+ * Atkinson Hyperlegible → PADRÃO (UI/leitura) — Braille Institute
4
+ * Andika → ALFABETIZAÇÃO — SIL
5
+ * Lexend → OPCIONAL (dislexia/TDAH/discalculia) — SIL
6
+ * Atkinson Hyperlegible Mono → MATEMÁTICA (algarismos) — Braille Institute
7
+ * latin cobre PT-BR + nórdico (À-ÿ, å ä ö ø æ); latin-ext só baixa se necessário. */
8
+
9
+ /* ---- ranges ---- */
10
+ /* latin: U+0000–00FF + pontuação tipográfica
11
+ latin-ext: U+0100–024F etc. (carga sob demanda) */
12
+
13
+ /* ===== Atkinson Hyperlegible (PADRÃO) ===== */
14
+ @font-face{font-family:'Atkinson Hyperlegible';font-style:normal;font-weight:400;font-display:swap;
15
+ src:url('fonts/atkinson-400.woff2') format('woff2');
16
+ unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;}
17
+ @font-face{font-family:'Atkinson Hyperlegible';font-style:normal;font-weight:400;font-display:swap;
18
+ src:url('fonts/atkinson-400-ext.woff2') format('woff2');
19
+ unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF;}
20
+ @font-face{font-family:'Atkinson Hyperlegible';font-style:normal;font-weight:700;font-display:swap;
21
+ src:url('fonts/atkinson-700.woff2') format('woff2');
22
+ unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;}
23
+ @font-face{font-family:'Atkinson Hyperlegible';font-style:normal;font-weight:700;font-display:swap;
24
+ src:url('fonts/atkinson-700-ext.woff2') format('woff2');
25
+ unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF;}
26
+
27
+ /* ===== Andika (ALFABETIZAÇÃO) ===== */
28
+ @font-face{font-family:'Andika';font-style:normal;font-weight:400;font-display:swap;
29
+ src:url('fonts/andika-400.woff2') format('woff2');
30
+ unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;}
31
+ @font-face{font-family:'Andika';font-style:normal;font-weight:400;font-display:swap;
32
+ src:url('fonts/andika-400-ext.woff2') format('woff2');
33
+ unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF;}
34
+ @font-face{font-family:'Andika';font-style:normal;font-weight:700;font-display:swap;
35
+ src:url('fonts/andika-700.woff2') format('woff2');
36
+ unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;}
37
+ @font-face{font-family:'Andika';font-style:normal;font-weight:700;font-display:swap;
38
+ src:url('fonts/andika-700-ext.woff2') format('woff2');
39
+ unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF;}
40
+
41
+ /* ===== Lexend (OPCIONAL) ===== */
42
+ @font-face{font-family:'Lexend';font-style:normal;font-weight:400;font-display:swap;
43
+ src:url('fonts/lexend-400.woff2') format('woff2');
44
+ unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;}
45
+ @font-face{font-family:'Lexend';font-style:normal;font-weight:400;font-display:swap;
46
+ src:url('fonts/lexend-400-ext.woff2') format('woff2');
47
+ unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF;}
48
+ @font-face{font-family:'Lexend';font-style:normal;font-weight:700;font-display:swap;
49
+ src:url('fonts/lexend-700.woff2') format('woff2');
50
+ unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;}
51
+ @font-face{font-family:'Lexend';font-style:normal;font-weight:700;font-display:swap;
52
+ src:url('fonts/lexend-700-ext.woff2') format('woff2');
53
+ unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF;}
54
+
55
+ /* ===== Atkinson Hyperlegible Mono (MATEMÁTICA) ===== */
56
+ @font-face{font-family:'Atkinson Hyperlegible Mono';font-style:normal;font-weight:400;font-display:swap;
57
+ src:url('fonts/atkinson-mono-400.woff2') format('woff2');
58
+ unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;}
59
+ @font-face{font-family:'Atkinson Hyperlegible Mono';font-style:normal;font-weight:400;font-display:swap;
60
+ src:url('fonts/atkinson-mono-400-ext.woff2') format('woff2');
61
+ unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF;}
62
+
63
+ /* ===== L2/Tipografia: catálogo escolhível no menu (todas SIL OFL 1.1; subset latin) ===== */
64
+ /* Sem serifa */
65
+ @font-face{font-family:'iA Writer Quattro';font-style:normal;font-weight:400;font-display:swap;src:url('fonts/quattro-400.woff2') format('woff2');}
66
+ @font-face{font-family:'iA Writer Quattro';font-style:normal;font-weight:700;font-display:swap;src:url('fonts/quattro-700.woff2') format('woff2');}
67
+ @font-face{font-family:'Source Sans 3';font-style:normal;font-weight:400;font-display:swap;src:url('fonts/sourcesans-400.woff2') format('woff2');}
68
+ @font-face{font-family:'Source Sans 3';font-style:normal;font-weight:700;font-display:swap;src:url('fonts/sourcesans-700.woff2') format('woff2');}
69
+ @font-face{font-family:'Inter';font-style:normal;font-weight:400;font-display:swap;src:url('fonts/inter-400.woff2') format('woff2');}
70
+ @font-face{font-family:'Inter';font-style:normal;font-weight:700;font-display:swap;src:url('fonts/inter-700.woff2') format('woff2');}
71
+ @font-face{font-family:'Open Sans';font-style:normal;font-weight:400;font-display:swap;src:url('fonts/opensans-400.woff2') format('woff2');}
72
+ @font-face{font-family:'Open Sans';font-style:normal;font-weight:700;font-display:swap;src:url('fonts/opensans-700.woff2') format('woff2');}
73
+ @font-face{font-family:'Lato';font-style:normal;font-weight:400;font-display:swap;src:url('fonts/lato-400.woff2') format('woff2');}
74
+ @font-face{font-family:'Lato';font-style:normal;font-weight:700;font-display:swap;src:url('fonts/lato-700.woff2') format('woff2');}
75
+ /* Serifadas */
76
+ @font-face{font-family:'Literata';font-style:normal;font-weight:400;font-display:swap;src:url('fonts/literata-400.woff2') format('woff2');}
77
+ @font-face{font-family:'Literata';font-style:normal;font-weight:700;font-display:swap;src:url('fonts/literata-700.woff2') format('woff2');}
78
+ @font-face{font-family:'Source Serif 4';font-style:normal;font-weight:400;font-display:swap;src:url('fonts/sourceserif-400.woff2') format('woff2');}
79
+ @font-face{font-family:'Source Serif 4';font-style:normal;font-weight:700;font-display:swap;src:url('fonts/sourceserif-700.woff2') format('woff2');}
80
+ @font-face{font-family:'Newsreader';font-style:normal;font-weight:400;font-display:swap;src:url('fonts/newsreader-400.woff2') format('woff2');}
81
+ @font-face{font-family:'Newsreader';font-style:normal;font-weight:700;font-display:swap;src:url('fonts/newsreader-700.woff2') format('woff2');}
82
+ /* Manuscritas (caligráficas inglesas · blackletter alemãs · alfabetização) */
83
+ @font-face{font-family:'Great Vibes';font-style:normal;font-weight:400;font-display:swap;src:url('fonts/greatvibes-400.woff2') format('woff2');}
84
+ @font-face{font-family:'Pinyon Script';font-style:normal;font-weight:400;font-display:swap;src:url('fonts/pinyon-400.woff2') format('woff2');}
85
+ @font-face{font-family:'UnifrakturCook';font-style:normal;font-weight:700;font-display:swap;src:url('fonts/ufcook-700.woff2') format('woff2');}
86
+ @font-face{font-family:'UnifrakturMaguntia';font-style:normal;font-weight:400;font-display:swap;src:url('fonts/ufmag-400.woff2') format('woff2');}
87
+ @font-face{font-family:'Comic Neue';font-style:normal;font-weight:400;font-display:swap;src:url('fonts/comicneue-400.woff2') format('woff2');}
88
+ @font-face{font-family:'Comic Neue';font-style:normal;font-weight:700;font-display:swap;src:url('fonts/comicneue-700.woff2') format('woff2');}
@@ -0,0 +1,159 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ import { type Alcance, type Disponibilidade } from '../input/transports.js';
3
+ import { type ActionPreset } from '../core/actions.js';
4
+ import { type GameDeclaration } from '../core/contract.js';
5
+ import { type SceneStack } from '../core/scenes.js';
6
+ import { createTts } from '../platform/tts.js';
7
+ import { type AudioSonar, type SonarPlayer } from '../platform/audio-sonar.js';
8
+ import type { AlcanceDoFiltro } from '../render/port.js';
9
+ import { type SettingsPanelApi } from '../ui/settings-panel.js';
10
+ import { type MenuNavApi } from '../ui/menu-nav.js';
11
+ import type { NavKeys } from '../input/edges.js';
12
+ import { type KeyboardRuntime } from '../input/keyboard-runtime.js';
13
+ /** O que o jogo empresta do documento. Tudo opcional menos `doc`/`win`: o que faltar vira `problems`. */
14
+ export interface EngineHost {
15
+ readonly doc: Document;
16
+ readonly win: Window;
17
+ /** Um `<svg>` vazio onde os seis filtros de daltonismo são montados em tempo de execução. */
18
+ readonly cvdHost?: SVGElement | Element | null;
19
+ }
20
+ /**
21
+ * O que este jogo NÃO tem. Declarado, e não deduzido de um getter que devolve null.
22
+ *
23
+ * O achado 10 do segundo consumidor é a razão de isto existir como tipo: o quiz precisava se declarar
24
+ * "pausado" para navegar os próprios menus, porque a engine não tinha por onde ouvir "eu não tenho fases".
25
+ */
26
+ export interface Declinios {
27
+ /** Sem menu de pausa por tela (um quiz não tem). */
28
+ readonly semMenuDePausa?: boolean;
29
+ /** Sem assistente de mapeamento de controle. */
30
+ readonly semAssistenteDePad?: boolean;
31
+ /** Sem "ator da pausa" — quem apertou o botão que abriu o menu. */
32
+ readonly semAtorDePausa?: boolean;
33
+ }
34
+ export interface CreateGameOptions {
35
+ /** Os SETE CAMPOS (core/contract). É o que a pilha de acessibilidade lê, e a única coisa que ela lê. */
36
+ readonly declaration: GameDeclaration;
37
+ readonly host: EngineHost;
38
+ readonly declines?: Declinios;
39
+ /**
40
+ * É AGORA hora de navegar menu? A plataforma responde `phase === 'paused'`; um quiz responde `true`.
41
+ * Ausente = `true`, que é o caso do jogo sem fases — o mais simples, e o que não obriga a inventar uma.
42
+ */
43
+ readonly isNavigable?: () => boolean;
44
+ /**
45
+ * O MODO `accessibility` do ADR-0044 (item 7): o direcional dirige a barra rápida em vez do personagem.
46
+ *
47
+ * Opcionais porque um hospedeiro pode não ter barra nenhuma — sem eles a resposta é "ninguém está nela" e
48
+ * nada é chamado. O jogo de plataforma os fornece; um quiz sem HUD de a11y, não.
49
+ */
50
+ /** O índice "N de M" está ligado? Ausente = sim. Ver `comIndice` em ui/menu-nav. */
51
+ readonly comIndice?: () => boolean;
52
+ readonly naBarraDe?: (i: number) => boolean;
53
+ readonly navBar?: (i: number, k: NavKeys) => void;
54
+ /** Jogadores para o teclado remapeável. `Pick<ControlledPlayer,'ctrl'>` — esquema de teclas e nada mais. */
55
+ readonly players?: {
56
+ ctrl: Record<string, string[]>;
57
+ }[];
58
+ /** Troca de fase, para quem tem fases. Ausente = não faz nada (o jogo sem fases não perde nada). */
59
+ readonly setPhase?: (p: 'title' | 'playing' | 'paused') => void;
60
+ /**
61
+ * Os jogadores como a NAVEGAÇÃO SONORA os vê. Ausente, `createGame` DERIVA um do campo 4 do contrato: o
62
+ * foco diz onde o jogador está, que é tudo o que o sonar precisa saber sobre posição.
63
+ *
64
+ * Um jogo com vários jogadores, ou com dispositivo de áudio por jogador, fornece a sua lista. Um jogo de
65
+ * uma criança só não fornece nada — e ganha sonar assim mesmo, que é o ponto.
66
+ */
67
+ readonly sonarPlayers?: () => SonarPlayer[];
68
+ /** Modo cego ligado? Ausente = não. Vale para todos os jogadores, como no jogo de plataforma. */
69
+ readonly isBlindMode?: () => boolean;
70
+ /**
71
+ * AS PALAVRAS DESTE JOGO (`core/actions`). Sem elas a engine não sabe QUANTAS ações pedir a um transporte,
72
+ * e a garantia do ADR-0079 §3 não tem como ser medida — foi a lacuna que a issue #112 encontrou: a
73
+ * aritmética existia, testada, e o `createGame` tinha ZERO ocorrências de qualquer coisa sobre ações.
74
+ *
75
+ * Opcional porque um jogo pode não declarar preset ainda; sem ele o aviso de alcance simplesmente não
76
+ * aparece, que é o comportamento de hoje e não uma regressão.
77
+ */
78
+ readonly preset?: ActionPreset;
79
+ /**
80
+ * Como se descobre que cada transporte está aqui. Ausente = a engine pergunta ao aparelho.
81
+ *
82
+ * Injetável porque «há um controle ligado?» e «isto é uma tela de toque?» são perguntas ao navegador, e um
83
+ * teste que não as possa responder não consegue exercitar a tela que depende delas.
84
+ */
85
+ readonly disponibilidade?: Disponibilidade;
86
+ }
87
+ export interface Engine {
88
+ readonly declaration: GameDeclaration;
89
+ readonly tts: ReturnType<typeof createTts>;
90
+ readonly overlays: SettingsPanelApi;
91
+ readonly nav: MenuNavApi;
92
+ readonly keyboard: KeyboardRuntime;
93
+ /**
94
+ * APLICA O FILTRO DE VISÃO NO MUNDO QUE ESTE JOGO DECLAROU (ADR-0087).
95
+ *
96
+ * ⚠️ Existe porque, sem ela, cada consumidor escrevia a sua — e o `game-15puzzle` escreveu, com o
97
+ * raciocínio certo e sozinho. O que ela acrescenta é a regra dos MENUS, que um consumidor não tem como
98
+ * saber: eles vivem por cima da simulação e são o instrumento de sair dela, então se herdaram o filtro por
99
+ * estarem DENTRO do mundo, ele é desfeito neles. Uma cegueira que apagasse o menu de pausa trancaria a
100
+ * criança dentro da simulação (#82).
101
+ */
102
+ readonly aplicarFiltroDeVisao: (css: string, alcance: AlcanceDoFiltro) => void;
103
+ /**
104
+ * A NAVEGAÇÃO SONORA, pronta e ligada à declaração deste jogo (item 19).
105
+ *
106
+ * Ela vem de graça porque o sonar deixou de precisar de tiles: pergunta topologia (campo 1), alvos (campo
107
+ * 5) e nome (campo 3), e o jogo já declarou os três para existir. Era o achado 9 do segundo consumidor —
108
+ * "ligá-lo exigiria MENTIR para a engine" —, e a mentira era exigida pela FORMA da pergunta, não pelo som.
109
+ */
110
+ readonly sonar: AudioSonar;
111
+ /**
112
+ * A PILHA DE CENAS (item 22, C3 do ADR-0030), vazia e pronta.
113
+ *
114
+ * Vem de `createGame` e não de cada jogo pelo mesmo motivo do sonar: é infraestrutura, e um jogo que a
115
+ * montasse sozinho montaria a décima quinta versão de push/pop. O que ela substitui é o
116
+ * `phase: 'title' | 'playing' | 'paused'` — um enum DESTE jogo que doze módulos leem, e que um jogo com
117
+ * mapa de fases ou tela de resultados não teria como estender sem pedir constante nova à engine (o ADR-0030
118
+ * registra alargar a união como NÃO-opção, e é essa a razão).
119
+ *
120
+ * Nasce VAZIA: quem empilha é o jogo, porque quais são as cenas é a única parte disto que é dele.
121
+ */
122
+ readonly cenas: SceneStack;
123
+ /** Quantos filtros de daltonismo foram montados. `0` = não havia host, e o menu visual perde metade. */
124
+ readonly cvdFilters: number;
125
+ /** O que FALTOU no documento do consumidor. Vazia = o hospedeiro cumpriu o contrato de marcação. */
126
+ readonly problems: readonly string[];
127
+ /** O que este jogo declarou não ter. Devolvido para poder ser auditado — declinar fica no registro. */
128
+ readonly declines: Declinios;
129
+ /**
130
+ * O ANÚNCIO DE QUE O LAÇO PAROU, pronto para entrar em `startLoop(ticker, quadro, maxDt, { aoFalhar })`.
131
+ *
132
+ * ⚠️ O ADR-0054 diz por escrito que fica *"só metade verdadeiro"* enquanto isto não existir, e a metade que
133
+ * faltava é a que importa: **criança cega não vê tela congelada.** Sem anúncio, o modo cego não distingue
134
+ * «travou» de «está pensando», e o silêncio é a mesma coisa nos dois casos.
135
+ *
136
+ * ⚠️ VEM DA ENGINE E NÃO DE CADA JOGO porque a mensagem é a mesma em todos e o canal (leitor de tela +
137
+ * narração + o que se VÊ) é infraestrutura. Mas quem chama `startLoop` é o JOGO — ele é o dono do ticker —,
138
+ * então isto é entregue e não instalado: um jogo que monte o laço sem passar isto continua a PARAR, porque
139
+ * parar não é opcional; o que ele perde é dizer que parou.
140
+ */
141
+ readonly aoFalhar: (erro: unknown) => void;
142
+ /**
143
+ * O ALCANCE MEDIDO NO ARRANQUE — a garantia do ADR-0079 §3 como dado, para quem quiser lê-la.
144
+ *
145
+ * A engine já mostrou o aviso se havia o que dizer; isto fica devolvido porque um jogo pode querer decidir
146
+ * mais (esconder uma fase que exige doze ações, por exemplo), e porque `ok: false` é o tipo de facto que
147
+ * tem de poder ser auditado em vez de ficar só numa tela que já fechou.
148
+ */
149
+ readonly alcance: Alcance;
150
+ }
151
+ /**
152
+ * Liga a engine para um jogo declarado.
153
+ *
154
+ * ⚠️ LANÇA se a declaração for malformada, e NÃO lança se faltar marcação. A diferença não é gosto: uma
155
+ * declaração errada é defeito de PROGRAMA, e um jogo que roda meio declarado é pior do que um que não abre;
156
+ * um id ausente é lacuna do HOSPEDEIRO, e o quiz provou que ligar só a parte que serve é legítimo — foi
157
+ * assim que ele recusou o pad e o sonar sem mentir. Por isso um vira exceção e o outro vira `problems`.
158
+ */
159
+ export declare function createGame(o: CreateGameOptions): Engine;
@@ -0,0 +1,266 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // boot/create-game — O TESTE DE FRONTEIRA DO ADR-0027 (passo 4), escrito em vez de discutido.
3
+ //
4
+ // ========================= A PERGUNTA QUE ESTE ARQUIVO EXISTE PARA RESPONDER =========================
5
+ // O ADR-0027 pôs um veredito e não o deixou vago:
6
+ //
7
+ // "se `createGame()` não puder ser escrito sem um parâmetro chamado `coinTarget`, a fronteira que este
8
+ // registro propõe está errada e os passos 5 a 7 NÃO PODEM COMEÇAR."
9
+ //
10
+ // Pode. Não há `coinTarget` aqui, e não é por disciplina — é porque o ADR-0030 resolveu de onde o alvo vem:
11
+ // campo 5 do contrato (`objectiveOf`). O HUD que pedia um número de moedas passa a perguntar ao jogo quantas
12
+ // de quantas, e "moeda" deixa de ser palavra da engine. `tests/boot-create-game.node.test.js` prende isso.
13
+ //
14
+ // ========================= O QUE ISTO É: A ORDEM, E O QUE FALTA =========================
15
+ // Não é um framework. É a sequência de ligar a engine, que hoje cada consumidor reescreve à mão — e o segundo
16
+ // consumidor mediu o preço de reescrevê-la (`consumer-quiz`, achados 3, 6 e 12):
17
+ //
18
+ // · ACHADO 3 — `initAudioMixer()` TEM de rodar antes de `createTts`, senão `audioCat` é null e o `narrate`
19
+ // desiste CALADO. Uma dependência de ordem que nenhum tipo declara e que se descobre pelo silêncio. Aqui
20
+ // ela é impossível de errar: quem chama não escolhe a ordem.
21
+ // · ACHADO 6 — os painéis exigem ids fixos no documento (`#typo`, `#typo-list`…) e, quando faltam, ABREM
22
+ // VAZIOS. Sem erro. Aqui a falta vira `problems`, que é uma lista que o consumidor pode ler e mostrar.
23
+ // · ACHADO 12 — todo consumidor escreve o mesmo adaptador de uma linha para o `window`. Escrito uma vez.
24
+ //
25
+ // E um achado NOVO, que só apareceu quando esta função tentou bootar contra um documento injetado:
26
+ //
27
+ // · ACHADO 15 — `initI18n()` chamava `applyDom(document)`, o GLOBAL, por baixo de quem a chamasse. Num
28
+ // navegador dá no mesmo e por isso sobreviveu; num teste de lógica pura é a diferença entre bootar e não
29
+ // bootar, e num futuro com dois documentos (uma engine em iframe, um editor ao lado do jogo) seria a
30
+ // diferença entre traduzir o documento certo e o outro. Consertado no mesmo passo: `initI18n(root)`, com
31
+ // o global como padrão, exatamente como `applyDom` já fazia.
32
+ //
33
+ // ========================= DECLINAR NÃO É MENTIR =========================
34
+ // O quiz recusou o sonar e o pad em vez de inventar tiles e uma caixa de colisão falsos, e essa distinção é o
35
+ // que separa um achado de um verde falso. Aqui ela vira TIPO: um jogo sem menu de pausa declara
36
+ // `semMenuDePausa: true` em vez de devolver `null` de um getter e torcer. O que se declina fica registrado no
37
+ // objeto devolvido, e um consumidor pode ser auditado pelo que recusou.
38
+ //
39
+ // ========================= O QUE ISTO AINDA NÃO FAZ, DITO AQUI E NÃO ESCONDIDO =========================
40
+ // Não liga render, física, tiles nem o sonar — nada disso é de todo jogo, e o achado 9 mostra que o sonar hoje
41
+ // exige seis coisas de plataforma. Não substitui o boot do `main.js`, que tem catorze anos de ordem própria.
42
+ // O que ele cobre é o que o quiz provou ser IDÊNTICO em qualquer jogo: idioma, leitor de tela, mixer, voz,
43
+ // pilha de diálogos, filtros de daltonismo, teclado remapeável e navegação de menu.
44
+ import { initI18n } from '../core/i18n.js';
45
+ import { criarAvisoDeQueda } from '../ui/loop-crash.js';
46
+ import { initFocusTrap, focaveisNoDom } from '../ui/focus-trap.js';
47
+ import { mostrarAvisoDeAlcance } from '../ui/reach-notice.js';
48
+ import { alcance, transportesPadrao } from '../input/transports.js';
49
+ import { presetActions } from '../core/actions.js';
50
+ import { t } from '../core/i18n.js';
51
+ import { srSay, srAlert } from '../core/a11y-sr.js';
52
+ import { conformanceProblems } from '../core/contract.js';
53
+ import { criarPilha } from '../core/scenes.js';
54
+ import { createTts } from '../platform/tts.js';
55
+ import { ensureAC, catNode, audioOut, soundOn, volume, audioCat, initAudioMixer, tonePan, audioCtx } from '../platform/audio.js';
56
+ import { createAudioSonar } from '../platform/audio-sonar.js';
57
+ import { VIZ_BY_KEY } from '../render/viz-modes.js';
58
+ import { OVERLAY_SCOPE_SELECTOR } from '../ui/settings-panel.js';
59
+ import { LOGICAL_W } from '../core/constants.js';
60
+ import { initSettingsPanel } from '../ui/settings-panel.js';
61
+ import { initMenuNav } from '../ui/menu-nav.js';
62
+ import { initKeyboardRuntime } from '../input/keyboard-runtime.js';
63
+ import { kb, initKB } from '../input/keyboard.js';
64
+ import { installCvdFilters } from '../render/cvd-matrices.js';
65
+ /** Os ids que os painéis emprestados exigem do documento. Achado 6: sem eles o painel abre VAZIO, sem erro. */
66
+ const MARCACAO_EXIGIDA = ['#game-region', '#sr-status', '#sr-alert'];
67
+ /**
68
+ * Liga a engine para um jogo declarado.
69
+ *
70
+ * ⚠️ LANÇA se a declaração for malformada, e NÃO lança se faltar marcação. A diferença não é gosto: uma
71
+ * declaração errada é defeito de PROGRAMA, e um jogo que roda meio declarado é pior do que um que não abre;
72
+ * um id ausente é lacuna do HOSPEDEIRO, e o quiz provou que ligar só a parte que serve é legítimo — foi
73
+ * assim que ele recusou o pad e o sonar sem mentir. Por isso um vira exceção e o outro vira `problems`.
74
+ */
75
+ export function createGame(o) {
76
+ const problemasDoContrato = conformanceProblems(o.declaration);
77
+ if (problemasDoContrato.length) {
78
+ throw new Error('createGame: declaração malformada — ' + problemasDoContrato.join('; '));
79
+ }
80
+ const { doc, win } = o.host;
81
+ const declines = o.declines ?? {};
82
+ const problems = [];
83
+ const $ = (sel) => doc.querySelector(sel);
84
+ const $$ = (sel) => [...doc.querySelectorAll(sel)];
85
+ for (const sel of MARCACAO_EXIGIDA) {
86
+ if (!$(sel))
87
+ problems.push(`marcação ausente: ${sel}`);
88
+ }
89
+ // ⚠️ O MUNDO DECLARADO TEM DE EXISTIR NO DOCUMENTO, e esta é a falha que o ADR-0087 deixaria aberta se
90
+ // parasse na conformidade. `conformanceProblems` confere a FORMA — que há um seletor e que ele não está
91
+ // vazio — e não tem como conferir se ele CASA alguma coisa, porque `core/contract` é puro e não vê DOM.
92
+ //
93
+ // Um seletor com erro de digitação (`#gaem-region`) passa na conformidade e produz exatamente o defeito
94
+ // que o registro existe para eliminar: a simulação de empatia aplicada a NADA, e um adulto informado de
95
+ // que sentiu algo que não sentiu. É um problema do HOSPEDEIRO e não do programa, então entra em
96
+ // `problems` como as marcações — o jogo abre, e quem o integrou lê que o mundo dele não está lá.
97
+ const mundo = o.declaration.world();
98
+ if (mundo.kind === 'element' && !$(mundo.selector)) {
99
+ problems.push(`mundo declarado não encontrado: ${mundo.selector}`);
100
+ }
101
+ // 1. IDIOMA ANTES DE TUDO. A interface não pode ser construída antes de a língua ser conhecida — foi o que
102
+ // o item 14 consertou movendo `initI18n()` para o topo do boot. O documento entra: ver o achado 15.
103
+ initI18n(doc);
104
+ // 2. MIXER ANTES DA VOZ. O achado 3, virado sequência: quem chama não tem como inverter estas duas linhas.
105
+ initAudioMixer();
106
+ const tts = createTts({
107
+ srSay, srAlert, ensureAC, catNode, audioOut,
108
+ getSoundOn: () => soundOn, getVolume: () => volume, getAudioCat: () => audioCat,
109
+ });
110
+ /**
111
+ * O FILTRO DE VISÃO, aplicado ao MUNDO QUE O JOGO DECLAROU (ADR-0087).
112
+ *
113
+ * ⚠️ E A REGRA DOS MENUS É UMA GENERALIZAÇÃO, não uma segunda regra. O jogo próprio da engine limpava o
114
+ * filtro em `#dom-layer` porque ele está DENTRO de `#game-region` e um filtro CSS herda — sem isso, uma
115
+ * simulação de cegueira apagaria o menu de pausa e trancaria a criança dentro dela (#82). O 15-puzzle não
116
+ * tem nada dentro, e a mesma linha não faz nada. Um só código serve às duas formas porque ele pergunta ao
117
+ * DOM em vez de assumir a forma: limpa o filtro nos overlays que ESTEJAM dentro do mundo.
118
+ *
119
+ * ⚠️ `{kind:'none'}` NÃO APLICA NADA. Uma atividade sem espaço não tem mundo para simular, e pintar um
120
+ * filtro sobre ela seria a mentira que o ADR-0087 existe para impedir, só que ao contrário.
121
+ */
122
+ function aplicarFiltroDeVisao(css, alcance) {
123
+ const mundo = o.declaration.world();
124
+ if (mundo.kind !== 'element')
125
+ return;
126
+ const el = $(mundo.selector);
127
+ if (!el)
128
+ return; // já reportado em `problems`; não se inventa superfície
129
+ el.style.filter = css;
130
+ if (alcance === 'mundo') {
131
+ // Os menus vivem POR CIMA da simulação e são o instrumento de sair dela: se herdaram o filtro por
132
+ // estarem dentro do mundo, desfaz-se neles.
133
+ for (const ov of $$(OVERLAY_SCOPE_SELECTOR)) {
134
+ if (el.contains(ov))
135
+ ov.style.filter = '';
136
+ }
137
+ }
138
+ }
139
+ // 3. A pilha de diálogos. O ctx é o mesmo em qualquer jogo — é boilerplate, e boilerplate repetido é onde
140
+ // consumidores divergem sem querer.
141
+ const overlays = initSettingsPanel({
142
+ $, $$, doc,
143
+ computedZ: (el) => +win.getComputedStyle(el).zIndex || 0,
144
+ });
145
+ // 4. Daltonismo: a engine ENTREGA o markup em vez de exigir que o consumidor o adivinhe (achado 7).
146
+ const cvdFilters = installCvdFilters(o.host.cvdHost ?? null);
147
+ if (!cvdFilters)
148
+ problems.push('sem host de filtros (<svg>): a correção de daltonismo não foi montada');
149
+ // 4b. NAVEGAÇÃO SONORA. Só o contrato entra: nada de tile, caixa de colisão ou array de moedas.
150
+ const sonar = createAudioSonar({
151
+ topology: () => o.declaration.topology(),
152
+ targetsOf: (i) => o.declaration.targetsOf(i),
153
+ nameAt: (at) => o.declaration.nameAt(at),
154
+ tonePan, srSay, narrate: (texto) => tts.narrate(texto),
155
+ VIZ_BY_KEY, getModoCego: o.isBlindMode ?? (() => false), LOGICAL_W,
156
+ // O jogador DERIVADO do foco: campo 4 respondendo "onde a criança está". Um jogo que não fornece lista
157
+ // ainda tem sonar, e é isso que faz a pilha de acessibilidade não ser acessório.
158
+ getPlayers: o.sonarPlayers ?? (() => {
159
+ const f = o.declaration.focusOf(0);
160
+ return f ? [{ i: 0, x: f.at.x, y: f.at.y, viz: 'normal' }] : [];
161
+ }),
162
+ getNumPlayers: () => (o.players ?? [null]).length,
163
+ getAudioCtx: () => audioCtx, getSoundOn: () => soundOn, getAudioCat: () => audioCat,
164
+ });
165
+ // 5. Teclado remapeável — o melhor recorte da base (achado 11): esquema de teclas, sem mundo.
166
+ initKB();
167
+ const players = o.players ?? [{ ctrl: {} }];
168
+ const keyboard = initKeyboardRuntime({
169
+ getKB: () => kb, getNumPlayers: () => players.length, getPlayers: () => players,
170
+ });
171
+ keyboard.assignControls();
172
+ // 6. Navegação de menu. Os três declínios entram como AUSÊNCIA DECLARADA, não como getter que devolve null.
173
+ const nav = initMenuNav({
174
+ $, getActiveElement: () => doc.activeElement,
175
+ topVisibleOverlay: overlays.topVisibleOverlay, closeById: overlays.closeById,
176
+ getPauseMenu: declines.semMenuDePausa ? () => null : (i) => $(`#vp-pause-${i}`),
177
+ setPhase: o.setPhase ?? (() => { }),
178
+ setPauseActor: () => { },
179
+ srSay,
180
+ // Sem opinião declarada, o índice fica LIGADO: quem precisa dele para se orientar não tem como saber
181
+ // que ele existe se vier desligado (a mesma razão de o modo cego nascer com TTS e sonar).
182
+ comIndice: o.comIndice ?? (() => true),
183
+ isNavigable: o.isNavigable ?? (() => true),
184
+ // Um hospedeiro que nao tenha barra de acessibilidade responde "nunca" e nunca chama nada — o modo e'
185
+ // opcional para o consumidor, obrigatorio para este jogo.
186
+ naBarraDe: o.naBarraDe ?? (() => false),
187
+ navBar: o.navBar ?? (() => { }),
188
+ isCapturing: () => false,
189
+ closePadWiz: () => { },
190
+ whichPlayer: (code) => keyboard.whichPlayer(code),
191
+ actionOf: (code, i) => keyboard.actionOf(code, i),
192
+ // Achado 12, RESOLVIDO NA ENGINE: o ctx tipava `win` com `fn: (e: never) => void`, o `window` real não
193
+ // casava, e cada consumidor escrevia o mesmo adaptador de uma linha. A porta agora é genérica sobre
194
+ // `WindowEventMap` (ver `EventTargetLike` em input/touch-bindings), então o `window` entra direto.
195
+ win,
196
+ });
197
+ // ⚠️ E AGORA LIGA. A `nav` era montada aqui e ficava desligada — `MenuNavApi.attach()` existia, `menu-nav.ts`
198
+ // descrevia-a como estando ali "para o game.js instalar exatamente como antes", e `createGame` nunca a
199
+ // chamava. O efeito num jogo que arranque pela engine: os diálogos de acessibilidade e o menu de pausa
200
+ // respondem só ao RATO, o que é o pilar 2 a falhar por inteiro — e o `consumer-quiz` teve de a chamar à mão
201
+ // depois do `createGame`, que é o sintoma da fronteira estar no lugar errado.
202
+ //
203
+ // Um fio que a engine MONTA e não liga é pior do que um que ela não monta: a ausência seria visível — o
204
+ // objeto tem uma `nav`, e ela parece pronta.
205
+ //
206
+ // Instalar aqui é seguro antes de o jogo acabar de arrancar: sem diálogo aberto e sem menu de pausa,
207
+ // `menuNavKey` não consome tecla nenhuma e a deixa seguir para quem for o dono.
208
+ nav.attach();
209
+ // ⚠️ E A ARMADILHA DE FOCO, que não existia em lado nenhum — os outros dois fios eram montados e deixados
210
+ // desligados; este nunca tinha sido escrito. Com um overlay aberto, o Tab entrava no tabuleiro por baixo,
211
+ // enquanto todo `.overlay__card` do documento diz `aria-modal="true"`. Uma promessa que o teclado desmente
212
+ // é pior do que promessa nenhuma: quem usa leitor de tela sai para um jogo cujo estado não percebe.
213
+ initFocusTrap({
214
+ overlayDeCima: overlays.topVisibleOverlay,
215
+ focoAtual: () => doc.activeElement,
216
+ focaveisDe: focaveisNoDom,
217
+ win,
218
+ }).attach();
219
+ /**
220
+ * O ALCANCE: entre os transportes DISPONÍVEIS a esta criança, algum carrega as ações deste jogo?
221
+ *
222
+ * ⚠️ A detecção segue o que o projeto JÁ usa para a mesma pergunta (`isCoarsePointer` em `game/session`):
223
+ * `pointer:coarse && hover:none` é toque, e o contrário é teclado. Ela erra num tablet COM teclado — e o
224
+ * erro só é tolerável porque a tela INFORMA em vez de recusar. Ver o cabeçalho de `ui/reach-notice`.
225
+ */
226
+ const disponibilidade = o.disponibilidade ?? {
227
+ gamepad: () => { try {
228
+ return [...(win.navigator?.getGamepads?.() ?? [])].some(Boolean);
229
+ }
230
+ catch {
231
+ return false;
232
+ } },
233
+ toque: () => { try {
234
+ return win.matchMedia('(pointer:coarse)').matches && win.matchMedia('(hover:none)').matches;
235
+ }
236
+ catch {
237
+ return false;
238
+ } },
239
+ teclado: () => { try {
240
+ return !(win.matchMedia('(pointer:coarse)').matches && win.matchMedia('(hover:none)').matches);
241
+ }
242
+ catch {
243
+ return true;
244
+ } },
245
+ };
246
+ const acoesDoJogo = o.preset ? presetActions(o.preset) : [];
247
+ const alcanceAqui = alcance(transportesPadrao(disponibilidade), acoesDoJogo);
248
+ // ⚠️ SÓ APARECE QUANDO HÁ O QUE DIZER. Um aviso que aparece sempre deixa de ser lido, e um jogo cujas ações
249
+ // cabem no toque não tem nada a avisar — que é o caso comum e tem de continuar silencioso.
250
+ if (acoesDoJogo.length) {
251
+ mostrarAvisoDeAlcance({
252
+ procurar: (sel) => $(sel),
253
+ criar: (tag) => doc.createElement(tag),
254
+ t,
255
+ srAlert,
256
+ }, alcanceAqui);
257
+ }
258
+ // O ANÚNCIO DE QUE O LAÇO PAROU (ADR-0054). Entregue e não instalado: quem chama `startLoop` é o JOGO, que
259
+ // é o dono do ticker. Um jogo que monte o laço sem passar isto continua a PARAR — parar não é opcional; o
260
+ // que ele perde é dizer que parou.
261
+ const aoFalhar = criarAvisoDeQueda({
262
+ procurar: (sel) => $(sel),
263
+ narrar: (texto) => tts.narrate(texto),
264
+ });
265
+ return { declaration: o.declaration, tts, overlays, nav, keyboard, sonar, aplicarFiltroDeVisao, cenas: criarPilha(), cvdFilters, problems, declines, aoFalhar, alcance: alcanceAqui };
266
+ }
@@ -0,0 +1,4 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ export declare function setVlibrasSay(fn: (text: string) => void): void;
3
+ export declare const srSay: (t: string) => void;
4
+ export declare const srAlert: (t: string) => void;