@the-inclusionist/engine 6.36.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/README.md +107 -0
- package/app/css/style.css +622 -0
- package/app/public/vendor/fonts/andika-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/andika-400.woff2 +0 -0
- package/app/public/vendor/fonts/andika-700-ext.woff2 +0 -0
- package/app/public/vendor/fonts/andika-700.woff2 +0 -0
- package/app/public/vendor/fonts/atkinson-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/atkinson-400.woff2 +0 -0
- package/app/public/vendor/fonts/atkinson-700-ext.woff2 +0 -0
- package/app/public/vendor/fonts/atkinson-700.woff2 +0 -0
- package/app/public/vendor/fonts/atkinson-mono-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/atkinson-mono-400.woff2 +0 -0
- package/app/public/vendor/fonts/comicneue-400.woff2 +0 -0
- package/app/public/vendor/fonts/comicneue-700.woff2 +0 -0
- package/app/public/vendor/fonts/greatvibes-400.woff2 +0 -0
- package/app/public/vendor/fonts/inter-400.woff2 +0 -0
- package/app/public/vendor/fonts/inter-700.woff2 +0 -0
- package/app/public/vendor/fonts/lato-400.woff2 +0 -0
- package/app/public/vendor/fonts/lato-700.woff2 +0 -0
- package/app/public/vendor/fonts/lexend-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/lexend-400.woff2 +0 -0
- package/app/public/vendor/fonts/lexend-700-ext.woff2 +0 -0
- package/app/public/vendor/fonts/lexend-700.woff2 +0 -0
- package/app/public/vendor/fonts/literata-400.woff2 +0 -0
- package/app/public/vendor/fonts/literata-700.woff2 +0 -0
- package/app/public/vendor/fonts/newsreader-400.woff2 +0 -0
- package/app/public/vendor/fonts/newsreader-700.woff2 +0 -0
- package/app/public/vendor/fonts/opensans-400.woff2 +0 -0
- package/app/public/vendor/fonts/opensans-700.woff2 +0 -0
- package/app/public/vendor/fonts/pinyon-400.woff2 +0 -0
- package/app/public/vendor/fonts/quattro-400.woff2 +0 -0
- package/app/public/vendor/fonts/quattro-700.woff2 +0 -0
- package/app/public/vendor/fonts/sourcesans-400.woff2 +0 -0
- package/app/public/vendor/fonts/sourcesans-700.woff2 +0 -0
- package/app/public/vendor/fonts/sourceserif-400.woff2 +0 -0
- package/app/public/vendor/fonts/sourceserif-700.woff2 +0 -0
- package/app/public/vendor/fonts/ufcook-700.woff2 +0 -0
- package/app/public/vendor/fonts/ufmag-400.woff2 +0 -0
- package/app/public/vendor/fonts.css +88 -0
- package/dist-pkg/boot/create-game.d.ts +159 -0
- package/dist-pkg/boot/create-game.js +266 -0
- package/dist-pkg/core/a11y-sr.d.ts +4 -0
- package/dist-pkg/core/a11y-sr.js +20 -0
- package/dist-pkg/core/actions.d.ts +100 -0
- package/dist-pkg/core/actions.js +149 -0
- package/dist-pkg/core/anel.d.ts +17 -0
- package/dist-pkg/core/anel.js +31 -0
- package/dist-pkg/core/collision.d.ts +21 -0
- package/dist-pkg/core/collision.js +62 -0
- package/dist-pkg/core/constants.d.ts +84 -0
- package/dist-pkg/core/constants.js +64 -0
- package/dist-pkg/core/contract.d.ts +227 -0
- package/dist-pkg/core/contract.js +184 -0
- package/dist-pkg/core/dom-query.d.ts +9 -0
- package/dist-pkg/core/dom-query.js +25 -0
- package/dist-pkg/core/entity.d.ts +150 -0
- package/dist-pkg/core/entity.js +38 -0
- package/dist-pkg/core/escape-html.d.ts +16 -0
- package/dist-pkg/core/escape-html.js +32 -0
- package/dist-pkg/core/i18n.d.ts +71 -0
- package/dist-pkg/core/i18n.js +236 -0
- package/dist-pkg/core/layers.d.ts +39 -0
- package/dist-pkg/core/layers.js +71 -0
- package/dist-pkg/core/letter-grid.d.ts +55 -0
- package/dist-pkg/core/letter-grid.js +116 -0
- package/dist-pkg/core/loop.d.ts +17 -0
- package/dist-pkg/core/loop.js +36 -0
- package/dist-pkg/core/password.d.ts +34 -0
- package/dist-pkg/core/password.js +205 -0
- package/dist-pkg/core/rng.d.ts +20 -0
- package/dist-pkg/core/rng.js +61 -0
- package/dist-pkg/core/rotulo-acessivel.d.ts +17 -0
- package/dist-pkg/core/rotulo-acessivel.js +41 -0
- package/dist-pkg/core/run-state.d.ts +115 -0
- package/dist-pkg/core/run-state.js +40 -0
- package/dist-pkg/core/scenes.d.ts +68 -0
- package/dist-pkg/core/scenes.js +64 -0
- package/dist-pkg/core/screens.d.ts +16 -0
- package/dist-pkg/core/screens.js +28 -0
- package/dist-pkg/core/state.d.ts +138 -0
- package/dist-pkg/core/state.js +290 -0
- package/dist-pkg/core/tiles.d.ts +12 -0
- package/dist-pkg/core/tiles.js +61 -0
- package/dist-pkg/core/world.d.ts +4 -0
- package/dist-pkg/core/world.js +58 -0
- package/dist-pkg/educational/activities-registry.d.ts +47 -0
- package/dist-pkg/educational/activities-registry.js +95 -0
- package/dist-pkg/i18n/en.d.ts +3 -0
- package/dist-pkg/i18n/en.js +560 -0
- package/dist-pkg/i18n/es.d.ts +3 -0
- package/dist-pkg/i18n/es.js +558 -0
- package/dist-pkg/i18n/pt.d.ts +3 -0
- package/dist-pkg/i18n/pt.js +618 -0
- package/dist-pkg/input/default-bindings.d.ts +24 -0
- package/dist-pkg/input/default-bindings.js +122 -0
- package/dist-pkg/input/devices.d.ts +15 -0
- package/dist-pkg/input/devices.js +31 -0
- package/dist-pkg/input/edges.d.ts +58 -0
- package/dist-pkg/input/edges.js +49 -0
- package/dist-pkg/input/gamepad.d.ts +203 -0
- package/dist-pkg/input/gamepad.js +531 -0
- package/dist-pkg/input/keyboard-runtime.d.ts +61 -0
- package/dist-pkg/input/keyboard-runtime.js +83 -0
- package/dist-pkg/input/keyboard.d.ts +45 -0
- package/dist-pkg/input/keyboard.js +80 -0
- package/dist-pkg/input/keydown.d.ts +270 -0
- package/dist-pkg/input/keydown.js +381 -0
- package/dist-pkg/input/latch.d.ts +30 -0
- package/dist-pkg/input/latch.js +63 -0
- package/dist-pkg/input/pointer-space.d.ts +40 -0
- package/dist-pkg/input/pointer-space.js +51 -0
- package/dist-pkg/input/state.d.ts +11 -0
- package/dist-pkg/input/state.js +12 -0
- package/dist-pkg/input/touch-bindings.d.ts +210 -0
- package/dist-pkg/input/touch-bindings.js +365 -0
- package/dist-pkg/input/touch.d.ts +173 -0
- package/dist-pkg/input/touch.js +314 -0
- package/dist-pkg/input/transports.d.ts +90 -0
- package/dist-pkg/input/transports.js +96 -0
- package/dist-pkg/input/vocabulary-migration.d.ts +58 -0
- package/dist-pkg/input/vocabulary-migration.js +125 -0
- package/dist-pkg/platform/audio-ambient.d.ts +27 -0
- package/dist-pkg/platform/audio-ambient.js +89 -0
- package/dist-pkg/platform/audio-earcons.d.ts +24 -0
- package/dist-pkg/platform/audio-earcons.js +62 -0
- package/dist-pkg/platform/audio-jingles.d.ts +17 -0
- package/dist-pkg/platform/audio-jingles.js +54 -0
- package/dist-pkg/platform/audio-mixer.d.ts +14 -0
- package/dist-pkg/platform/audio-mixer.js +62 -0
- package/dist-pkg/platform/audio-nav.d.ts +51 -0
- package/dist-pkg/platform/audio-nav.js +99 -0
- package/dist-pkg/platform/audio-sonar.d.ts +71 -0
- package/dist-pkg/platform/audio-sonar.js +173 -0
- package/dist-pkg/platform/audio.d.ts +32 -0
- package/dist-pkg/platform/audio.js +191 -0
- package/dist-pkg/platform/interruptible-speech.d.ts +19 -0
- package/dist-pkg/platform/interruptible-speech.js +79 -0
- package/dist-pkg/platform/speech.d.ts +2 -0
- package/dist-pkg/platform/speech.js +35 -0
- package/dist-pkg/platform/storage.d.ts +87 -0
- package/dist-pkg/platform/storage.js +148 -0
- package/dist-pkg/platform/tts.d.ts +33 -0
- package/dist-pkg/platform/tts.js +142 -0
- package/dist-pkg/render/camera.d.ts +58 -0
- package/dist-pkg/render/camera.js +105 -0
- package/dist-pkg/render/canvas.d.ts +13 -0
- package/dist-pkg/render/canvas.js +26 -0
- package/dist-pkg/render/cenario-data.d.ts +127 -0
- package/dist-pkg/render/cenario-data.js +145 -0
- package/dist-pkg/render/city-tex.d.ts +63 -0
- package/dist-pkg/render/city-tex.js +228 -0
- package/dist-pkg/render/city-tiles.d.ts +11 -0
- package/dist-pkg/render/city-tiles.js +140 -0
- package/dist-pkg/render/crt.d.ts +19 -0
- package/dist-pkg/render/crt.js +92 -0
- package/dist-pkg/render/cvd-matrices.d.ts +35 -0
- package/dist-pkg/render/cvd-matrices.js +91 -0
- package/dist-pkg/render/draw.d.ts +149 -0
- package/dist-pkg/render/draw.js +221 -0
- package/dist-pkg/render/fx.d.ts +63 -0
- package/dist-pkg/render/fx.js +123 -0
- package/dist-pkg/render/hc-role-data.d.ts +14 -0
- package/dist-pkg/render/hc-role-data.js +24 -0
- package/dist-pkg/render/high-contrast.d.ts +109 -0
- package/dist-pkg/render/high-contrast.js +254 -0
- package/dist-pkg/render/lq-filter.d.ts +37 -0
- package/dist-pkg/render/lq-filter.js +94 -0
- package/dist-pkg/render/minimap.d.ts +11 -0
- package/dist-pkg/render/minimap.js +94 -0
- package/dist-pkg/render/parallax.d.ts +85 -0
- package/dist-pkg/render/parallax.js +158 -0
- package/dist-pkg/render/player-anim.d.ts +71 -0
- package/dist-pkg/render/player-anim.js +152 -0
- package/dist-pkg/render/port.d.ts +143 -0
- package/dist-pkg/render/port.js +29 -0
- package/dist-pkg/render/recycling-tex.d.ts +48 -0
- package/dist-pkg/render/recycling-tex.js +164 -0
- package/dist-pkg/render/scene-city.d.ts +77 -0
- package/dist-pkg/render/scene-city.js +181 -0
- package/dist-pkg/render/scene-parallax.d.ts +72 -0
- package/dist-pkg/render/scene-parallax.js +426 -0
- package/dist-pkg/render/scene-sky.d.ts +126 -0
- package/dist-pkg/render/scene-sky.js +295 -0
- package/dist-pkg/render/screen-pipeline.d.ts +134 -0
- package/dist-pkg/render/screen-pipeline.js +177 -0
- package/dist-pkg/render/set-cenario.d.ts +54 -0
- package/dist-pkg/render/set-cenario.js +103 -0
- package/dist-pkg/render/sprite-fx.d.ts +3 -0
- package/dist-pkg/render/sprite-fx.js +40 -0
- package/dist-pkg/render/textures.d.ts +75 -0
- package/dist-pkg/render/textures.js +240 -0
- package/dist-pkg/render/title-scene.d.ts +46 -0
- package/dist-pkg/render/title-scene.js +75 -0
- package/dist-pkg/render/viewports.d.ts +52 -0
- package/dist-pkg/render/viewports.js +185 -0
- package/dist-pkg/render/viz-axes.d.ts +98 -0
- package/dist-pkg/render/viz-axes.js +184 -0
- package/dist-pkg/render/viz-modes.d.ts +61 -0
- package/dist-pkg/render/viz-modes.js +74 -0
- package/dist-pkg/render/viz-setters.d.ts +130 -0
- package/dist-pkg/render/viz-setters.js +244 -0
- package/dist-pkg/render/weather.d.ts +96 -0
- package/dist-pkg/render/weather.js +171 -0
- package/dist-pkg/render/wheelchair-sprites.d.ts +24 -0
- package/dist-pkg/render/wheelchair-sprites.js +63 -0
- package/dist-pkg/render/world-tex.d.ts +18 -0
- package/dist-pkg/render/world-tex.js +87 -0
- package/dist-pkg/ui/activities-menu.d.ts +258 -0
- package/dist-pkg/ui/activities-menu.js +648 -0
- package/dist-pkg/ui/caa-sets.d.ts +39 -0
- package/dist-pkg/ui/caa-sets.js +65 -0
- package/dist-pkg/ui/changed-mark.d.ts +15 -0
- package/dist-pkg/ui/changed-mark.js +96 -0
- package/dist-pkg/ui/debug-panel.d.ts +67 -0
- package/dist-pkg/ui/debug-panel.js +173 -0
- package/dist-pkg/ui/dom.d.ts +24 -0
- package/dist-pkg/ui/dom.js +47 -0
- package/dist-pkg/ui/focus-trap.d.ts +71 -0
- package/dist-pkg/ui/focus-trap.js +99 -0
- package/dist-pkg/ui/fonts.d.ts +41 -0
- package/dist-pkg/ui/fonts.js +59 -0
- package/dist-pkg/ui/hud.d.ts +137 -0
- package/dist-pkg/ui/hud.js +229 -0
- package/dist-pkg/ui/item-announcement.d.ts +19 -0
- package/dist-pkg/ui/item-announcement.js +36 -0
- package/dist-pkg/ui/layout.d.ts +6 -0
- package/dist-pkg/ui/layout.js +54 -0
- package/dist-pkg/ui/loop-crash.d.ts +19 -0
- package/dist-pkg/ui/loop-crash.js +60 -0
- package/dist-pkg/ui/map-hub.d.ts +56 -0
- package/dist-pkg/ui/map-hub.js +138 -0
- package/dist-pkg/ui/menu-nav.d.ts +141 -0
- package/dist-pkg/ui/menu-nav.js +390 -0
- package/dist-pkg/ui/pause-icons.d.ts +327 -0
- package/dist-pkg/ui/pause-icons.js +620 -0
- package/dist-pkg/ui/reach-notice.d.ts +34 -0
- package/dist-pkg/ui/reach-notice.js +71 -0
- package/dist-pkg/ui/settings-audio.d.ts +139 -0
- package/dist-pkg/ui/settings-audio.js +544 -0
- package/dist-pkg/ui/settings-caa.d.ts +45 -0
- package/dist-pkg/ui/settings-caa.js +137 -0
- package/dist-pkg/ui/settings-controls.d.ts +100 -0
- package/dist-pkg/ui/settings-controls.js +152 -0
- package/dist-pkg/ui/settings-empathy.d.ts +62 -0
- package/dist-pkg/ui/settings-empathy.js +137 -0
- package/dist-pkg/ui/settings-motion.d.ts +95 -0
- package/dist-pkg/ui/settings-motion.js +282 -0
- package/dist-pkg/ui/settings-motor.d.ts +77 -0
- package/dist-pkg/ui/settings-motor.js +215 -0
- package/dist-pkg/ui/settings-panel.d.ts +76 -0
- package/dist-pkg/ui/settings-panel.js +187 -0
- package/dist-pkg/ui/settings-typo.d.ts +74 -0
- package/dist-pkg/ui/settings-typo.js +150 -0
- package/dist-pkg/ui/settings-visual.d.ts +111 -0
- package/dist-pkg/ui/settings-visual.js +252 -0
- package/dist-pkg/ui/shell.d.ts +239 -0
- package/dist-pkg/ui/shell.js +430 -0
- package/dist-pkg/ui/title.d.ts +36 -0
- package/dist-pkg/ui/title.js +60 -0
- package/dist-pkg/ui/vlibras.d.ts +11 -0
- package/dist-pkg/ui/vlibras.js +87 -0
- package/dist-pkg/ui/webcam.d.ts +7 -0
- package/dist-pkg/ui/webcam.js +88 -0
- package/docs/CREDITS.md +64 -0
- package/docs/LICENSES.md +132 -0
- package/package.json +115 -0
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// ui/menu-nav.ts — NAVEGAÇÃO UNIVERSAL de menus e diálogos: andar, escolher e voltar sem mouse.
|
|
3
|
+
//
|
|
4
|
+
// Este módulo é acessibilidade pura, e é o coração do pilar de a11y no que diz respeito a operação por
|
|
5
|
+
// teclado (WCAG 2.1.1) e a ordem de foco (2.4.3). A regra que ele encarna é a do projeto, escrita no game.js
|
|
6
|
+
// antes de qualquer código: QUALQUER menu aberto — o de pausa OU um submenu de configuração — é navegável
|
|
7
|
+
// pelos MESMOS seis gestos, e esses seis gestos valem igualmente para teclado, controle, olhos e fala:
|
|
8
|
+
// · cima/baixo/esquerda/direita — andam entre itens; nos `select`/`slider`, esquerda/direita AJUSTAM o valor
|
|
9
|
+
// · sim — confirma, alterna, entra
|
|
10
|
+
// · não — volta ao menu anterior; na raiz, volta ao jogo (= Continuar)
|
|
11
|
+
// Por isso o módulo não fala em "tecla": ele fala em INTENÇÃO (`NavKeys`). O teclado é só um dos tradutores —
|
|
12
|
+
// input/gamepad.ts monta exatamente o mesmo `{yes,no,up,down,left,right}` e chama `navDialog`/`navPause` daqui.
|
|
13
|
+
// É essa forma comum que faz "o jogo inteiro é operável de seis maneiras" ser verdade por construção e não por
|
|
14
|
+
// disciplina.
|
|
15
|
+
//
|
|
16
|
+
// O QUE VEIO, E DE ONDE (bloco "NAVEGAÇÃO UNIVERSAL de menus" do game.js)
|
|
17
|
+
// · `sharedDialogOpen` · `menuItems` · `menuFocus` · `dialogBack` — a leitura do "onde estou" e o voltar.
|
|
18
|
+
// · `navDialog` — andar dentro de um diálogo de configuração (o único lugar que ajusta select/slider).
|
|
19
|
+
// · `pauseSetSel` · `navPause` — andar dentro de um menu de pausa, que NÃO usa foco do navegador e sim uma
|
|
20
|
+
// classe de seleção própria (`.pm-sel`/`.pi-sel`), porque ele é desenhado dentro da tela do jogador.
|
|
21
|
+
// · `menuNavKey` — o tradutor de teclado, em fase de CAPTURA.
|
|
22
|
+
//
|
|
23
|
+
// A DUPLICAÇÃO QUE ESTA EXTRAÇÃO CUROU
|
|
24
|
+
// `sharedDialogOpen` do game.js e `topVisibleOverlay` de ui/settings-panel.ts eram A MESMA FUNÇÃO escrita
|
|
25
|
+
// duas vezes: mesmo seletor de escopo (`#game-region .overlay`), mesmo filtro (`!hidden`), mesma ordenação
|
|
26
|
+
// estável por z-index efetivo, mesmo "vence o último". Conferi as duas linha a linha antes de unificar. Aqui
|
|
27
|
+
// `sharedDialogOpen` é um ALIAS de `ctx.topVisibleOverlay` — o nome antigo sobrevive porque input/gamepad.ts o
|
|
28
|
+
// consome por esse nome, mas o CÓDIGO agora existe uma vez só, em ui/settings-panel.ts. O ganho não é linha de
|
|
29
|
+
// menos: enquanto eram duas, uma correção de escopo (é justamente o que o defeito 1 abaixo pede) podia ser
|
|
30
|
+
// aplicada num lado e não no outro, e o sintoma seria "às vezes o foco volta".
|
|
31
|
+
//
|
|
32
|
+
// ======================= DOIS DEFEITOS CONHECIDOS — PRESERVADOS VERBATIM, NÃO CONSERTE =======================
|
|
33
|
+
//
|
|
34
|
+
// DEFEITO 1 — o escopo de `sharedDialogOpen` não alcança os menus de pausa (WCAG 2.4.3 · foco perdido).
|
|
35
|
+
// O escopo é `#game-region .overlay`. Os nove diálogos de configuração estão lá (o `inCanvasMenus()` do game.js
|
|
36
|
+
// reparenta os onze elementos para dentro do `#game-region`), MAS os menus de pausa por tela são
|
|
37
|
+
// `.screen-pause` — classe diferente, construída por ui/pause-icons.ts, e nunca `.overlay`. Consequência:
|
|
38
|
+
// `menuFocus(sharedDialogOpen())` devolve `null` quando o último diálogo se fecha, `menuFocus` sai pelo guarda
|
|
39
|
+
// de nulo, e o foco fica no botão que acabou de ficar `hidden` — o navegador o joga no `<body>`. Quem sofre são
|
|
40
|
+
// `#typo` (closeTypo) e `#help` (closeHelp), os dois únicos que devolvem o foco por esse caminho.
|
|
41
|
+
//
|
|
42
|
+
// O QUE EU VERIFIQUEI ANTES DE ESCREVER ISTO (a nota do coordenador estava certa, e a conclusão dela também):
|
|
43
|
+
// li os NOVE `close*` e o `app/index.html` inteiro, um por um. De todos os nove, só UM devolve o foco de fato:
|
|
44
|
+
// · touchcfg → `#opt-touchcfg` — EXISTE (index.html, dentro do painel #movement). Funciona.
|
|
45
|
+
// · options → `#opt-controls` — NÃO existe no index.html. No-op silencioso.
|
|
46
|
+
// · movement → `#opt-movement` — NÃO existe. No-op.
|
|
47
|
+
// · visual → `#opt-visual` — NÃO existe. No-op.
|
|
48
|
+
// · audio → `#opt-sound` — NÃO existe. No-op.
|
|
49
|
+
// · animation → `#opt-animation` — NÃO existe (o próprio game.js já anota isso na linha do `#animation-close`).
|
|
50
|
+
// · empathy → `#opt-empathy` — NÃO existe. No-op.
|
|
51
|
+
// · typo/help → `menuFocus(sharedDialogOpen())` — o caminho quebrado descrito acima.
|
|
52
|
+
// Os ids que EXISTEM no index.html com prefixo `opt-` são: opt-title, opt-telas, opt-letra, opt-facil,
|
|
53
|
+
// opt-altmove, opt-touchcfg, opt-eyes, opt-modocego, opt-tts, opt-onebtn, opt-wheelchair, opt-hearing,
|
|
54
|
+
// opt-captions. Nenhum `#opt-visual`/`#opt-sound`/`#opt-movement`/`#opt-controls`/`#opt-empathy`/`#opt-animation`
|
|
55
|
+
// — são ganchos para uma barra de botões que ainda não existe. Ou seja: NÃO é "sete certos e dois errados". É
|
|
56
|
+
// "um certo e oito quebrados, por duas causas diferentes" — e a causa dos oito (botão inexistente) não é a
|
|
57
|
+
// mesma dos dois (escopo do seletor). Quem consertar precisa saber disso, porque arrumar só o escopo deixa oito
|
|
58
|
+
// painéis ainda largando o foco.
|
|
59
|
+
//
|
|
60
|
+
// DEFEITO 2 — `menuNavKey` trata Escape como "voltar", em CAPTURA, com `stopPropagation()`, e resolve por
|
|
61
|
+
// z-index em vez de pela cadeia registrada. `addEventListener('keydown', menuNavKey, true)` roda ANTES do
|
|
62
|
+
// ouvinte principal do game.js (que é de BOLHA, na mesma janela); o `stopPropagation()` na fase de captura
|
|
63
|
+
// impede o evento de descer e, portanto, de voltar — o ouvinte de bolha nunca vê a tecla. Efeitos verificados
|
|
64
|
+
// lendo os dois ouvintes lado a lado:
|
|
65
|
+
// (a) a cadeia `overlays.escapeTarget()` (ORDEM DE REGISTRO: options→movement→animation→visual→empathy→
|
|
66
|
+
// audio→typo→touchcfg→help) é código morto com o jogo pausado. Quem decide é o topo da pilha de z-index.
|
|
67
|
+
// O próprio game.js já anota isso ("MEDIDO no navegador") no ouvinte de bolha.
|
|
68
|
+
// (b) `#help` e `#touchcfg` estão registrados com `inEscapeChain:false` e por isso ficam fora da cadeia de Escape.
|
|
69
|
+
// Enquanto a captura os cobrir por z-index, Escape os fecha; se alguém tirar o `stopPropagation()` ou a
|
|
70
|
+
// fase de captura sem antes dar flag aos dois, a tecla cai no ouvinte de bolha e o
|
|
71
|
+
// `if(Escape||Enter) togglePause()` de lá DESPAUSA O JOGO com o diálogo de Ajuda ainda aberto.
|
|
72
|
+
// Ou seja: o comportamento correto de hoje depende de um `stopPropagation()`, não da cadeia — é uma rede
|
|
73
|
+
// de segurança acidental, e é exatamente o que o conserto vai mexer.
|
|
74
|
+
// (c) Escape é a MESMA intenção que "especial" do gamepad (`no`). Não há como distinguir "fechar diálogo" de
|
|
75
|
+
// "voltar ao jogo" — na raiz do menu de pausa, `no` faz `setPhase('playing')`.
|
|
76
|
+
// `menuNavKey` é exportado por si só (e não só instalado) justamente para o teste poder dispará-lo sem depender
|
|
77
|
+
// de fase de propagação, e `attach()` existe para o game.js instalar exatamente como antes.
|
|
78
|
+
//
|
|
79
|
+
// Os testes de `menu-nav` PINAM os dois comportamentos ACIMA como estão hoje. Eles falham de propósito se
|
|
80
|
+
// alguém "melhorar" a região sem tratar o conserto inteiro — que é o ponto: o conserto futuro precisa da rede.
|
|
81
|
+
// ============================================================================================================
|
|
82
|
+
//
|
|
83
|
+
// O QUE FICOU DE FORA, E POR QUÊ
|
|
84
|
+
// · `updateTitleLegend` foi para ui/shell.ts, não para cá. Ela não navega nada — não lê foco, não trata tecla,
|
|
85
|
+
// não anda entre itens; pinta o rodapé de UMA tela (o título) e quem a chama é `setPhase('title')`. Trazê-la
|
|
86
|
+
// obrigaria a casca a importar o módulo de navegação só para desenhar uma legenda.
|
|
87
|
+
// · `padKind()` não veio para lugar nenhum: JÁ ESTÁ em `input/touch.ts` (Onda A), com corpo idêntico, e a
|
|
88
|
+
// cópia do game.js não tem chamador nenhum. É código morto duplicado — apagar, não mover.
|
|
89
|
+
// · `navTitle`/`titleButtons` são de ui/activities-menu.ts (menu inicial), já extraídos.
|
|
90
|
+
// · A pilha de z-index, o registro de overlays e o fechar por id são de ui/settings-panel.ts. Este módulo
|
|
91
|
+
// CONSOME (`topVisibleOverlay`, `closeById`) e não reimplementa.
|
|
92
|
+
//
|
|
93
|
+
// INJEÇÃO E ORDEM DE BOOT
|
|
94
|
+
// · `phase` entra como binding vivo de core/state.ts (deixou de ser `let` do game.js na Fase 2) — mesmo
|
|
95
|
+
// precedente de game/session.ts e input/touch.ts. O que AINDA é `let` do game.js entra por getter:
|
|
96
|
+
// `vpPause` (`getPauseMenu(i)`), reatribuído por `buildGameHud`.
|
|
97
|
+
// · `closePadWiz` é LAZY (`(save) => gamepadApi.closePadWiz(save)`): input/gamepad.ts é `const` do game.js e o
|
|
98
|
+
// ctx DELE consome `sharedDialogOpen`/`navDialog`/`navPause` daqui. A dependência é mútua, e a única forma
|
|
99
|
+
// de quebrar o ciclo sem reordenar o boot é ela ser resolvida na CHAMADA. Mesmo motivo para `isCapturing`
|
|
100
|
+
// (ui/settings-controls.ts) e para `setPhase` (o envelope içado do game.js sobre ui/shell.ts).
|
|
101
|
+
// · `actionOf`/`whichPlayer` entram já ligados a input/keyboard-runtime.ts, que é `const` declarado bem no
|
|
102
|
+
// alto do game.js — mas entram como callback mesmo assim, porque o roteamento por jogador é dele e não daqui.
|
|
103
|
+
//
|
|
104
|
+
// SEM I/O NO IMPORT: o corpo do módulo só declara dados e funções puras. Todo efeito passa por `initMenuNav`.
|
|
105
|
+
//
|
|
106
|
+
// Ver docs/5-Refactoring/plano-modularizacao-mapa.md (C3).
|
|
107
|
+
// ---------------------------------------------------------------------------------------------------------
|
|
108
|
+
// PURO — a decisão de navegação, sem DOM. Testável no project `node`.
|
|
109
|
+
// ---------------------------------------------------------------------------------------------------------
|
|
110
|
+
/* As tabelas de tecla GENÉRICA (as que valem para qualquer jogador, mesmo sem remap). Viraram `Set` porque é
|
|
111
|
+
o que elas sempre foram semanticamente — pertinência, não ordem — e porque um `Set` nomeado deixa a tabela
|
|
112
|
+
auditável de fora (o teste importa a constante em vez de repetir os literais). Conteúdo VERBATIM. */
|
|
113
|
+
/** Confirma/entra. `NumpadEnter` conta aqui (mas NÃO pausa: ver o ouvinte de bolha do game.js). */
|
|
114
|
+
export const KEY_YES = new Set(['Space', 'KeyJ', 'Enter', 'NumpadEnter']);
|
|
115
|
+
/** Volta. ⚠️ DEFEITO 2: `Escape` é a MESMA intenção que a ação "especial" do gamepad. Verbatim. */
|
|
116
|
+
export const KEY_NO = new Set(['Escape']);
|
|
117
|
+
export const KEY_UP = new Set(['ArrowUp', 'KeyW']);
|
|
118
|
+
export const KEY_DOWN = new Set(['ArrowDown', 'KeyS']);
|
|
119
|
+
export const KEY_LEFT = new Set(['ArrowLeft', 'KeyA']);
|
|
120
|
+
export const KEY_RIGHT = new Set(['ArrowRight', 'KeyD']);
|
|
121
|
+
/**
|
|
122
|
+
* Traduz (tecla física, ação remapeada do dono da tecla) → intenção. `act` é `null` quando a tecla não é de
|
|
123
|
+
* nenhum jogador (tecla genérica). Verbatim das seis linhas de `menuNavKey`.
|
|
124
|
+
*/
|
|
125
|
+
export function menuKeyIntent(code, act) {
|
|
126
|
+
return {
|
|
127
|
+
yes: KEY_YES.has(code) || act === 'action2',
|
|
128
|
+
no: KEY_NO.has(code) || act === 'action3',
|
|
129
|
+
up: KEY_UP.has(code) || act === 'up',
|
|
130
|
+
down: KEY_DOWN.has(code) || act === 'down',
|
|
131
|
+
left: KEY_LEFT.has(code) || act === 'left',
|
|
132
|
+
right: KEY_RIGHT.has(code) || act === 'right',
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
/** Alguma intenção foi expressa? Definição única em input/edges; aqui só o nome que este módulo sempre teve. */
|
|
136
|
+
import { hasNavIntent as hasIntent } from '../input/edges.js';
|
|
137
|
+
import { mostrarSubmenuDaPausa, PM_ITENS_VISIVEIS } from './pause-icons.js';
|
|
138
|
+
import { anunciarItem } from './item-announcement.js';
|
|
139
|
+
import { rotuloAcessivel } from '../core/rotulo-acessivel.js';
|
|
140
|
+
import { passoNoAnel } from '../core/anel.js';
|
|
141
|
+
export { hasNavIntent as hasIntent } from '../input/edges.js';
|
|
142
|
+
// A CONTA DO ANEL mudou de casa para `core/anel` no item 7 do ADR-0044: `ui/pause-icons` passou a precisar
|
|
143
|
+
// dela, e como ESTE módulo já importa aquele, a volta fecharia um ciclo de importação — que em ESM não
|
|
144
|
+
// estoura na hora, estoura no boot em TDZ. O nome público fica: é daqui que os menus e os testes já a
|
|
145
|
+
// importavam, e mudar isso seria pedir uma edição em cada um deles para não ganhar nada.
|
|
146
|
+
export { passoNoAnel } from '../core/anel.js';
|
|
147
|
+
/** `select` com esquerda/direita: um passo, SEM dar a volta — ajustar VALOR não é navegar lista (ver acima). */
|
|
148
|
+
export function selectStep(selectedIndex, optionsLen, delta) {
|
|
149
|
+
return Math.max(0, Math.min(optionsLen - 1, selectedIndex + delta));
|
|
150
|
+
}
|
|
151
|
+
/** `select` com "sim": um passo, COM volta. É a diferença deliberada entre confirmar e ajustar. */
|
|
152
|
+
export function selectWrap(selectedIndex, optionsLen) {
|
|
153
|
+
return (selectedIndex + 1) % optionsLen;
|
|
154
|
+
}
|
|
155
|
+
/** `input[type=range]` com esquerda/direita: um `step` (default 1), preso entre `min` e `max`. */
|
|
156
|
+
export function rangeStep(value, min, max, step, delta) {
|
|
157
|
+
const st = step || 1; // `+cur.step||1`: step ausente/0/NaN vira 1, verbatim
|
|
158
|
+
return Math.max(min, Math.min(max, value + delta * st));
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* O PASSO DO CURSOR NO MENU DE PAUSA — um ANEL, agora que a pausa é uma lista (ADR-0044, itens 1 e 7).
|
|
162
|
+
*
|
|
163
|
+
* ISTO SUBSTITUI `pauseGridMove`, e a substituição é o desfecho do ADR-0044, não uma limpeza. O que havia
|
|
164
|
+
* era uma GRADE de duas zonas — a barra de dez ícones em cima, a lista de itens em duas colunas embaixo — e
|
|
165
|
+
* uma fronteira entre elas com quatro regras próprias ("de cima, 'baixo' cai sempre no primeiro item", "da
|
|
166
|
+
* primeira linha, 'cima' sobe para o ícone de mesmo índice", …). Cada regra dessas era uma coisa a mais para
|
|
167
|
+
* a criança descobrir sem ver, e nenhuma delas era descobrível: só se aprendia esbarrando.
|
|
168
|
+
*
|
|
169
|
+
* A XAG 106 permite laço para menu LINEAR e o PROÍBE para grade de duas dimensões — numa grade, dar a volta
|
|
170
|
+
* teleporta o cursor para o outro canto e a pessoa perde a noção de onde está. Era por isso que o anel do
|
|
171
|
+
* item 1 valia para todo menu do jogo MENOS este. Com a barra no HUD (item 7), o cartão passou a ter uma
|
|
172
|
+
* lista só, e o laço deixou de ser proibido para virar o recomendado.
|
|
173
|
+
*
|
|
174
|
+
* O que se ganha em troca das quatro regras: `quit` fica a UMA tecla para CIMA de `resume`. Último na
|
|
175
|
+
* leitura, vizinho no dedo.
|
|
176
|
+
*/
|
|
177
|
+
export function passoNaPausa(len, idx, k) {
|
|
178
|
+
const d = (k.down || k.right) ? 1 : -1;
|
|
179
|
+
return passoNoAnel(len, idx < 0 ? 0 : idx, d);
|
|
180
|
+
}
|
|
181
|
+
/** Os controles que contam como "item navegável" de um menu. Verbatim do seletor do `menuItems`. */
|
|
182
|
+
const ITEM_SELECTOR = 'button:not([disabled]), select:not([disabled]), input[type=range]:not([disabled])';
|
|
183
|
+
/** Onde os itens moram: o card do diálogo (`.overlay__card`) ou o card da pausa (`.pause-card`). */
|
|
184
|
+
const CARD_SELECTOR = '.overlay__card, .pause-card';
|
|
185
|
+
export function initMenuNav(ctx) {
|
|
186
|
+
/* ===================== diálogos de configuração ===================== */
|
|
187
|
+
// ALIAS, não cópia: o corpo mora em ui/settings-panel.ts. Ver "a duplicação que esta extração curou".
|
|
188
|
+
const sharedDialogOpen = () => ctx.topVisibleOverlay();
|
|
189
|
+
function menuItems(menu) {
|
|
190
|
+
const card = menu.querySelector(CARD_SELECTOR) || menu;
|
|
191
|
+
// `offsetParent === null` = fora do fluxo de layout (escondido, `display:none` ou dentro de aba oculta).
|
|
192
|
+
// É o único filtro de VISIBILIDADE real que existe aqui: sem ele o cursor pousa em botão invisível e o
|
|
193
|
+
// leitor de tela anuncia um controle que ninguém vê. Verbatim.
|
|
194
|
+
return [...card.querySelectorAll(ITEM_SELECTOR)].filter((el) => el.offsetParent !== null);
|
|
195
|
+
}
|
|
196
|
+
function menuFocus(menu) {
|
|
197
|
+
if (!menu)
|
|
198
|
+
return; // ⚠️ DEFEITO 1: com os menus de pausa fora do escopo, este guarda é o fim da linha
|
|
199
|
+
const it = menuItems(menu);
|
|
200
|
+
if (!it.length)
|
|
201
|
+
return;
|
|
202
|
+
const cur = it.indexOf(ctx.getActiveElement());
|
|
203
|
+
(cur >= 0 ? it[cur] : it[0]).focus();
|
|
204
|
+
}
|
|
205
|
+
function dialogBack(menu) {
|
|
206
|
+
if (!ctx.closeById(menu.id))
|
|
207
|
+
menu.hidden = true; // sem entrada no registro: some na marra
|
|
208
|
+
menuFocus(sharedDialogOpen());
|
|
209
|
+
}
|
|
210
|
+
/** Ajusta um `select` (esquerda/direita = passo preso; sim = passo com volta) e dispara `change`. */
|
|
211
|
+
function tweakSelect(el, delta) {
|
|
212
|
+
el.selectedIndex = delta === 'wrap' ? selectWrap(el.selectedIndex, el.options.length) : selectStep(el.selectedIndex, el.options.length, delta);
|
|
213
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
214
|
+
}
|
|
215
|
+
/** Ajusta um `input[type=range]` em um `step` e dispara `input` (é o evento que os painéis escutam). */
|
|
216
|
+
function tweakRange(el, delta) {
|
|
217
|
+
el.value = String(rangeStep(+el.value, +el.min, +el.max, +el.step, delta));
|
|
218
|
+
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
219
|
+
}
|
|
220
|
+
function navDialog(menu, k) {
|
|
221
|
+
const items = menuItems(menu);
|
|
222
|
+
if (!items.length)
|
|
223
|
+
return;
|
|
224
|
+
let idx = items.indexOf(ctx.getActiveElement());
|
|
225
|
+
if (idx < 0) {
|
|
226
|
+
idx = 0;
|
|
227
|
+
items[0].focus();
|
|
228
|
+
} // foco fora do diálogo (ou no card): entra pelo primeiro
|
|
229
|
+
const cur = items[idx];
|
|
230
|
+
if (k.no) {
|
|
231
|
+
dialogBack(menu);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (k.left || k.right) {
|
|
235
|
+
const d = k.right ? 1 : -1;
|
|
236
|
+
if (cur.tagName === 'SELECT') {
|
|
237
|
+
tweakSelect(cur, d);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
if (cur.tagName === 'INPUT') {
|
|
241
|
+
tweakRange(cur, d);
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
items[passoNoAnel(items.length, idx, d)].focus();
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
if (k.up || k.down) {
|
|
248
|
+
items[passoNoAnel(items.length, idx, k.down ? 1 : -1)].focus();
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
if (k.yes) {
|
|
252
|
+
if (cur.tagName === 'SELECT') {
|
|
253
|
+
tweakSelect(cur, 'wrap');
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
if (cur.tagName === 'INPUT')
|
|
257
|
+
return; // slider não tem "confirmar" — só ajuste
|
|
258
|
+
cur.click();
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
/* ===================== menu de pausa (seleção por classe, não por foco) ===================== */
|
|
262
|
+
/**
|
|
263
|
+
* Move o cursor do menu de pausa. Só ITENS: desde o item 7 do ADR-0044 a barra de ícones vive no HUD, e o
|
|
264
|
+
* cursor dela é dela.
|
|
265
|
+
*
|
|
266
|
+
* O QUE SAIU DAQUI, e vale registrar: esta função também escrevia a legenda dos ícones, porque o cursor
|
|
267
|
+
* atravessava a fronteira entre as duas zonas. Sem a fronteira, escrever legenda de ícone a partir do menu
|
|
268
|
+
* de pausa seria um módulo mexendo na tela de outro.
|
|
269
|
+
*/
|
|
270
|
+
function pauseSetSel(menu, el) {
|
|
271
|
+
if (!el)
|
|
272
|
+
return; // índice fora da lista (menu vazio): não mexe em nada — verbatim
|
|
273
|
+
menu.querySelectorAll('.pm-sel').forEach((b) => b.classList.remove('pm-sel'));
|
|
274
|
+
el.classList.add('pm-sel');
|
|
275
|
+
}
|
|
276
|
+
function navPause(menu, playerIndex, k) {
|
|
277
|
+
if (k.no) {
|
|
278
|
+
// "não" DENTRO do submenu de opções volta à raiz, e não ao jogo (ADR-0044, item 5). A regra "voltar sai
|
|
279
|
+
// um nível" já valia para os diálogos de configuração (`dialogBack`); o que mudou é que o menu de pausa
|
|
280
|
+
// passou a TER um nível a mais. Sem esta linha, quem entra em Opções sem enxergar só sairia despausando.
|
|
281
|
+
const aberto = menu.querySelector('.pause-menu:not([hidden])');
|
|
282
|
+
if (aberto && aberto.dataset.sub === 'opcoes') {
|
|
283
|
+
mostrarSubmenuDaPausa(menu, 'raiz');
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
ctx.setPhase('playing');
|
|
287
|
+
return; // "não" na raiz → volta ao jogo (retoma todos)
|
|
288
|
+
}
|
|
289
|
+
const items = [...menu.querySelectorAll(PM_ITENS_VISIVEIS)];
|
|
290
|
+
const cur = menu.querySelector('.pm-sel') || items[0];
|
|
291
|
+
// "sim": o jogador que agiu vira o `pauseActor` (o submenu de a11y abre na aba dele) e o item é clicado.
|
|
292
|
+
if (k.yes) {
|
|
293
|
+
ctx.setPauseActor(playerIndex);
|
|
294
|
+
if (cur)
|
|
295
|
+
cur.click();
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
// Guarda NOVA (o original estouraria em `cur.classList` aqui): menu sem `.pm-btn` nenhum. Não é o defeito
|
|
299
|
+
// preservado — é um TypeError, e trocar um crash por um no-op não tira rede de conserto nenhum.
|
|
300
|
+
if (!cur)
|
|
301
|
+
return;
|
|
302
|
+
// UMA LISTA, um anel. A barra de ícones saiu do cartão no item 7 do ADR-0044, e com ela saíram as quatro
|
|
303
|
+
// regras de fronteira que ninguém conseguia descobrir sem esbarrar.
|
|
304
|
+
const n = passoNaPausa(items.length, items.indexOf(cur), k);
|
|
305
|
+
pauseSetSel(menu, items[n]);
|
|
306
|
+
// E O ITEM NOVO É FALADO. Este menu não usa foco do navegador — seleciona por classe, porque é desenhado
|
|
307
|
+
// dentro da tela do jogador —, então nada dispara anúncio sozinho: nem foco, nem `aria-activedescendant`,
|
|
308
|
+
// nem região viva. MEDIDO no jogo construído: a seta andava e o `#sr-status` ficava vazio. O item 3 do
|
|
309
|
+
// ADR-0044 pede posição e total "em todo lugar", e este era o lugar onde ele não tinha chegado — o menu
|
|
310
|
+
// que o item 5 reconstruiu.
|
|
311
|
+
// `rotuloAcessivel` e não `textContent`: um item com `aria-label` seria narrado de um jeito pelo jogo e de
|
|
312
|
+
// outro pelo leitor de tela, e quem ouve os dois não teria como saber qual é a verdadeira.
|
|
313
|
+
ctx.srSay(anunciarItem({ rotulo: rotuloAcessivel(items[n]), posicao: n + 1, total: items.length }, ctx.comIndice()));
|
|
314
|
+
}
|
|
315
|
+
/* ===================== teclado ===================== */
|
|
316
|
+
/** O assistente de gamepad fica POR CIMA de tudo: com ele aberto, só Escape passa (e cancela). */
|
|
317
|
+
function padWizKey(e) {
|
|
318
|
+
const pw = ctx.$('#padwiz');
|
|
319
|
+
if (!pw || pw.hidden)
|
|
320
|
+
return false;
|
|
321
|
+
if (e.code === 'Escape') {
|
|
322
|
+
ctx.closePadWiz(false);
|
|
323
|
+
e.preventDefault();
|
|
324
|
+
e.stopPropagation();
|
|
325
|
+
}
|
|
326
|
+
return true; // aberto = consome (mesmo sem ser Escape: o resto do menu não navega por baixo dele)
|
|
327
|
+
}
|
|
328
|
+
/** Consome a tecla: ela era nossa, e ninguém mais deve vê-la. Uma função só para o par nunca se separar. */
|
|
329
|
+
const consumir = (e) => { e.preventDefault(); e.stopPropagation(); };
|
|
330
|
+
function menuNavKey(e) {
|
|
331
|
+
if (ctx.isCapturing())
|
|
332
|
+
return; // remap em andamento: a tecla é dele
|
|
333
|
+
if (padWizKey(e))
|
|
334
|
+
return;
|
|
335
|
+
// ===================== O MODO `accessibility` VEM ANTES DO GUARDA DE "NAVEGÁVEL" =====================
|
|
336
|
+
// `isNavigable()` é `phase === 'paused'`, e este modo roda com o jogo ANDANDO — é para isso que ele
|
|
337
|
+
// existe: ajustar a acessibilidade DURANTE a partida, sem parar. Se ele ficasse depois do guarda, a
|
|
338
|
+
// direção cairia no personagem e o modo não faria nada, que é a versão silenciosa da armadilha.
|
|
339
|
+
//
|
|
340
|
+
// No TECLADO a saída é Escape (o `no` do projeto). O START do controle é a segunda saída e entra por
|
|
341
|
+
// `input/gamepad`; aqui ele não tem par próprio, porque Enter já é "confirmar" e roubá-lo tiraria da
|
|
342
|
+
// criança o único jeito de ATIVAR o ícone sob o cursor.
|
|
343
|
+
const donoTecla = ctx.whichPlayer(e.code);
|
|
344
|
+
const pTecla = donoTecla < 0 ? 0 : donoTecla;
|
|
345
|
+
if (ctx.naBarraDe(pTecla)) {
|
|
346
|
+
const kb = menuKeyIntent(e.code, donoTecla >= 0 ? ctx.actionOf(e.code, pTecla) : null);
|
|
347
|
+
if (hasIntent(kb)) {
|
|
348
|
+
consumir(e);
|
|
349
|
+
ctx.navBar(pTecla, kb);
|
|
350
|
+
}
|
|
351
|
+
return; // na barra, tecla de menu é da barra — com ou sem intenção, não desce para o personagem
|
|
352
|
+
}
|
|
353
|
+
if (!ctx.isNavigable())
|
|
354
|
+
return;
|
|
355
|
+
const owner = ctx.whichPlayer(e.code);
|
|
356
|
+
const pi = owner < 0 ? 0 : owner; // tecla genérica → Jogador 1
|
|
357
|
+
const k = menuKeyIntent(e.code, owner >= 0 ? ctx.actionOf(e.code, pi) : null);
|
|
358
|
+
if (!hasIntent(k))
|
|
359
|
+
return;
|
|
360
|
+
// A TECLA SÓ É CONSUMIDA SE HOUVER O QUE NAVEGAR. Antes, `preventDefault()` + `stopPropagation()` vinham
|
|
361
|
+
// AQUI, antes de se saber se havia diálogo ou menu aberto — e o `menuNavKey` matava o evento para depois
|
|
362
|
+
// descobrir que não tinha nada a fazer com ele. Na plataforma isso era invisível: `isNavigable()` é
|
|
363
|
+
// `phase === 'paused'`, e pausar ABRE o menu, então quase nunca havia um caso "navegável e nada aberto".
|
|
364
|
+
//
|
|
365
|
+
// No segundo consumidor não era invisível — era total. Um quiz cujos ajustes estão SEMPRE disponíveis
|
|
366
|
+
// responde `isNavigable(): true`, e com isso TODA tecla com intenção de menu morria aqui: as setas de
|
|
367
|
+
// escolher alternativa, e o `S` do sonar. Duas funcionalidades que existiam e não chegavam à criança.
|
|
368
|
+
//
|
|
369
|
+
// A REDE DE SEGURANÇA DO ESCAPE FICA INTACTA, e era o risco real desta mudança. O bloco (b) acima explica
|
|
370
|
+
// que `#help` e `#touchcfg` dependem do `stopPropagation()` para não deixarem a tecla cair no ouvinte de
|
|
371
|
+
// bolha, que despausaria o jogo com o diálogo aberto. Esses dois casos entram por `sharedDialogOpen()`
|
|
372
|
+
// — há diálogo, logo a tecla É consumida, exatamente como antes.
|
|
373
|
+
const dlg = sharedDialogOpen();
|
|
374
|
+
if (dlg) {
|
|
375
|
+
consumir(e);
|
|
376
|
+
navDialog(dlg, k);
|
|
377
|
+
return;
|
|
378
|
+
} // diálogo de a11y aberto: navega ele (compartilhado)
|
|
379
|
+
const menu = ctx.getPauseMenu(pi); // senão: menu de pausa do PRÓPRIO jogador
|
|
380
|
+
if (menu && !menu.hidden) {
|
|
381
|
+
consumir(e);
|
|
382
|
+
navPause(menu, pi, k);
|
|
383
|
+
}
|
|
384
|
+
// Sem diálogo e sem menu: a tecla NÃO é nossa. Segue o caminho dela até quem for o dono.
|
|
385
|
+
}
|
|
386
|
+
function attach() {
|
|
387
|
+
ctx.win.addEventListener('keydown', menuNavKey, true);
|
|
388
|
+
}
|
|
389
|
+
return { sharedDialogOpen, menuItems, menuFocus, dialogBack, navDialog, pauseSetSel, navPause, menuNavKey, attach };
|
|
390
|
+
}
|