@the-inclusionist/engine 9.0.0 → 11.0.0
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/README.md +120 -78
- package/app/css/style.css +761 -377
- package/app/public/vendor/fonts/playwrite-cu.woff2 +0 -0
- package/app/public/vendor/fonts/playwrite-es-deco.woff2 +0 -0
- package/app/public/vendor/fonts/playwrite-es.woff2 +0 -0
- package/app/public/vendor/fonts/playwrite-gb-j.woff2 +0 -0
- package/app/public/vendor/fonts/playwrite-gb-s.woff2 +0 -0
- package/app/public/vendor/fonts/playwrite-pe.woff2 +0 -0
- package/app/public/vendor/fonts/playwrite-pt.woff2 +0 -0
- package/app/public/vendor/fonts-licences/NOTICE.txt +166 -0
- package/app/public/vendor/fonts-licences/OFL-1.1.txt +86 -0
- package/app/public/vendor/fonts.css +36 -84
- package/dist-pkg/boot/create-game.d.ts +499 -219
- package/dist-pkg/boot/create-game.js +3944 -562
- package/dist-pkg/core/a11y-sr.d.ts +25 -3
- package/dist-pkg/core/a11y-sr.js +38 -19
- package/dist-pkg/core/accessible-label.d.ts +17 -0
- package/dist-pkg/core/accessible-label.js +37 -0
- package/dist-pkg/core/accommodation-subjects.d.ts +26 -0
- package/dist-pkg/core/accommodation-subjects.js +32 -0
- package/dist-pkg/core/accommodations.d.ts +125 -0
- package/dist-pkg/core/accommodations.js +195 -0
- package/dist-pkg/core/actions.d.ts +108 -55
- package/dist-pkg/core/actions.js +122 -71
- package/dist-pkg/core/calm-mode.d.ts +31 -0
- package/dist-pkg/core/calm-mode.js +47 -0
- package/dist-pkg/core/camera-cycle.d.ts +15 -0
- package/dist-pkg/core/camera-cycle.js +7 -0
- package/dist-pkg/core/caption-duration.d.ts +15 -0
- package/dist-pkg/core/caption-duration.js +19 -0
- package/dist-pkg/core/cartridge-problems.d.ts +34 -0
- package/dist-pkg/core/cartridge-problems.js +53 -0
- package/dist-pkg/core/constants.d.ts +0 -54
- package/dist-pkg/core/constants.js +9 -76
- package/dist-pkg/core/contract.d.ts +142 -164
- package/dist-pkg/core/contract.js +222 -208
- package/dist-pkg/core/dom-query.d.ts +4 -4
- package/dist-pkg/core/dom-query.js +12 -20
- package/dist-pkg/core/entity.d.ts +62 -77
- package/dist-pkg/core/entity.js +18 -32
- package/dist-pkg/core/escape-html.d.ts +9 -10
- package/dist-pkg/core/escape-html.js +16 -20
- package/dist-pkg/core/flash-threshold.d.ts +17 -0
- package/dist-pkg/core/flash-threshold.js +113 -0
- package/dist-pkg/core/game-speed.d.ts +11 -0
- package/dist-pkg/core/game-speed.js +16 -0
- package/dist-pkg/core/genres.d.ts +20 -0
- package/dist-pkg/core/genres.js +44 -0
- package/dist-pkg/core/i18n.d.ts +71 -60
- package/dist-pkg/core/i18n.js +186 -200
- package/dist-pkg/core/loop.d.ts +17 -6
- package/dist-pkg/core/loop.js +26 -26
- package/dist-pkg/core/pause-icon-catalogue.d.ts +15 -0
- package/dist-pkg/core/pause-icon-catalogue.js +54 -0
- package/dist-pkg/core/ring.d.ts +17 -0
- package/dist-pkg/core/ring.js +29 -0
- package/dist-pkg/core/rng.d.ts +7 -12
- package/dist-pkg/core/rng.js +23 -53
- package/dist-pkg/core/route.d.ts +26 -26
- package/dist-pkg/core/route.js +126 -121
- package/dist-pkg/core/scenes.d.ts +37 -40
- package/dist-pkg/core/scenes.js +41 -43
- package/dist-pkg/core/screens.d.ts +4 -4
- package/dist-pkg/core/screens.js +11 -16
- package/dist-pkg/core/session-clock.d.ts +23 -0
- package/dist-pkg/core/session-clock.js +37 -0
- package/dist-pkg/core/setting-defaults.d.ts +61 -0
- package/dist-pkg/core/setting-defaults.js +66 -0
- package/dist-pkg/core/speech-rate.d.ts +29 -0
- package/dist-pkg/core/speech-rate.js +47 -0
- package/dist-pkg/core/state.d.ts +118 -118
- package/dist-pkg/core/state.js +322 -297
- package/dist-pkg/core/visual-cycles.d.ts +30 -0
- package/dist-pkg/core/visual-cycles.js +52 -0
- package/dist-pkg/core/visual-state.d.ts +13 -0
- package/dist-pkg/core/visual-state.js +6 -0
- package/dist-pkg/educational/activities-registry.d.ts +1 -1
- package/dist-pkg/educational/activities-registry.js +9 -8
- package/dist-pkg/educational/adaptive-engine.d.ts +11 -11
- package/dist-pkg/educational/adaptive-engine.js +17 -17
- package/dist-pkg/educational/segment-bar.d.ts +12 -12
- package/dist-pkg/educational/segment-bar.js +9 -9
- package/dist-pkg/i18n/en.js +324 -223
- package/dist-pkg/i18n/es.js +323 -223
- package/dist-pkg/i18n/pt.js +348 -226
- package/dist-pkg/input/default-bindings.d.ts +37 -38
- package/dist-pkg/input/default-bindings.js +127 -144
- package/dist-pkg/input/devices.d.ts +7 -7
- package/dist-pkg/input/devices.js +17 -16
- package/dist-pkg/input/edges.d.ts +23 -32
- package/dist-pkg/input/edges.js +23 -28
- package/dist-pkg/input/empathy-filter.d.ts +22 -0
- package/dist-pkg/input/empathy-filter.js +34 -0
- package/dist-pkg/input/face-map.d.ts +96 -0
- package/dist-pkg/input/face-map.js +255 -0
- package/dist-pkg/input/face-signals.d.ts +40 -0
- package/dist-pkg/input/face-signals.js +48 -0
- package/dist-pkg/input/gamepad.d.ts +195 -145
- package/dist-pkg/input/gamepad.js +383 -556
- package/dist-pkg/input/gaze-cycle.d.ts +64 -0
- package/dist-pkg/input/gaze-cycle.js +133 -0
- package/dist-pkg/input/gaze-relative.d.ts +68 -0
- package/dist-pkg/input/gaze-relative.js +142 -0
- package/dist-pkg/input/hand-map.d.ts +28 -0
- package/dist-pkg/input/hand-map.js +142 -0
- package/dist-pkg/input/input-cooldown.d.ts +17 -0
- package/dist-pkg/input/input-cooldown.js +39 -0
- package/dist-pkg/input/key-default.d.ts +30 -0
- package/dist-pkg/input/key-default.js +60 -0
- package/dist-pkg/input/keyboard-runtime.d.ts +15 -21
- package/dist-pkg/input/keyboard-runtime.js +23 -27
- package/dist-pkg/input/keyboard.d.ts +68 -63
- package/dist-pkg/input/keyboard.js +83 -105
- package/dist-pkg/input/keydown.d.ts +123 -137
- package/dist-pkg/input/keydown.js +284 -289
- package/dist-pkg/input/latch-edge.d.ts +33 -21
- package/dist-pkg/input/latch-edge.js +34 -35
- package/dist-pkg/input/latch-scope.d.ts +51 -50
- package/dist-pkg/input/latch-scope.js +61 -82
- package/dist-pkg/input/latch-store.d.ts +26 -30
- package/dist-pkg/input/latch-store.js +42 -50
- package/dist-pkg/input/latch-sync.d.ts +29 -29
- package/dist-pkg/input/latch-sync.js +25 -25
- package/dist-pkg/input/latch.d.ts +14 -17
- package/dist-pkg/input/latch.js +29 -37
- package/dist-pkg/input/pad-defaults.d.ts +13 -16
- package/dist-pkg/input/pad-defaults.js +21 -28
- package/dist-pkg/input/pad-reading.d.ts +76 -0
- package/dist-pkg/input/pad-reading.js +169 -0
- package/dist-pkg/input/pad-wizard.d.ts +99 -0
- package/dist-pkg/input/pad-wizard.js +256 -0
- package/dist-pkg/input/pointer-space.d.ts +19 -20
- package/dist-pkg/input/pointer-space.js +27 -32
- package/dist-pkg/input/pointer.d.ts +39 -40
- package/dist-pkg/input/pointer.js +42 -45
- package/dist-pkg/input/state.d.ts +101 -89
- package/dist-pkg/input/state.js +54 -134
- package/dist-pkg/input/switch-scan.d.ts +59 -0
- package/dist-pkg/input/switch-scan.js +67 -0
- package/dist-pkg/input/synthetic-source.d.ts +42 -0
- package/dist-pkg/input/synthetic-source.js +57 -0
- package/dist-pkg/input/touch-bindings.d.ts +127 -116
- package/dist-pkg/input/touch-bindings.js +158 -173
- package/dist-pkg/input/touch.d.ts +147 -72
- package/dist-pkg/input/touch.js +362 -126
- package/dist-pkg/input/transport-in-use.d.ts +107 -0
- package/dist-pkg/input/transport-in-use.js +130 -0
- package/dist-pkg/input/transports.d.ts +90 -117
- package/dist-pkg/input/transports.js +104 -106
- package/dist-pkg/input/virtual-controller.d.ts +82 -0
- package/dist-pkg/input/virtual-controller.js +69 -0
- package/dist-pkg/input/vocabulary-migration.d.ts +50 -49
- package/dist-pkg/input/vocabulary-migration.js +80 -79
- package/dist-pkg/input/voice-map.d.ts +59 -0
- package/dist-pkg/input/voice-map.js +123 -0
- package/dist-pkg/platform/audio-ambient.d.ts +13 -2
- package/dist-pkg/platform/audio-ambient.js +14 -14
- package/dist-pkg/platform/audio-earcons.d.ts +20 -19
- package/dist-pkg/platform/audio-earcons.js +51 -42
- package/dist-pkg/platform/audio-jingles.js +9 -10
- package/dist-pkg/platform/audio-mixer.d.ts +14 -2
- package/dist-pkg/platform/audio-mixer.js +31 -39
- package/dist-pkg/platform/audio-sonar.d.ts +49 -152
- package/dist-pkg/platform/audio-sonar.js +124 -302
- package/dist-pkg/platform/audio.d.ts +62 -19
- package/dist-pkg/platform/audio.js +193 -168
- package/dist-pkg/platform/choose-by-voice.d.ts +70 -0
- package/dist-pkg/platform/choose-by-voice.js +157 -0
- package/dist-pkg/platform/flash-sampler.d.ts +19 -0
- package/dist-pkg/platform/flash-sampler.js +79 -0
- package/dist-pkg/platform/font-library.d.ts +77 -0
- package/dist-pkg/platform/font-library.js +98 -0
- package/dist-pkg/platform/font-library.json +721 -0
- package/dist-pkg/platform/heavy-catalogue.d.ts +77 -0
- package/dist-pkg/platform/heavy-catalogue.js +256 -0
- package/dist-pkg/platform/heavy-mirror.d.ts +20 -0
- package/dist-pkg/platform/heavy-mirror.js +73 -0
- package/dist-pkg/platform/heavy.d.ts +136 -0
- package/dist-pkg/platform/heavy.js +324 -0
- package/dist-pkg/platform/interruptible-speech.d.ts +23 -16
- package/dist-pkg/platform/interruptible-speech.js +77 -54
- package/dist-pkg/platform/kokoro-port.d.ts +27 -0
- package/dist-pkg/platform/kokoro-port.js +50 -0
- package/dist-pkg/platform/kokoro-runtime.d.ts +20 -0
- package/dist-pkg/platform/kokoro-runtime.js +52 -0
- package/dist-pkg/platform/kokoro.d.ts +70 -0
- package/dist-pkg/platform/kokoro.js +133 -0
- package/dist-pkg/platform/listener-scope.d.ts +12 -0
- package/dist-pkg/platform/listener-scope.js +90 -0
- package/dist-pkg/platform/locale-host.d.ts +14 -0
- package/dist-pkg/platform/locale-host.js +45 -0
- package/dist-pkg/platform/microphone.d.ts +51 -0
- package/dist-pkg/platform/microphone.js +118 -0
- package/dist-pkg/platform/onnx-runtime.d.ts +34 -0
- package/dist-pkg/platform/onnx-runtime.js +42 -0
- package/dist-pkg/platform/reading-in-worker.d.ts +64 -0
- package/dist-pkg/platform/reading-in-worker.js +125 -0
- package/dist-pkg/platform/reading-model.d.ts +94 -0
- package/dist-pkg/platform/reading-model.js +272 -0
- package/dist-pkg/platform/reading-runtime.d.ts +24 -0
- package/dist-pkg/platform/reading-runtime.js +164 -0
- package/dist-pkg/platform/reading-worker.d.ts +41 -0
- package/dist-pkg/platform/reading-worker.js +58 -0
- package/dist-pkg/platform/reading.d.ts +80 -0
- package/dist-pkg/platform/reading.js +136 -0
- package/dist-pkg/platform/speech-recognition.d.ts +60 -0
- package/dist-pkg/platform/speech-recognition.js +104 -0
- package/dist-pkg/platform/speech.d.ts +23 -1
- package/dist-pkg/platform/speech.js +29 -18
- package/dist-pkg/platform/storage-keys.d.ts +96 -0
- package/dist-pkg/platform/storage-keys.js +106 -0
- package/dist-pkg/platform/storage.d.ts +46 -111
- package/dist-pkg/platform/storage.js +104 -169
- package/dist-pkg/platform/tts.d.ts +85 -39
- package/dist-pkg/platform/tts.js +447 -127
- package/dist-pkg/platform/vision-loop.d.ts +23 -0
- package/dist-pkg/platform/vision-loop.js +56 -0
- package/dist-pkg/platform/vision.d.ts +132 -0
- package/dist-pkg/platform/vision.js +117 -0
- package/dist-pkg/platform/voice-listener.d.ts +40 -0
- package/dist-pkg/platform/voice-listener.js +88 -0
- package/dist-pkg/platform/voice-plan.d.ts +15 -98
- package/dist-pkg/platform/voice-plan.js +56 -143
- package/dist-pkg/platform/vosk-runtime.d.ts +96 -0
- package/dist-pkg/platform/vosk-runtime.js +151 -0
- package/dist-pkg/platform/vosk-vocabulary.d.ts +6 -0
- package/dist-pkg/platform/vosk-vocabulary.js +178 -0
- package/dist-pkg/render/canvas.d.ts +13 -7
- package/dist-pkg/render/canvas.js +13 -13
- package/dist-pkg/render/crt.d.ts +48 -12
- package/dist-pkg/render/crt.js +78 -80
- package/dist-pkg/render/cvd-matrices.d.ts +22 -18
- package/dist-pkg/render/cvd-matrices.js +37 -38
- package/dist-pkg/render/hc-role-data.d.ts +7 -7
- package/dist-pkg/render/hc-role-data.js +14 -14
- package/dist-pkg/render/high-contrast.d.ts +67 -56
- package/dist-pkg/render/high-contrast.js +226 -217
- package/dist-pkg/render/low-vision-drawing.d.ts +5 -0
- package/dist-pkg/render/low-vision-drawing.js +46 -0
- package/dist-pkg/render/lq-filter.d.ts +32 -20
- package/dist-pkg/render/lq-filter.js +59 -65
- package/dist-pkg/render/port.d.ts +86 -88
- package/dist-pkg/render/port.js +17 -23
- package/dist-pkg/render/screen-pipeline.d.ts +42 -42
- package/dist-pkg/render/screen-pipeline.js +80 -87
- package/dist-pkg/render/sprite-fx.d.ts +2 -1
- package/dist-pkg/render/sprite-fx.js +16 -14
- package/dist-pkg/render/viewports.d.ts +20 -15
- package/dist-pkg/render/viewports.js +83 -114
- package/dist-pkg/render/viz-axes-labels.d.ts +47 -0
- package/dist-pkg/render/viz-axes-labels.js +88 -0
- package/dist-pkg/render/viz-axes.d.ts +89 -86
- package/dist-pkg/render/viz-axes.js +128 -116
- package/dist-pkg/render/viz-modes.d.ts +28 -29
- package/dist-pkg/render/viz-modes.js +40 -45
- package/dist-pkg/render/viz-refusal.d.ts +32 -0
- package/dist-pkg/render/viz-refusal.js +57 -0
- package/dist-pkg/render/viz-setters.d.ts +104 -66
- package/dist-pkg/render/viz-setters.js +182 -191
- package/dist-pkg/ui/aac-sets.d.ts +45 -0
- package/dist-pkg/ui/aac-sets.js +41 -0
- package/dist-pkg/ui/audio-choices.d.ts +68 -0
- package/dist-pkg/ui/audio-choices.js +98 -0
- package/dist-pkg/ui/audio-rows-that-apply.d.ts +12 -0
- package/dist-pkg/ui/audio-rows-that-apply.js +43 -0
- package/dist-pkg/ui/camera-control.d.ts +53 -0
- package/dist-pkg/ui/camera-control.js +48 -0
- package/dist-pkg/ui/changed-mark.d.ts +9 -8
- package/dist-pkg/ui/changed-mark.js +22 -53
- package/dist-pkg/ui/control-choices.d.ts +37 -0
- package/dist-pkg/ui/control-choices.js +84 -0
- package/dist-pkg/ui/debug-panel.d.ts +59 -43
- package/dist-pkg/ui/debug-panel.js +68 -69
- package/dist-pkg/ui/declared-words.d.ts +21 -0
- package/dist-pkg/ui/declared-words.js +66 -0
- package/dist-pkg/ui/dom.d.ts +10 -14
- package/dist-pkg/ui/dom.js +10 -47
- package/dist-pkg/ui/drawing-problems.d.ts +25 -0
- package/dist-pkg/ui/drawing-problems.js +94 -0
- package/dist-pkg/ui/eye-control.d.ts +36 -0
- package/dist-pkg/ui/eye-control.js +178 -0
- package/dist-pkg/ui/face-control.d.ts +25 -0
- package/dist-pkg/ui/face-control.js +183 -0
- package/dist-pkg/ui/focus-trap.d.ts +39 -49
- package/dist-pkg/ui/focus-trap.js +56 -65
- package/dist-pkg/ui/fonts.d.ts +157 -68
- package/dist-pkg/ui/fonts.js +305 -115
- package/dist-pkg/ui/footer-scroll-driver.d.ts +14 -0
- package/dist-pkg/ui/footer-scroll-driver.js +201 -0
- package/dist-pkg/ui/footer-scroll.d.ts +38 -0
- package/dist-pkg/ui/footer-scroll.js +55 -0
- package/dist-pkg/ui/game-options.d.ts +47 -0
- package/dist-pkg/ui/game-options.js +158 -0
- package/dist-pkg/ui/gaze-overlay.d.ts +64 -0
- package/dist-pkg/ui/gaze-overlay.js +165 -0
- package/dist-pkg/ui/hand-control.d.ts +25 -0
- package/dist-pkg/ui/hand-control.js +143 -0
- package/dist-pkg/ui/help-panel.d.ts +87 -0
- package/dist-pkg/ui/help-panel.js +242 -0
- package/dist-pkg/ui/hud-bands.d.ts +62 -0
- package/dist-pkg/ui/hud-bands.js +146 -0
- package/dist-pkg/ui/hud-row.d.ts +34 -0
- package/dist-pkg/ui/hud-row.js +80 -0
- package/dist-pkg/ui/hud.d.ts +73 -73
- package/dist-pkg/ui/hud.js +94 -98
- package/dist-pkg/ui/item-announcement.d.ts +13 -12
- package/dist-pkg/ui/item-announcement.js +25 -26
- package/dist-pkg/ui/layout.d.ts +103 -28
- package/dist-pkg/ui/layout.js +117 -99
- package/dist-pkg/ui/libras-avatar-clip.d.ts +30 -0
- package/dist-pkg/ui/libras-avatar-clip.js +86 -0
- package/dist-pkg/ui/libras-avatar-load.d.ts +52 -0
- package/dist-pkg/ui/libras-avatar-load.js +134 -0
- package/dist-pkg/ui/libras-avatar-plan.d.ts +102 -0
- package/dist-pkg/ui/libras-avatar-plan.js +211 -0
- package/dist-pkg/ui/libras-avatar-player.d.ts +28 -0
- package/dist-pkg/ui/libras-avatar-player.js +226 -0
- package/dist-pkg/ui/libras-avatar-stage.d.ts +37 -0
- package/dist-pkg/ui/libras-avatar-stage.js +208 -0
- package/dist-pkg/ui/libras-glosses.d.ts +28 -0
- package/dist-pkg/ui/libras-glosses.js +161 -0
- package/dist-pkg/ui/locale-flags.d.ts +16 -0
- package/dist-pkg/ui/locale-flags.js +30 -0
- package/dist-pkg/ui/loop-crash.d.ts +17 -17
- package/dist-pkg/ui/loop-crash.js +33 -62
- package/dist-pkg/ui/menu-intent.d.ts +61 -0
- package/dist-pkg/ui/menu-intent.js +88 -0
- package/dist-pkg/ui/menu-items.d.ts +20 -0
- package/dist-pkg/ui/menu-items.js +51 -0
- package/dist-pkg/ui/menu-nav.d.ts +98 -92
- package/dist-pkg/ui/menu-nav.js +398 -286
- package/dist-pkg/ui/mobility-choices.d.ts +26 -0
- package/dist-pkg/ui/mobility-choices.js +41 -0
- package/dist-pkg/ui/motion-choices.d.ts +31 -0
- package/dist-pkg/ui/motion-choices.js +68 -0
- package/dist-pkg/ui/motion-scene.d.ts +24 -19
- package/dist-pkg/ui/motion-scene.js +29 -61
- package/dist-pkg/ui/mount-panel.d.ts +81 -0
- package/dist-pkg/ui/mount-panel.js +61 -0
- package/dist-pkg/ui/panel-shell.d.ts +65 -31
- package/dist-pkg/ui/panel-shell.js +92 -66
- package/dist-pkg/ui/panel-widgets.d.ts +150 -0
- package/dist-pkg/ui/panel-widgets.js +294 -0
- package/dist-pkg/ui/pause-buttons.d.ts +35 -0
- package/dist-pkg/ui/pause-buttons.js +66 -0
- package/dist-pkg/ui/pause-icons.d.ts +261 -347
- package/dist-pkg/ui/pause-icons.js +671 -601
- package/dist-pkg/ui/pause-markup.d.ts +86 -0
- package/dist-pkg/ui/pause-markup.js +125 -0
- package/dist-pkg/ui/reach-notice.d.ts +22 -23
- package/dist-pkg/ui/reach-notice.js +55 -58
- package/dist-pkg/ui/scan-overlay.d.ts +23 -0
- package/dist-pkg/ui/scan-overlay.js +50 -0
- package/dist-pkg/ui/screen-text.d.ts +25 -0
- package/dist-pkg/ui/screen-text.js +152 -0
- package/dist-pkg/ui/session-clock.d.ts +32 -0
- package/dist-pkg/ui/session-clock.js +67 -0
- package/dist-pkg/ui/settings-aac.d.ts +70 -0
- package/dist-pkg/ui/settings-aac.js +208 -0
- package/dist-pkg/ui/settings-audio.d.ts +97 -114
- package/dist-pkg/ui/settings-audio.js +493 -425
- package/dist-pkg/ui/settings-controls.d.ts +53 -96
- package/dist-pkg/ui/settings-controls.js +143 -215
- package/dist-pkg/ui/settings-empathy.d.ts +39 -30
- package/dist-pkg/ui/settings-empathy.js +53 -60
- package/dist-pkg/ui/settings-mobility.d.ts +192 -0
- package/dist-pkg/ui/settings-mobility.js +332 -0
- package/dist-pkg/ui/settings-motion.d.ts +91 -70
- package/dist-pkg/ui/settings-motion.js +287 -198
- package/dist-pkg/ui/settings-panel.d.ts +58 -45
- package/dist-pkg/ui/settings-panel.js +189 -94
- package/dist-pkg/ui/settings-typo.d.ts +29 -78
- package/dist-pkg/ui/settings-typo.js +158 -129
- package/dist-pkg/ui/settings-visual.d.ts +29 -87
- package/dist-pkg/ui/settings-visual.js +240 -193
- package/dist-pkg/ui/shell.d.ts +112 -135
- package/dist-pkg/ui/shell.js +148 -217
- package/dist-pkg/ui/simulation-list.d.ts +27 -0
- package/dist-pkg/ui/simulation-list.js +76 -0
- package/dist-pkg/ui/simulation-over-the-world.d.ts +23 -0
- package/dist-pkg/ui/simulation-over-the-world.js +131 -0
- package/dist-pkg/ui/simulation-refusal.d.ts +5 -31
- package/dist-pkg/ui/simulation-refusal.js +8 -56
- package/dist-pkg/ui/switchable-control.d.ts +11 -0
- package/dist-pkg/ui/switchable-control.js +19 -0
- package/dist-pkg/ui/title.d.ts +3 -12
- package/dist-pkg/ui/title.js +49 -18
- package/dist-pkg/ui/top-band.d.ts +26 -0
- package/dist-pkg/ui/top-band.js +96 -0
- package/dist-pkg/ui/typo-choices.d.ts +71 -0
- package/dist-pkg/ui/typo-choices.js +85 -0
- package/dist-pkg/ui/visual-axes-panel.d.ts +11 -47
- package/dist-pkg/ui/visual-axes-panel.js +12 -94
- package/dist-pkg/ui/visual-choices.d.ts +77 -0
- package/dist-pkg/ui/visual-choices.js +107 -0
- package/dist-pkg/ui/vlibras.d.ts +77 -10
- package/dist-pkg/ui/vlibras.js +130 -81
- package/dist-pkg/ui/voice-control.d.ts +68 -0
- package/dist-pkg/ui/voice-control.js +276 -0
- package/dist-pkg/ui/voice-settings.d.ts +95 -0
- package/dist-pkg/ui/voice-settings.js +352 -0
- package/dist-pkg/ui/where-the-child-is.d.ts +31 -0
- package/dist-pkg/ui/where-the-child-is.js +70 -0
- package/docs/CREDITS.md +300 -44
- package/docs/LICENSES.md +189 -145
- package/package.json +51 -27
- package/scripts/check-cartridge.mjs +78 -0
- package/scripts/game-build.d.mts +25 -0
- package/scripts/game-build.mjs +144 -0
- package/scripts/heavy-into-the-delivery.mjs +388 -0
- package/scripts/libras-avatar.json +680 -0
- package/scripts/libras-avatar.mjs +246 -0
- package/scripts/libras-glosses/gloss.py +95 -0
- package/scripts/libras-glosses/pyproject.toml +32 -0
- package/scripts/libras-glosses/uv.lock +781 -0
- package/scripts/libras-glosses.mjs +351 -0
- package/scripts/licences/Apache-2.0.txt +176 -0
- package/scripts/licences/GPL-3.0.txt +674 -0
- package/scripts/licences/MIT.txt +17 -0
- package/scripts/licences/OFL-1.1.txt +86 -0
- package/scripts/licences/UFL-1.0.txt +96 -0
- package/scripts/licences/fonts.mjs +389 -0
- package/scripts/licences/third-party.mjs +301 -0
- package/scripts/licences/vosk-browser.NOTICE.txt +268 -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/fondamento-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/fondamento-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/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/opendyslexic-400.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/pressstart-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/ufmag-400.woff2 +0 -0
- package/dist-pkg/core/anel.d.ts +0 -17
- package/dist-pkg/core/anel.js +0 -31
- package/dist-pkg/core/collision.d.ts +0 -21
- package/dist-pkg/core/collision.js +0 -62
- package/dist-pkg/core/layers.d.ts +0 -63
- package/dist-pkg/core/layers.js +0 -95
- package/dist-pkg/core/letter-grid.d.ts +0 -55
- package/dist-pkg/core/letter-grid.js +0 -116
- package/dist-pkg/core/password.d.ts +0 -34
- package/dist-pkg/core/password.js +0 -205
- package/dist-pkg/core/rotulo-acessivel.d.ts +0 -17
- package/dist-pkg/core/rotulo-acessivel.js +0 -41
- package/dist-pkg/core/run-state.d.ts +0 -115
- package/dist-pkg/core/run-state.js +0 -40
- package/dist-pkg/core/tiles.d.ts +0 -12
- package/dist-pkg/core/tiles.js +0 -61
- package/dist-pkg/core/world.d.ts +0 -4
- package/dist-pkg/core/world.js +0 -58
- package/dist-pkg/input/origem-sintetica.d.ts +0 -44
- package/dist-pkg/input/origem-sintetica.js +0 -60
- package/dist-pkg/input/transporte-em-uso.d.ts +0 -101
- package/dist-pkg/input/transporte-em-uso.js +0 -130
- package/dist-pkg/platform/audio-nav.d.ts +0 -51
- package/dist-pkg/platform/audio-nav.js +0 -99
- package/dist-pkg/platform/guide-intensity.d.ts +0 -40
- package/dist-pkg/platform/guide-intensity.js +0 -73
- package/dist-pkg/platform/pesados-catalogo.d.ts +0 -14
- package/dist-pkg/platform/pesados-catalogo.js +0 -177
- package/dist-pkg/platform/pesados.d.ts +0 -31
- package/dist-pkg/platform/pesados.js +0 -75
- package/dist-pkg/platform/vozes-prontas.d.ts +0 -28
- package/dist-pkg/platform/vozes-prontas.js +0 -61
- package/dist-pkg/render/camera.d.ts +0 -58
- package/dist-pkg/render/camera.js +0 -105
- package/dist-pkg/render/cenario-data.d.ts +0 -127
- package/dist-pkg/render/cenario-data.js +0 -145
- package/dist-pkg/render/city-tex.d.ts +0 -63
- package/dist-pkg/render/city-tex.js +0 -228
- package/dist-pkg/render/city-tiles.d.ts +0 -11
- package/dist-pkg/render/city-tiles.js +0 -140
- package/dist-pkg/render/draw.d.ts +0 -149
- package/dist-pkg/render/draw.js +0 -231
- package/dist-pkg/render/fx.d.ts +0 -63
- package/dist-pkg/render/fx.js +0 -123
- package/dist-pkg/render/minimap.d.ts +0 -11
- package/dist-pkg/render/minimap.js +0 -94
- package/dist-pkg/render/parallax.d.ts +0 -85
- package/dist-pkg/render/parallax.js +0 -158
- package/dist-pkg/render/player-anim.d.ts +0 -71
- package/dist-pkg/render/player-anim.js +0 -152
- package/dist-pkg/render/recycling-tex.d.ts +0 -48
- package/dist-pkg/render/recycling-tex.js +0 -164
- package/dist-pkg/render/scene-city.d.ts +0 -77
- package/dist-pkg/render/scene-city.js +0 -181
- package/dist-pkg/render/scene-parallax.d.ts +0 -72
- package/dist-pkg/render/scene-parallax.js +0 -426
- package/dist-pkg/render/scene-sky.d.ts +0 -126
- package/dist-pkg/render/scene-sky.js +0 -295
- package/dist-pkg/render/set-cenario.d.ts +0 -54
- package/dist-pkg/render/set-cenario.js +0 -103
- package/dist-pkg/render/textures.d.ts +0 -75
- package/dist-pkg/render/textures.js +0 -240
- package/dist-pkg/render/title-scene.d.ts +0 -46
- package/dist-pkg/render/title-scene.js +0 -75
- package/dist-pkg/render/weather.d.ts +0 -96
- package/dist-pkg/render/weather.js +0 -171
- package/dist-pkg/render/wheelchair-sprites.d.ts +0 -32
- package/dist-pkg/render/wheelchair-sprites.js +0 -69
- package/dist-pkg/render/world-tex.d.ts +0 -18
- package/dist-pkg/render/world-tex.js +0 -87
- package/dist-pkg/ui/activities-menu.d.ts +0 -258
- package/dist-pkg/ui/activities-menu.js +0 -648
- package/dist-pkg/ui/caa-sets.d.ts +0 -39
- package/dist-pkg/ui/caa-sets.js +0 -65
- package/dist-pkg/ui/latch-refusal.d.ts +0 -32
- package/dist-pkg/ui/latch-refusal.js +0 -60
- package/dist-pkg/ui/map-hub.d.ts +0 -56
- package/dist-pkg/ui/map-hub.js +0 -138
- package/dist-pkg/ui/settings-caa.d.ts +0 -45
- package/dist-pkg/ui/settings-caa.js +0 -137
- package/dist-pkg/ui/settings-motor.d.ts +0 -175
- package/dist-pkg/ui/settings-motor.js +0 -342
- package/dist-pkg/ui/webcam.d.ts +0 -7
- package/dist-pkg/ui/webcam.js +0 -93
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
import type { PadTable } from './pad-defaults.js';
|
|
3
|
+
export interface PadButtonLike {
|
|
4
|
+
pressed: boolean;
|
|
5
|
+
}
|
|
6
|
+
export interface PadLike {
|
|
7
|
+
id: string;
|
|
8
|
+
index: number;
|
|
9
|
+
mapping: string;
|
|
10
|
+
buttons: readonly (PadButtonLike | null | undefined)[];
|
|
11
|
+
axes: readonly number[];
|
|
12
|
+
}
|
|
13
|
+
/** Adapter for `navigator.getGamepads()` — the DI point that lets tests feed a fake pad without a browser. */
|
|
14
|
+
export type GetGamepads = () => readonly (PadLike | null | undefined)[] | null | undefined;
|
|
15
|
+
/** One binding captured by the wizard: a digital button, a signed analog threshold, or an exact hat/POV value.
|
|
16
|
+
* Loosely-shaped (all fields optional) rather than a strict union: bindings round-trip through JSON in
|
|
17
|
+
* localStorage, so `bindActive` must stay defensive against malformed/partial saved data, same as the original. */
|
|
18
|
+
export interface PadBinding {
|
|
19
|
+
b?: number;
|
|
20
|
+
ax?: number;
|
|
21
|
+
s?: number;
|
|
22
|
+
av?: number;
|
|
23
|
+
v?: number;
|
|
24
|
+
}
|
|
25
|
+
/** action -> binding, keyed by the 9 wizard steps (left/right/up/down/jump/run/swap/especial/start), PLUS the
|
|
26
|
+
* `_skip: true` sentinel meaning "user cancelled the wizard for this model — use the default mapping, don't
|
|
27
|
+
* ask again this session" (not persisted). A flat index signature (not `Partial<Record<..>> & {_skip}`) so the
|
|
28
|
+
* boolean `_skip` and the PadBinding action values can coexist under TS's index-signature rule; `bindingAt`
|
|
29
|
+
* narrows a lookup back down to a PadBinding for `bindActive`. */
|
|
30
|
+
export type PadMap = Record<string, PadBinding | boolean | undefined>;
|
|
31
|
+
export type ActionKey = 'left' | 'right' | 'up' | 'down' | 'action2' | 'action1' | 'action4' | 'action3';
|
|
32
|
+
export interface Dirs {
|
|
33
|
+
left: boolean;
|
|
34
|
+
right: boolean;
|
|
35
|
+
up: boolean;
|
|
36
|
+
down: boolean;
|
|
37
|
+
}
|
|
38
|
+
export interface PadActions extends Dirs {
|
|
39
|
+
[key: string]: boolean;
|
|
40
|
+
action2: boolean;
|
|
41
|
+
action1: boolean;
|
|
42
|
+
action4: boolean;
|
|
43
|
+
action3: boolean;
|
|
44
|
+
_start: boolean;
|
|
45
|
+
_pause: boolean;
|
|
46
|
+
}
|
|
47
|
+
/** Directions from the STANDARD SOURCES: stick 0/1 (dead zone PAD_DEAD), D-pad 12-15, and the "hat" on axes >=6
|
|
48
|
+
* (DirectInput's POV). A pad with both directionals mapped keeps both alive (a thumb on the stick does not kill the D-pad). */
|
|
49
|
+
export declare function stdDirs(gp: PadLike): Dirs;
|
|
50
|
+
/** Is binding `bd` active now on this gamepad? Digital = pressed; analogue ({ax,s}) = threshold by sign (half the
|
|
51
|
+
* travel); hat ({av,v}) = the step's exact value (±0.13 — the 8 steps are ~0.286 apart). */
|
|
52
|
+
export declare function bindActive(gp: PadLike, bd: PadBinding | null | undefined): boolean;
|
|
53
|
+
/** This frame's actions for this gamepad. `custom` = the map the wizard saved for this `gp.id` (null/`_skip` = the
|
|
54
|
+
* declared table). ⚠️ The table decides only in the second branch, which is right: a game's default does not override
|
|
55
|
+
* a choice the child recorded. */
|
|
56
|
+
export declare function padActions(gp: PadLike, custom: PadMap | null, table?: PadTable): PadActions;
|
|
57
|
+
/**
|
|
58
|
+
* ONE BUTTON AT A TIME, APPLIED TO THE PAD — the missing half of the motor empathy simulation (issue #120).
|
|
59
|
+
*
|
|
60
|
+
* ⚠️ IT USED TO HOLD ONLY ON THE KEYBOARD. The keyboard transport releases every other game key when a new one arrives
|
|
61
|
+
* with the simulation on; the pad polling had no equivalent. A child who turned it on with a pad in hand **was not in
|
|
62
|
+
* it** — no error, no warning, no symptom, because the settings kept saying it was on.
|
|
63
|
+
*
|
|
64
|
+
* ⚠️ THE DIRECTIONS COUNT, which is what makes the rule faithful to the keyboard: there, the game keys include the four
|
|
65
|
+
* directions — walking and jumping do not coexist. A filter that spared the directions would be more comfortable and
|
|
66
|
+
* would be simulating another disability.
|
|
67
|
+
*
|
|
68
|
+
* ⚠️ AND WHO SURVIVES DIFFERS FROM THE KEYBOARD, BY NECESSITY. On the keyboard the new arrival wins, because there IS an
|
|
69
|
+
* arrival: the event says which. A pad is read by POLLING — what arrives is a snapshot, without order. So the one that
|
|
70
|
+
* already held stays, and only when it lets go does the next take over. That keeps the run button from being cut because
|
|
71
|
+
* the thumb brushed another, and it is the reading of "holding" ADR-0077 gives.
|
|
72
|
+
*
|
|
73
|
+
* The POLICY is the keyboard's; the IMPLEMENTATION cannot be shared today because the state's shapes differ — there a
|
|
74
|
+
* `Set` of key codes, here a snapshot of booleans by position.
|
|
75
|
+
*/
|
|
76
|
+
export declare function oneButtonAtOnce(wasDown: Readonly<Record<string, boolean | undefined>>, reading: PadActions, on: boolean): PadActions;
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// input/pad-reading — WHAT A GAMEPAD IS DOING RIGHT NOW, and nothing else.
|
|
3
|
+
//
|
|
4
|
+
// 🔴 WHY IT IS ITS OWN MODULE. `input/gamepad` carried two jobs under one name: this one — buttons and axes in,
|
|
5
|
+
// positions out, no host, no state, no clock — and the DI runtime that polls the pads every frame and decides
|
|
6
|
+
// where the reading goes (title, pause, seat, play, wizard). The second is the engine's wiring and can only be
|
|
7
|
+
// read with a ctx in hand; the first is arithmetic over a snapshot, and every one of its rules is a promise to a
|
|
8
|
+
// child whose controller the browser does not recognise: a D-pad on a DirectInput POV hat, an analogue axis used
|
|
9
|
+
// as a button, a map the child recorded in the wizard.
|
|
10
|
+
//
|
|
11
|
+
// 📌 Nothing here reads or writes state. The frame-to-frame memory (`padCur`, `padPrevAct`) belongs to whoever
|
|
12
|
+
// polls — one root's `input/state` (ADR-0232 D4).
|
|
13
|
+
import { GAMEPAD_STANDARD } from './default-bindings.js';
|
|
14
|
+
/**
|
|
15
|
+
* THE STICK'S DEAD ZONE = the first HALF of its travel (ergonomics — the Dev's decision). A constant, and so it lives
|
|
16
|
+
* here with the reading that uses it, not in `input/state`, which is a factory now (ADR-0232 D4). Internal: nothing
|
|
17
|
+
* outside this reading asks for it, in this repository or in the games.
|
|
18
|
+
*/
|
|
19
|
+
const PAD_DEAD = 0.5;
|
|
20
|
+
function bindingAt(map, key) {
|
|
21
|
+
const v = map[key];
|
|
22
|
+
return typeof v === 'object' && v !== null ? v : undefined;
|
|
23
|
+
}
|
|
24
|
+
// [axisValue, up, down, left, right] — the 8 steps of a "POV hat" D-pad (DirectInput's high axis), resting at ~1.286.
|
|
25
|
+
const HAT_STEPS = [
|
|
26
|
+
[-1, 1, 0, 0, 0], [-0.7143, 1, 0, 0, 1], [-0.4286, 0, 0, 0, 1], [-0.1429, 0, 1, 0, 1],
|
|
27
|
+
[0.1429, 0, 1, 0, 0], [0.4286, 0, 1, 1, 0], [0.7143, 0, 0, 1, 0], [1, 1, 0, 1, 0],
|
|
28
|
+
];
|
|
29
|
+
/** The hat step this axis value IS, or `null` — the eight are ~0.286 apart, and the tolerance is ±0.09. */
|
|
30
|
+
function hatStepAt(v) {
|
|
31
|
+
if (typeof v !== 'number' || Math.abs(v) > 1.001)
|
|
32
|
+
return null; // the hat's rest, outside the steps
|
|
33
|
+
return HAT_STEPS.find(([hv]) => Math.abs(v - hv) <= 0.09) ?? null;
|
|
34
|
+
}
|
|
35
|
+
/** A hat step LIGHTS directions and never puts them out — which keeps the stick and the D-pad alive at once. */
|
|
36
|
+
function applyHatStep(d, step) {
|
|
37
|
+
const [, up, down, left, right] = step;
|
|
38
|
+
if (up)
|
|
39
|
+
d.up = true;
|
|
40
|
+
if (down)
|
|
41
|
+
d.down = true;
|
|
42
|
+
if (left)
|
|
43
|
+
d.left = true;
|
|
44
|
+
if (right)
|
|
45
|
+
d.right = true;
|
|
46
|
+
}
|
|
47
|
+
/** Directions from the STANDARD SOURCES: stick 0/1 (dead zone PAD_DEAD), D-pad 12-15, and the "hat" on axes >=6
|
|
48
|
+
* (DirectInput's POV). A pad with both directionals mapped keeps both alive (a thumb on the stick does not kill the D-pad). */
|
|
49
|
+
export function stdDirs(gp) {
|
|
50
|
+
const b = (i) => !!(gp.buttons[i] && gp.buttons[i].pressed);
|
|
51
|
+
const ax = (i) => gp.axes[i] || 0;
|
|
52
|
+
const d = { left: ax(0) < -PAD_DEAD || b(14), right: ax(0) > PAD_DEAD || b(15), up: ax(1) < -PAD_DEAD || b(12), down: ax(1) > PAD_DEAD || b(13) };
|
|
53
|
+
for (let i = 6; i < gp.axes.length; i++) {
|
|
54
|
+
const step = hatStepAt(gp.axes[i]);
|
|
55
|
+
if (step)
|
|
56
|
+
applyHatStep(d, step);
|
|
57
|
+
}
|
|
58
|
+
return d;
|
|
59
|
+
}
|
|
60
|
+
/** Is binding `bd` active now on this gamepad? Digital = pressed; analogue ({ax,s}) = threshold by sign (half the
|
|
61
|
+
* travel); hat ({av,v}) = the step's exact value (±0.13 — the 8 steps are ~0.286 apart). */
|
|
62
|
+
export function bindActive(gp, bd) {
|
|
63
|
+
if (!bd)
|
|
64
|
+
return false;
|
|
65
|
+
if (bd.b != null)
|
|
66
|
+
return !!(gp.buttons[bd.b] && gp.buttons[bd.b].pressed);
|
|
67
|
+
if (bd.ax != null)
|
|
68
|
+
return ((gp.axes[bd.ax] || 0) * (bd.s ?? 0)) > 0.5;
|
|
69
|
+
if (bd.av != null)
|
|
70
|
+
return Math.abs((gp.axes[bd.av] || 0) - (bd.v ?? 0)) <= 0.13;
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
/** The snapshot when the CHILD recorded a map in the wizard: their directions fall back to the standard ones when the
|
|
74
|
+
* binding is not active, so the D-pad and the stick stay alive beside what they chose.
|
|
75
|
+
* ⚠️ `start` and `select` are read AS POSITIONS, as the table's reading does: the wizard asks them and stores them, and a
|
|
76
|
+
* map read without `select` left a mapped pad with no door to the menus — not even as the one-button scan's switch
|
|
77
|
+
* (ADR-0155, ADR-0218 §4). */
|
|
78
|
+
function actionsFromTheSavedMap(gp, custom) {
|
|
79
|
+
const A = (k) => bindActive(gp, bindingAt(custom, k));
|
|
80
|
+
const sd = stdDirs(gp);
|
|
81
|
+
return {
|
|
82
|
+
left: A('left') || sd.left, right: A('right') || sd.right, up: A('up') || sd.up, down: A('down') || sd.down,
|
|
83
|
+
action2: A('action2'), action1: A('action1'), action4: A('action4'), action3: A('action3'),
|
|
84
|
+
start: A('start'), select: A('select'),
|
|
85
|
+
_start: A('action2') || A('start'), _pause: A('start'),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* The snapshot by the declared TABLE — the engine's factory with this game's default on top (ADR-0115), resolved in
|
|
90
|
+
* `input/pad-defaults`.
|
|
91
|
+
*
|
|
92
|
+
* ⚠️ THE INDICES COME FROM THE TABLE, not from literals here. While they were literals, this line and
|
|
93
|
+
* `input/default-bindings` DISAGREED and nothing noticed — the defect the touch gate caught: two tables agreeing with
|
|
94
|
+
* each other prove nothing about a third that reads them.
|
|
95
|
+
*
|
|
96
|
+
* ⚠️ AND THE DISAGREEMENT WAS REAL: here `action1` was `b(2) || b(5) || b(7)` — X, R1 and R2 all running — while the table
|
|
97
|
+
* declares R1 as `rightShoulder` and R2 as `rightTrigger`. ADR-0086 recorded it as the asterisk of its "zero movement": no
|
|
98
|
+
* VERB changes button, but that one loses two of its three.
|
|
99
|
+
*/
|
|
100
|
+
function actionsFromTheTable(gp, table) {
|
|
101
|
+
const b = (i) => !!(gp.buttons[i] && gp.buttons[i].pressed);
|
|
102
|
+
const sd = stdDirs(gp);
|
|
103
|
+
const at = (a) => { const i = table[a]; return typeof i === 'number' ? b(i) : false; };
|
|
104
|
+
return {
|
|
105
|
+
left: sd.left, right: sd.right, up: sd.up, down: sd.down,
|
|
106
|
+
action1: at('action1'), action2: at('action2'), action3: at('action3'), action4: at('action4'),
|
|
107
|
+
leftShoulder: at('leftShoulder'), leftTrigger: at('leftTrigger'),
|
|
108
|
+
rightShoulder: at('rightShoulder'), rightTrigger: at('rightTrigger'),
|
|
109
|
+
// ⚠️ `start` AS A POSITION, not only as the derived ones below. It was missing, and the table's gate found it:
|
|
110
|
+
// whoever wanted to know "is START pressed?" had to read `_start`, which starts with an underscore and means
|
|
111
|
+
// something else (closes a dialog, and accepts action 2 too).
|
|
112
|
+
start: at('start'), select: at('select'),
|
|
113
|
+
// `_start` and `_pause` are DERIVED, not positions: "close dialog" accepts action 2 or START, "pause" only START.
|
|
114
|
+
// They are written here because they describe what the root does with two positions, not a third one.
|
|
115
|
+
_start: at('action2') || at('start'), _pause: at('start'),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/** This frame's actions for this gamepad. `custom` = the map the wizard saved for this `gp.id` (null/`_skip` = the
|
|
119
|
+
* declared table). ⚠️ The table decides only in the second branch, which is right: a game's default does not override
|
|
120
|
+
* a choice the child recorded. */
|
|
121
|
+
export function padActions(gp, custom, table = GAMEPAD_STANDARD) {
|
|
122
|
+
return custom && !custom._skip ? actionsFromTheSavedMap(gp, custom) : actionsFromTheTable(gp, table);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* THE POSITIONS THE ONE-BUTTON SIMULATION DOES NOT CUT. Pausing is the WAY OUT, not a move.
|
|
126
|
+
*
|
|
127
|
+
* ⚠️ Cutting START would trap the child inside the match — the reasoning that puts "the way out first" in ADR-0044 and
|
|
128
|
+
* made the focus trap exist in ADR-0090. An accommodation that locks is not an accommodation. The derived ones (`_start`,
|
|
129
|
+
* `_pause`) pass too: they describe what the root does with these two positions, not a third.
|
|
130
|
+
*/
|
|
131
|
+
const OUTSIDE_CUT = new Set(['start', 'select', '_start', '_pause']);
|
|
132
|
+
/**
|
|
133
|
+
* ONE BUTTON AT A TIME, APPLIED TO THE PAD — the missing half of the motor empathy simulation (issue #120).
|
|
134
|
+
*
|
|
135
|
+
* ⚠️ IT USED TO HOLD ONLY ON THE KEYBOARD. The keyboard transport releases every other game key when a new one arrives
|
|
136
|
+
* with the simulation on; the pad polling had no equivalent. A child who turned it on with a pad in hand **was not in
|
|
137
|
+
* it** — no error, no warning, no symptom, because the settings kept saying it was on.
|
|
138
|
+
*
|
|
139
|
+
* ⚠️ THE DIRECTIONS COUNT, which is what makes the rule faithful to the keyboard: there, the game keys include the four
|
|
140
|
+
* directions — walking and jumping do not coexist. A filter that spared the directions would be more comfortable and
|
|
141
|
+
* would be simulating another disability.
|
|
142
|
+
*
|
|
143
|
+
* ⚠️ AND WHO SURVIVES DIFFERS FROM THE KEYBOARD, BY NECESSITY. On the keyboard the new arrival wins, because there IS an
|
|
144
|
+
* arrival: the event says which. A pad is read by POLLING — what arrives is a snapshot, without order. So the one that
|
|
145
|
+
* already held stays, and only when it lets go does the next take over. That keeps the run button from being cut because
|
|
146
|
+
* the thumb brushed another, and it is the reading of "holding" ADR-0077 gives.
|
|
147
|
+
*
|
|
148
|
+
* The POLICY is the keyboard's; the IMPLEMENTATION cannot be shared today because the state's shapes differ — there a
|
|
149
|
+
* `Set` of key codes, here a snapshot of booleans by position.
|
|
150
|
+
*/
|
|
151
|
+
export function oneButtonAtOnce(
|
|
152
|
+
// ⚠️ THE PREVIOUS FRAME IS TYPED BY WHAT THIS FUNCTION READS, not by `PadActions`: the loop's previous-frame state is
|
|
153
|
+
// looser, and demanding the full shape would force the caller into a mould that does not describe what happens here —
|
|
154
|
+
// the only question is "was this key down?".
|
|
155
|
+
wasDown, reading, on) {
|
|
156
|
+
if (!on)
|
|
157
|
+
return reading;
|
|
158
|
+
const cuttable = Object.keys(reading).filter((k) => !OUTSIDE_CUT.has(k));
|
|
159
|
+
const active = cuttable.filter((k) => reading[k] === true);
|
|
160
|
+
if (active.length <= 1)
|
|
161
|
+
return reading;
|
|
162
|
+
// The one that already held has priority; with none, the snapshot's first takes over.
|
|
163
|
+
const kept = active.find((k) => wasDown[k] === true) ?? active[0];
|
|
164
|
+
const onlyOne = { ...reading };
|
|
165
|
+
for (const k of active)
|
|
166
|
+
if (k !== kept)
|
|
167
|
+
onlyOne[k] = false;
|
|
168
|
+
return onlyOne;
|
|
169
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
import type { Translate } from '../core/i18n.js';
|
|
3
|
+
import type { Store } from '../platform/storage.js';
|
|
4
|
+
interface PadLike {
|
|
5
|
+
readonly id: string;
|
|
6
|
+
readonly index: number;
|
|
7
|
+
readonly buttons: readonly ({
|
|
8
|
+
pressed: boolean;
|
|
9
|
+
} | null | undefined)[];
|
|
10
|
+
readonly axes: readonly number[];
|
|
11
|
+
}
|
|
12
|
+
type GetGamepads = () => readonly (PadLike | null | undefined)[] | null | undefined;
|
|
13
|
+
interface PadBinding {
|
|
14
|
+
b?: number;
|
|
15
|
+
ax?: number;
|
|
16
|
+
s?: number;
|
|
17
|
+
av?: number;
|
|
18
|
+
v?: number;
|
|
19
|
+
}
|
|
20
|
+
type PadMap = Record<string, PadBinding | boolean | undefined>;
|
|
21
|
+
/** This pad's resting pose: which buttons were already down and where each axis rested. */
|
|
22
|
+
interface Baseline {
|
|
23
|
+
b: boolean[];
|
|
24
|
+
a: number[];
|
|
25
|
+
}
|
|
26
|
+
interface WizState {
|
|
27
|
+
gi: number;
|
|
28
|
+
id: string;
|
|
29
|
+
step: number;
|
|
30
|
+
base: Baseline | null;
|
|
31
|
+
map: PadMap;
|
|
32
|
+
release: boolean;
|
|
33
|
+
baseWait: boolean;
|
|
34
|
+
axTrack: {
|
|
35
|
+
i: number;
|
|
36
|
+
v: number;
|
|
37
|
+
last: number;
|
|
38
|
+
changes: number;
|
|
39
|
+
ticks: number;
|
|
40
|
+
} | null;
|
|
41
|
+
timer: ReturnType<typeof setInterval> | null;
|
|
42
|
+
}
|
|
43
|
+
export declare const PADWIZ_ORDER: readonly string[];
|
|
44
|
+
/** What this module reads and writes the maps through: the page's store, built by the root (ADR-0232, issue #207). */
|
|
45
|
+
export type PadMapStore = Pick<Store, 'getJSON' | 'setJSON'>;
|
|
46
|
+
/** ONE ROOT'S STORED PAD MAPS, per pad id — what `createPadMaps` returns. */
|
|
47
|
+
export interface PadMaps {
|
|
48
|
+
/**
|
|
49
|
+
* The stored map of pad `id`, or `null`. Read through the vocabulary translator: a map saved before ADR-0086 has the old
|
|
50
|
+
* action keys, and a custom pad would otherwise stop answering with no word said.
|
|
51
|
+
*/
|
|
52
|
+
padMap(id: string): PadMap | null;
|
|
53
|
+
/** Stores the map of pad `id` and makes it the one read from now on. */
|
|
54
|
+
store(id: string, map: PadMap): void;
|
|
55
|
+
/** A cancelled wizard: the DEFAULT map for this session, not stored, so the wizard does not reopen in a loop. */
|
|
56
|
+
skip(id: string): void;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* THE CACHE OF STORED MAPS, one per ROOT (ADR-0232 D4). ⚠️ Not safe to share: two roots on two stores would read each
|
|
60
|
+
* other's map, and the one the page's first wizard saved would answer in a root whose child never recorded it. The root
|
|
61
|
+
* builds ONE and hands it to both readers — its own wizard and `initGamepad` — so a map saved by the wizard is the one the
|
|
62
|
+
* pad reads on the next frame, not a copy cached before.
|
|
63
|
+
*/
|
|
64
|
+
export declare function createPadMaps(store: PadMapStore): PadMaps;
|
|
65
|
+
export interface PadWizardCtx {
|
|
66
|
+
/** Translates in the page's language — the root's translator (ADR-0232 D3). REQUIRED: text built from nowhere is a raw key. */
|
|
67
|
+
t: Translate;
|
|
68
|
+
/** Where the finished map is stored — this root's maps (`createPadMaps`). Required: a wizard that saved nowhere would ask
|
|
69
|
+
* the child the fourteen questions again at every visit, in silence. */
|
|
70
|
+
maps: PadMaps;
|
|
71
|
+
getGamepads: GetGamepads;
|
|
72
|
+
/**
|
|
73
|
+
* The position's name in the GAME's word and the language of now; `null` = the game does not use it (the step is skipped).
|
|
74
|
+
* ⚠️ Not asked about `start` and `select`: the wizard always asks them, in the engine's words (`SYSTEM_WORDS`).
|
|
75
|
+
*/
|
|
76
|
+
actionLabel: (action: string) => string | null;
|
|
77
|
+
/** Shows and says the wizard's sentence. */
|
|
78
|
+
say: (phrase: string) => void;
|
|
79
|
+
/** Shows what is mapped so far. */
|
|
80
|
+
progress: (text: string) => void;
|
|
81
|
+
srAlert: (phrase: string) => void;
|
|
82
|
+
/** The step that starts (`null` while waiting for a pad) — the host's demonstration, when it has one. */
|
|
83
|
+
onStep?: (action: string | null) => void;
|
|
84
|
+
/** Every tick while open — the host's animation, when it has one. */
|
|
85
|
+
onTick?: () => void;
|
|
86
|
+
/** After closing: the pad index mapped (-1 if none was identified) and whether the map was saved. */
|
|
87
|
+
onClose?: (gi: number, saved: boolean) => void;
|
|
88
|
+
}
|
|
89
|
+
export interface PadWizard {
|
|
90
|
+
/** Opens waiting for any pad to press a button. */
|
|
91
|
+
open(): void;
|
|
92
|
+
/** Opens for a pad already identified. */
|
|
93
|
+
openFor(gp: PadLike): void;
|
|
94
|
+
close(save: boolean): void;
|
|
95
|
+
tick(): void;
|
|
96
|
+
state(): WizState | null;
|
|
97
|
+
}
|
|
98
|
+
export declare function createPadWizard(ctx: PadWizardCtx): PadWizard;
|
|
99
|
+
export {};
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
import { SYSTEM } from '../core/actions.js';
|
|
3
|
+
import { migrateControlMap } from './vocabulary-migration.js';
|
|
4
|
+
import { buttonName } from './touch.js';
|
|
5
|
+
export const PADWIZ_ORDER = [
|
|
6
|
+
// Directions first: they are what the child finds without thinking, and getting all four right gives confidence for
|
|
7
|
+
// the other ten.
|
|
8
|
+
'up', 'down', 'left', 'right',
|
|
9
|
+
// The diamond, in the order the finger walks it in this project (ADR-0086 §2).
|
|
10
|
+
'action2', 'action1', 'action4', 'action3',
|
|
11
|
+
// The four shoulders: the wizard exists FOR pads that are not "standard" — generic, adapted, one-handed — and a game
|
|
12
|
+
// that declares `leftShoulder` needs a way for the child to map it.
|
|
13
|
+
'leftShoulder', 'leftTrigger', 'rightShoulder', 'rightTrigger',
|
|
14
|
+
// System last: `start` and `select` are usually the smallest and most hidden buttons.
|
|
15
|
+
'start', 'select',
|
|
16
|
+
];
|
|
17
|
+
/**
|
|
18
|
+
* 🔴 THE SYSTEM'S TWO ARE ALWAYS ASKED, IN THE ENGINE'S WORDS (ADR-0122, ADR-0144 §1 and its erratum of 2026-09-26, ADR-0155).
|
|
19
|
+
* A game's preset may not name `start` or `select` (ADR-0144 §4, ADR-0155 §4 — refused at boot), so a wizard that asked only
|
|
20
|
+
* what the game names never asked them, and every pad it mapped had no quick pause and no menus: the pause no game may decline,
|
|
21
|
+
* declined by the pad. They are the system's, and `core/actions` (`SYSTEM`) says the engine may name them — the legend printed
|
|
22
|
+
* on the pad. The host's labeller is not asked about them.
|
|
23
|
+
* 📌 Each carries its QUESTION and its NAME: the name is the word the question says (START, SELECT), and it is how the progress
|
|
24
|
+
* line lists the position once mapped — never its id (ADR-0074: no abstract name reaches a person).
|
|
25
|
+
*/
|
|
26
|
+
const SYSTEM_WORDS = {
|
|
27
|
+
start: { question: 'pad.wiz.stepStart', name: 'touch.start' },
|
|
28
|
+
select: { question: 'pad.wiz.stepSelect', name: 'touch.select' },
|
|
29
|
+
};
|
|
30
|
+
const systemWords = (action) => (SYSTEM.includes(action) ? SYSTEM_WORDS[action] : null);
|
|
31
|
+
/**
|
|
32
|
+
* 🔴 THE DEFAULT POSITIONS (ADR-0144, erratum of 2026-09-26 — the Dev: «as posições padrões são up, down, left, right, 1, 2, 3, 4,
|
|
33
|
+
* L1, L2, R1, R2, START e SELECT»). A game that names no position still gets a pad the child can map: these fourteen, in this
|
|
34
|
+
* order, each asked by its button's name (`input/touch.buttonName`, ADR-0165 — the names printed on the virtual pad).
|
|
35
|
+
*/
|
|
36
|
+
const DEFAULT_ORDER = [
|
|
37
|
+
'up', 'down', 'left', 'right', 'action1', 'action2', 'action3', 'action4',
|
|
38
|
+
'leftShoulder', 'leftTrigger', 'rightShoulder', 'rightTrigger', 'start', 'select',
|
|
39
|
+
];
|
|
40
|
+
const KEY = (id) => 'incl_padmap_' + id;
|
|
41
|
+
/**
|
|
42
|
+
* THE CACHE OF STORED MAPS, one per ROOT (ADR-0232 D4). ⚠️ Not safe to share: two roots on two stores would read each
|
|
43
|
+
* other's map, and the one the page's first wizard saved would answer in a root whose child never recorded it. The root
|
|
44
|
+
* builds ONE and hands it to both readers — its own wizard and `initGamepad` — so a map saved by the wizard is the one the
|
|
45
|
+
* pad reads on the next frame, not a copy cached before.
|
|
46
|
+
*/
|
|
47
|
+
export function createPadMaps(store) {
|
|
48
|
+
const maps = new Map();
|
|
49
|
+
return {
|
|
50
|
+
padMap(id) {
|
|
51
|
+
if (!maps.has(id))
|
|
52
|
+
maps.set(id, migrateControlMap(store.getJSON(KEY(id), null)));
|
|
53
|
+
return maps.get(id) ?? null;
|
|
54
|
+
},
|
|
55
|
+
store(id, map) {
|
|
56
|
+
store.setJSON(KEY(id), map);
|
|
57
|
+
maps.set(id, map);
|
|
58
|
+
},
|
|
59
|
+
skip(id) {
|
|
60
|
+
if (!maps.get(id))
|
|
61
|
+
maps.set(id, { _skip: true });
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
export function createPadWizard(ctx) {
|
|
66
|
+
const { t } = ctx;
|
|
67
|
+
let padWiz = null;
|
|
68
|
+
/**
|
|
69
|
+
* THIS WIZARD'S STEPS, decided when it opens: the game's positions (`PADWIZ_ORDER`, those it names) and then START and SELECT;
|
|
70
|
+
* or, where the game names none, the fourteen defaults (`DEFAULT_ORDER`), all asked, in the engine's words.
|
|
71
|
+
*/
|
|
72
|
+
let defaults = false;
|
|
73
|
+
let order = PADWIZ_ORDER;
|
|
74
|
+
const gameNamesAnyPosition = () => PADWIZ_ORDER.some((action) => systemWords(action) === null && !!ctx.actionLabel(action));
|
|
75
|
+
/** Is this step asked? Every default; else the game's positions when it names them; the system's two always (`SYSTEM_WORDS`). */
|
|
76
|
+
const asked = (action) => defaults || systemWords(action) !== null || !!ctx.actionLabel(action);
|
|
77
|
+
/**
|
|
78
|
+
* A position's word, the one its question said: the engine's for START and SELECT and for every default, the game's for the
|
|
79
|
+
* rest. ⚠️ A position the game no longer names (a cartridge mounted mid-wizard) has no word, and is left out rather than shown
|
|
80
|
+
* by its id.
|
|
81
|
+
*/
|
|
82
|
+
const wordFor = (action) => {
|
|
83
|
+
const system = systemWords(action);
|
|
84
|
+
if (system)
|
|
85
|
+
return t(system.name);
|
|
86
|
+
return defaults ? buttonName(ctx.t, action) : ctx.actionLabel(action);
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Walks to the next step asked, or closes if there is none left. ONE function: after the last step the wizard does not stay
|
|
90
|
+
* open pointing at a position nobody asks.
|
|
91
|
+
*/
|
|
92
|
+
function advance() {
|
|
93
|
+
if (!padWiz)
|
|
94
|
+
return;
|
|
95
|
+
while (padWiz.step < order.length && !asked(order[padWiz.step]))
|
|
96
|
+
padWiz.step++;
|
|
97
|
+
if (padWiz.step >= order.length)
|
|
98
|
+
closeWizard(true);
|
|
99
|
+
}
|
|
100
|
+
function ask() {
|
|
101
|
+
if (!padWiz)
|
|
102
|
+
return;
|
|
103
|
+
advance();
|
|
104
|
+
if (!padWiz)
|
|
105
|
+
return; // it closed while advancing
|
|
106
|
+
const action = order[padWiz.step];
|
|
107
|
+
const n = padWiz.step + 1, total = order.length;
|
|
108
|
+
const system = systemWords(action);
|
|
109
|
+
ctx.say(t(system?.question ?? 'pad.wiz.step', { n, total, acao: system ? '' : wordFor(action) }));
|
|
110
|
+
ctx.onStep?.(action);
|
|
111
|
+
// What is mapped, in the words it was asked with (ADR-0074) — the map's keys are position ids. The empty list's dash stays
|
|
112
|
+
// raw on purpose: it is punctuation, not language.
|
|
113
|
+
const words = Object.keys(padWiz.map).map(wordFor).filter((word) => !!word);
|
|
114
|
+
ctx.progress(t('pad.wiz.mapped', { lista: words.join(' · ') || '—' }));
|
|
115
|
+
}
|
|
116
|
+
function wire(bd) {
|
|
117
|
+
if (!padWiz)
|
|
118
|
+
return;
|
|
119
|
+
padWiz.map[order[padWiz.step]] = bd;
|
|
120
|
+
padWiz.step++;
|
|
121
|
+
padWiz.release = true; // requires letting go before the next step
|
|
122
|
+
advance();
|
|
123
|
+
}
|
|
124
|
+
function begin(gi, id, phrase) {
|
|
125
|
+
defaults = !gameNamesAnyPosition();
|
|
126
|
+
order = defaults ? DEFAULT_ORDER : PADWIZ_ORDER;
|
|
127
|
+
padWiz = { gi, id, step: -1, base: null, map: {}, release: false, baseWait: gi >= 0, axTrack: null, timer: null };
|
|
128
|
+
ctx.say(phrase);
|
|
129
|
+
ctx.onStep?.(null);
|
|
130
|
+
ctx.progress('');
|
|
131
|
+
padWiz.timer = setInterval(tickWizard, 30);
|
|
132
|
+
}
|
|
133
|
+
function openAny() { begin(-1, '', t('pad.wiz.pressAny')); }
|
|
134
|
+
function openForPad(gp) { begin(gp.index, gp.id, t('pad.wiz.detected', { id: gp.id })); }
|
|
135
|
+
function closeWizard(save) {
|
|
136
|
+
if (!padWiz)
|
|
137
|
+
return;
|
|
138
|
+
if (padWiz.timer != null)
|
|
139
|
+
clearInterval(padWiz.timer);
|
|
140
|
+
if (save && padWiz.id) {
|
|
141
|
+
ctx.maps.store(padWiz.id, padWiz.map);
|
|
142
|
+
ctx.srAlert(t('sr.pad.mapSaved', { id: padWiz.id }));
|
|
143
|
+
}
|
|
144
|
+
else if (padWiz.id) {
|
|
145
|
+
ctx.maps.skip(padWiz.id);
|
|
146
|
+
}
|
|
147
|
+
const gi = padWiz.gi;
|
|
148
|
+
const saved = save && !!padWiz.id;
|
|
149
|
+
padWiz = null;
|
|
150
|
+
ctx.onClose?.(gi, saved);
|
|
151
|
+
}
|
|
152
|
+
/* ===================== the five moments of a frame =====================
|
|
153
|
+
*
|
|
154
|
+
* 🔴 This is a STATE MACHINE, and the order below is what it is: no pad adopted · waiting for the resting pose ·
|
|
155
|
+
* waiting for the hand to let go · an axis under watch · reading what moved. Each moment is a function named for what
|
|
156
|
+
* it waits for, and `tickWizard` is their list.
|
|
157
|
+
*
|
|
158
|
+
* ⚠️ AND IT IS NOT A TABLE, unlike `input/keydown`'s chain, because the moments are not symmetric: the first runs
|
|
159
|
+
* WITHOUT a pad in hand (it is the one that picks it) and the other four need one. A list of equal rows would have to
|
|
160
|
+
* pretend the first receives what does not exist yet.
|
|
161
|
+
*
|
|
162
|
+
* 📌 Each moment receives the state instead of reaching for it, which is what lets them be read one at a time — and
|
|
163
|
+
* what removes the non-null assertions a closure looking at `padWiz` would need.
|
|
164
|
+
*/
|
|
165
|
+
/** 1 · No pad adopted: the FIRST button pressed on any pad picks the pad the hand is holding. */
|
|
166
|
+
function adoptTheHandsPad(w, pads) {
|
|
167
|
+
for (const gp of pads) {
|
|
168
|
+
if (gp && gp.buttons.some((b) => b && b.pressed)) {
|
|
169
|
+
w.gi = gp.index;
|
|
170
|
+
w.id = gp.id;
|
|
171
|
+
w.baseWait = true;
|
|
172
|
+
ctx.say(t('pad.wiz.releaseAll', { id: gp.id }));
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/** 2 · This pad's RESTING pose, measured on the one frame in which nothing is pressed. */
|
|
178
|
+
function takeTheRestingPose(w, gp) {
|
|
179
|
+
if (gp.buttons.some((b) => b && b.pressed))
|
|
180
|
+
return;
|
|
181
|
+
w.baseWait = false;
|
|
182
|
+
w.base = { b: gp.buttons.map((x) => !!(x && x.pressed)), a: gp.axes.slice() };
|
|
183
|
+
w.step = 0;
|
|
184
|
+
ask();
|
|
185
|
+
}
|
|
186
|
+
/** 3 · The hand has to LET GO before the next question — and letting go has two halves, the buttons and the axes. */
|
|
187
|
+
function waitForTheHandToLetGo(w, gp, base) {
|
|
188
|
+
const idle = !gp.buttons.some((b, i) => b && b.pressed && !base.b[i]) && gp.axes.every((v, i) => Math.abs((v || 0) - base.a[i]) < 0.35);
|
|
189
|
+
if (idle) {
|
|
190
|
+
w.release = false;
|
|
191
|
+
ask();
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* 4 · An axis under watch (~240 ms): classified by BEHAVIOUR — varies continuously = analogue (threshold by sign);
|
|
196
|
+
* jumps and STAYS CONSTANT = a D-pad/POV hat (exact value, ±0.13).
|
|
197
|
+
*/
|
|
198
|
+
function classifyTheWatchedAxis(w, gp, base) {
|
|
199
|
+
const tr = w.axTrack;
|
|
200
|
+
const v = gp.axes[tr.i] || 0;
|
|
201
|
+
if (Math.abs(v - tr.last) > 0.03)
|
|
202
|
+
tr.changes++;
|
|
203
|
+
tr.last = v;
|
|
204
|
+
if (Math.abs(v - base.a[tr.i]) > Math.abs(tr.v - base.a[tr.i]))
|
|
205
|
+
tr.v = v;
|
|
206
|
+
if (++tr.ticks < 8)
|
|
207
|
+
return;
|
|
208
|
+
const pv = tr.v;
|
|
209
|
+
w.axTrack = null;
|
|
210
|
+
wire(tr.changes >= 2 ? { ax: tr.i, s: pv > 0 ? 1 : -1 } : { av: tr.i, v: Math.round(pv * 10000) / 10000 });
|
|
211
|
+
}
|
|
212
|
+
/** 5 · What moved since rest: a button first, and only then an axis that really left. */
|
|
213
|
+
function readWhatMoved(w, gp, base) {
|
|
214
|
+
for (let i = 0; i < gp.buttons.length; i++) {
|
|
215
|
+
if (gp.buttons[i] && gp.buttons[i].pressed && !base.b[i]) {
|
|
216
|
+
wire({ b: i });
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
for (let i = 0; i < gp.axes.length; i++) {
|
|
221
|
+
const v = gp.axes[i] || 0;
|
|
222
|
+
if (Math.abs(v - base.a[i]) > 0.45) {
|
|
223
|
+
w.axTrack = { i, v, last: v, changes: 0, ticks: 0 };
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
function tickWizard() {
|
|
229
|
+
if (!padWiz)
|
|
230
|
+
return;
|
|
231
|
+
ctx.onTick?.();
|
|
232
|
+
const pads = ctx.getGamepads() ?? [];
|
|
233
|
+
if (padWiz.gi < 0) {
|
|
234
|
+
adoptTheHandsPad(padWiz, pads);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const gp = pads[padWiz.gi];
|
|
238
|
+
if (!gp)
|
|
239
|
+
return; // pad disconnected (or its index not populated yet): freezes until it comes back
|
|
240
|
+
if (padWiz.baseWait) {
|
|
241
|
+
takeTheRestingPose(padWiz, gp);
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
const base = padWiz.base;
|
|
245
|
+
if (padWiz.release) {
|
|
246
|
+
waitForTheHandToLetGo(padWiz, gp, base);
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
if (padWiz.axTrack) {
|
|
250
|
+
classifyTheWatchedAxis(padWiz, gp, base);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
readWhatMoved(padWiz, gp, base);
|
|
254
|
+
}
|
|
255
|
+
return { open: openAny, openFor: openForPad, close: closeWizard, tick: tickWizard, state: () => padWiz };
|
|
256
|
+
}
|