@diffenderfer-games/hub 2.0.0-next.0 → 2.0.0-next.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (293) hide show
  1. package/CHANGELOG.md +163 -0
  2. package/LICENSE +21 -21
  3. package/README.md +203 -139
  4. package/dist/auto.d.ts +8 -8
  5. package/dist/auto.js +3 -3
  6. package/dist/avatar-pixi.d.ts +165 -0
  7. package/dist/avatar-pixi.d.ts.map +1 -0
  8. package/dist/avatar-pixi.js +299 -0
  9. package/dist/avatar-pixi.js.map +1 -0
  10. package/dist/avatar-svg.d.ts +4 -0
  11. package/dist/avatar-svg.js +5 -0
  12. package/dist/avatar-three.d.ts +69 -0
  13. package/dist/avatar-three.d.ts.map +1 -0
  14. package/dist/avatar-three.js +49 -0
  15. package/dist/avatar-three.js.map +1 -0
  16. package/dist/avatar.d.ts +7 -0
  17. package/dist/avatar.js +500 -0
  18. package/dist/avatar.js.map +1 -0
  19. package/dist/daily.d.ts +2 -2
  20. package/dist/doctor/cli.mjs +119 -28
  21. package/dist/doctor/cli.mjs.map +1 -1
  22. package/dist/index.d.ts +8 -8
  23. package/dist/index.js +3 -3
  24. package/dist/input.d.ts +2 -2
  25. package/dist/input.js +2 -2
  26. package/dist/mp.d.ts +3 -3
  27. package/dist/race.d.ts +1 -1
  28. package/dist/rooms.d.ts +3 -3
  29. package/dist/rooms.js +1 -1
  30. package/dist/shared/animator.d.ts +104 -0
  31. package/dist/shared/animator.d.ts.map +1 -0
  32. package/dist/shared/art.js +1946 -0
  33. package/dist/shared/art.js.map +1 -0
  34. package/dist/shared/atlas.d.ts +138 -0
  35. package/dist/shared/atlas.d.ts.map +1 -0
  36. package/dist/shared/avatar.d.ts +123 -0
  37. package/dist/shared/avatar.d.ts.map +1 -0
  38. package/dist/shared/bridge.js +18 -4
  39. package/dist/shared/bridge.js.map +1 -1
  40. package/dist/shared/client-version.gen.js +1 -1
  41. package/dist/shared/daily.d.ts +145 -19
  42. package/dist/shared/daily.d.ts.map +1 -1
  43. package/dist/shared/daily.js +138 -25
  44. package/dist/shared/daily.js.map +1 -1
  45. package/dist/shared/defineProperty.js +39 -0
  46. package/dist/shared/describe.d.ts +32 -0
  47. package/dist/shared/describe.d.ts.map +1 -0
  48. package/dist/shared/describe.js +109 -0
  49. package/dist/shared/describe.js.map +1 -0
  50. package/dist/shared/globals.js +9 -42
  51. package/dist/shared/globals.js.map +1 -1
  52. package/dist/shared/index.d.ts +1122 -46
  53. package/dist/shared/index.d.ts.map +1 -1
  54. package/dist/shared/input.d.ts +74 -14
  55. package/dist/shared/input.d.ts.map +1 -1
  56. package/dist/shared/input.js +179 -25
  57. package/dist/shared/input.js.map +1 -1
  58. package/dist/shared/json-guards.js +48 -0
  59. package/dist/shared/json-guards.js.map +1 -0
  60. package/dist/shared/latest-state.js +1 -1
  61. package/dist/shared/live-canvas.js +62 -0
  62. package/dist/shared/live-canvas.js.map +1 -0
  63. package/dist/shared/mp.js +10 -2
  64. package/dist/shared/mp.js.map +1 -1
  65. package/dist/shared/multiplayer-types.d.ts +15 -2
  66. package/dist/shared/multiplayer-types.d.ts.map +1 -1
  67. package/dist/shared/overlays.js +800 -0
  68. package/dist/shared/overlays.js.map +1 -0
  69. package/dist/shared/page-stack.js +2 -1
  70. package/dist/shared/page-stack.js.map +1 -1
  71. package/dist/shared/pose-key.js +249 -0
  72. package/dist/shared/pose-key.js.map +1 -0
  73. package/dist/shared/race-types.d.ts +1 -1
  74. package/dist/shared/race.js +1 -1
  75. package/dist/shared/room-types.d.ts +28 -3
  76. package/dist/shared/room-types.d.ts.map +1 -1
  77. package/dist/shared/rooms.d.ts.map +1 -1
  78. package/dist/shared/rooms.js +23 -4
  79. package/dist/shared/rooms.js.map +1 -1
  80. package/dist/shared/runtime-opener.js +1 -1
  81. package/dist/shared/scene.d.ts +90 -0
  82. package/dist/shared/scene.d.ts.map +1 -0
  83. package/dist/shared/social.d.ts +90 -9
  84. package/dist/shared/social.d.ts.map +1 -1
  85. package/dist/shared/social.js +6 -1
  86. package/dist/shared/social.js.map +1 -1
  87. package/dist/shared/src.js +4563 -667
  88. package/dist/shared/src.js.map +1 -1
  89. package/dist/shared/svg-markup.js +2354 -0
  90. package/dist/shared/svg-markup.js.map +1 -0
  91. package/dist/shared/svg.js +27 -0
  92. package/dist/shared/svg.js.map +1 -0
  93. package/dist/shared/types.d.ts +174 -0
  94. package/dist/shared/types.d.ts.map +1 -0
  95. package/dist/shared/wire-types.d.ts +6 -2
  96. package/dist/shared/wire-types.d.ts.map +1 -1
  97. package/dist/social.d.ts +5 -5
  98. package/dist/social.js +2 -2
  99. package/dist/turns-rules.d.ts +2 -0
  100. package/dist/turns-rules.js +1 -0
  101. package/dist/turns.d.ts +61 -0
  102. package/dist/turns.d.ts.map +1 -0
  103. package/dist/turns.js +183 -0
  104. package/dist/turns.js.map +1 -0
  105. package/package.json +125 -89
  106. package/src/auto.ts +39 -39
  107. package/src/avatar/adapters/pixi-avatar.ts +73 -0
  108. package/src/avatar/adapters/pixi-crowd.ts +329 -0
  109. package/src/avatar/adapters/pixi-types.ts +84 -0
  110. package/src/avatar/adapters/three.ts +126 -0
  111. package/src/avatar/animator/animator.ts +285 -0
  112. package/src/avatar/animator/reactions.ts +44 -0
  113. package/src/avatar/animator/schedule.ts +73 -0
  114. package/src/avatar/art/beard.ts +49 -0
  115. package/src/avatar/art/glasses.ts +67 -0
  116. package/src/avatar/art/hair.ts +254 -0
  117. package/src/avatar/art/headwear.ts +75 -0
  118. package/src/avatar/art/index.ts +21 -0
  119. package/src/avatar/art/shape.ts +87 -0
  120. package/src/avatar/art-store.ts +36 -0
  121. package/src/avatar/atlas.ts +246 -0
  122. package/src/avatar/catalog-view.ts +14 -0
  123. package/src/avatar/colour.ts +109 -0
  124. package/src/avatar/describe.ts +79 -0
  125. package/src/avatar/face.ts +187 -0
  126. package/src/avatar/geom.ts +173 -0
  127. package/src/avatar/layout.ts +47 -0
  128. package/src/avatar/look-like.ts +52 -0
  129. package/src/avatar/outputs/bitmap.ts +75 -0
  130. package/src/avatar/outputs/canvas-paint.ts +177 -0
  131. package/src/avatar/outputs/canvas.ts +62 -0
  132. package/src/avatar/outputs/custom-element.ts +140 -0
  133. package/src/avatar/outputs/element.ts +58 -0
  134. package/src/avatar/outputs/live-canvas.ts +57 -0
  135. package/src/avatar/outputs/overlay-svg.ts +22 -0
  136. package/src/avatar/outputs/pose-key.ts +30 -0
  137. package/src/avatar/outputs/svg-markup.ts +113 -0
  138. package/src/avatar/outputs/svg.ts +41 -0
  139. package/src/avatar/overlays.ts +128 -0
  140. package/src/avatar/render/body.ts +116 -0
  141. package/src/avatar/render/colours.ts +81 -0
  142. package/src/avatar/render/compose.ts +320 -0
  143. package/src/avatar/render/context.ts +117 -0
  144. package/src/avatar/render/eyes.ts +78 -0
  145. package/src/avatar/render/eyewear.ts +118 -0
  146. package/src/avatar/render/frame.ts +73 -0
  147. package/src/avatar/render/head.ts +81 -0
  148. package/src/avatar/render/mouth.ts +86 -0
  149. package/src/avatar/render/props.ts +91 -0
  150. package/src/avatar/render/types.ts +93 -0
  151. package/src/avatar/render/walker-parts.ts +121 -0
  152. package/src/avatar/render/walker.ts +199 -0
  153. package/src/avatar/scene.ts +119 -0
  154. package/src/avatar-pixi.ts +20 -0
  155. package/src/avatar-svg.ts +21 -0
  156. package/src/avatar-three.ts +18 -0
  157. package/src/avatar.ts +63 -0
  158. package/src/core/avatar/avatar-cache.ts +95 -0
  159. package/src/core/avatar/avatar-ref.ts +34 -0
  160. package/src/core/avatar/avatar-types.ts +43 -0
  161. package/src/core/avatar/hub-avatar.ts +308 -0
  162. package/src/core/connection.ts +408 -299
  163. package/src/core/errors.ts +160 -147
  164. package/src/core/game-data.ts +40 -38
  165. package/src/core/hub-economy.ts +216 -179
  166. package/src/core/hub-progress.ts +269 -205
  167. package/src/core/hub.ts +352 -131
  168. package/src/core/menu-control.ts +61 -61
  169. package/src/core/offline/cloud-saves.ts +62 -0
  170. package/src/core/offline/events.ts +50 -50
  171. package/src/core/offline/favorites.ts +21 -21
  172. package/src/core/offline/hub-kinds/avatar-kind.ts +42 -0
  173. package/src/core/offline/hub-kinds/daily-kind.ts +68 -0
  174. package/src/core/offline/hub-kinds/economy-kinds.ts +86 -0
  175. package/src/core/offline/hub-kinds/game-fields.ts +2 -0
  176. package/src/core/offline/hub-kinds/index.ts +52 -0
  177. package/src/core/offline/hub-kinds/legacy-ops.ts +37 -0
  178. package/src/core/offline/hub-kinds/progress-kinds.ts +165 -0
  179. package/src/core/offline/hub-kinds/resume-kind.ts +108 -0
  180. package/src/core/offline/hub-kinds/saved-kind.ts +93 -0
  181. package/src/core/offline/kind-adapters.ts +48 -0
  182. package/src/core/offline/local-inventory.ts +64 -0
  183. package/src/core/offline/local-store.ts +304 -261
  184. package/src/core/offline/offline-api.ts +65 -0
  185. package/src/core/offline/outbox-entry.ts +59 -0
  186. package/src/core/offline/outbox-kinds.ts +78 -0
  187. package/src/core/offline/outbox-merge.ts +39 -0
  188. package/src/core/offline/outbox-replayer.ts +141 -0
  189. package/src/core/offline/outbox-store.ts +85 -0
  190. package/src/core/offline/outbox.ts +232 -0
  191. package/src/core/offline/queue.ts +126 -171
  192. package/src/core/offline/replay-lease.ts +87 -0
  193. package/src/core/offline/replay-policy.ts +52 -0
  194. package/src/core/page-account.ts +121 -0
  195. package/src/core/page.ts +87 -74
  196. package/src/core/rest/endpoint-types.ts +22 -22
  197. package/src/core/rest/rest-client.ts +163 -152
  198. package/src/core/rest/routes.gen.ts +144 -88
  199. package/src/core/resume/hub-resume.ts +286 -0
  200. package/src/core/resume/local-resume.ts +130 -0
  201. package/src/core/resume/resume-list.ts +65 -0
  202. package/src/core/resume/resume-request.ts +52 -0
  203. package/src/core/resume/resume-types.ts +64 -0
  204. package/src/core/saved/daily-saves.ts +196 -0
  205. package/src/core/saved/daily-seed.ts +16 -0
  206. package/src/core/saved/hub-saved.ts +221 -0
  207. package/src/core/saved/local-saved.ts +122 -0
  208. package/src/core/saved/saved-list.ts +129 -0
  209. package/src/core/saved/saved-open.ts +104 -0
  210. package/src/core/saved/saved-order.ts +49 -0
  211. package/src/core/saved/saved-types.ts +24 -0
  212. package/src/core/storage/json-guards.ts +80 -74
  213. package/src/core/storage/key-value.ts +66 -66
  214. package/src/core/ticket.ts +43 -43
  215. package/src/core/turns/hub-turns.ts +161 -0
  216. package/src/core/turns/turn-game.ts +510 -0
  217. package/src/core/turns/your-turn-buzz.ts +69 -0
  218. package/src/core/types.ts +134 -134
  219. package/src/core/version/client-version.gen.ts +2 -2
  220. package/src/core/version/hub-version.ts +62 -62
  221. package/src/core/version/semver.ts +97 -97
  222. package/src/daily/daily-system.ts +276 -161
  223. package/src/daily/page-daily.ts +36 -36
  224. package/src/daily/types.ts +105 -67
  225. package/src/daily.ts +10 -10
  226. package/src/doctor/checks.ts +86 -76
  227. package/src/doctor/cli.ts +101 -106
  228. package/src/doctor/hub-version-fetch.ts +43 -0
  229. package/src/doctor/tsconfig.json +12 -12
  230. package/src/doctor/world-check.ts +61 -0
  231. package/src/globals.ts +115 -110
  232. package/src/index.ts +68 -50
  233. package/src/input/device-state.ts +146 -146
  234. package/src/input/edges.ts +55 -55
  235. package/src/input/focus-ring.ts +13 -0
  236. package/src/input/gamepad.ts +61 -61
  237. package/src/input/input-events.ts +32 -32
  238. package/src/input/input-registry.ts +171 -164
  239. package/src/input/input-system.ts +406 -393
  240. package/src/input/legacy-controls.ts +64 -64
  241. package/src/input/modality.ts +122 -0
  242. package/src/input/nav-state.ts +200 -191
  243. package/src/input/navigator.ts +182 -171
  244. package/src/input/on-screen-controls.ts +101 -101
  245. package/src/input/page-control.ts +43 -43
  246. package/src/input/page-input.ts +193 -193
  247. package/src/input/player-remaps.ts +77 -77
  248. package/src/input/source-values.ts +116 -116
  249. package/src/input/spatial.ts +47 -47
  250. package/src/input/types.ts +331 -331
  251. package/src/input/zero-config.ts +164 -177
  252. package/src/input.ts +26 -22
  253. package/src/mp.ts +10 -10
  254. package/src/overlay/controls-panel.ts +163 -163
  255. package/src/overlay/dom.ts +46 -46
  256. package/src/overlay/layout-clusters.ts +162 -162
  257. package/src/overlay/layout-geometry.ts +136 -136
  258. package/src/overlay/layout.ts +203 -203
  259. package/src/overlay/listener-scope.ts +23 -23
  260. package/src/overlay/on-screen-keyboard.ts +137 -137
  261. package/src/overlay/overlay.ts +234 -208
  262. package/src/overlay/styles.ts +13 -12
  263. package/src/overlay/virtual-controls.ts +262 -262
  264. package/src/race.ts +10 -10
  265. package/src/realtime/bridge.ts +126 -121
  266. package/src/realtime/cross-bundle.ts +29 -29
  267. package/src/realtime/fallback-notice.ts +48 -48
  268. package/src/realtime/hub-link.ts +65 -65
  269. package/src/realtime/latest-state.ts +33 -33
  270. package/src/realtime/multiplayer-forwarder.ts +40 -28
  271. package/src/realtime/multiplayer-types.ts +66 -52
  272. package/src/realtime/page-runtime.ts +60 -60
  273. package/src/realtime/race-forwarder.ts +59 -59
  274. package/src/realtime/race-types.ts +132 -132
  275. package/src/realtime/room-types.ts +151 -125
  276. package/src/realtime/rooms-forwarder.ts +104 -79
  277. package/src/realtime/runtime-api.ts +74 -49
  278. package/src/realtime/runtime-opener.ts +61 -61
  279. package/src/realtime/social-forwarder.ts +77 -72
  280. package/src/realtime/social-types.ts +86 -78
  281. package/src/realtime/types.ts +54 -53
  282. package/src/realtime/wire-types.ts +79 -72
  283. package/src/realtime/world-types.ts +20 -0
  284. package/src/rooms.ts +10 -10
  285. package/src/social.ts +19 -18
  286. package/src/turns-kit/check-rules.ts +281 -0
  287. package/src/turns-rules.ts +12 -0
  288. package/src/turns.ts +14 -0
  289. package/src/types.ts +13 -13
  290. package/src/uistack/page-stack.ts +45 -45
  291. package/src/uistack/play-suspension.ts +82 -82
  292. package/src/uistack/types.ts +76 -76
  293. package/src/uistack/ui-stack.ts +144 -144
@@ -1,116 +1,116 @@
1
- /**
2
- * An input's raw 0..1 value from the device state: the strongest of its
3
- * sources. Keyboard and touch always count; mouse and gamepad only while
4
- * they are the active pointer.
5
- */
6
- import type { DeviceState } from './device-state';
7
- import { AXIS_DEADZONE } from './gamepad';
8
- import type { AxisSide, GamepadMapping, InputDef, InputSource, MouseMapping, TouchMapping } from './types';
9
-
10
- /** Clamp to 0..1. */
11
- export function clampUnit(amount: number): number {
12
- return Math.min(1, Math.max(0, amount));
13
- }
14
-
15
- /** The pointer's position from screen centre on an axis, as 0..1 on one side. */
16
- function centreValue(state: DeviceState, axis: 'x' | 'y', side: AxisSide | undefined): number {
17
- const fraction = axis === 'x' ? state.pointerX / window.innerWidth : state.pointerY / window.innerHeight;
18
- const signed = (fraction - 0.5) * 2;
19
- return clampUnit(side === '-' ? -signed : signed);
20
- }
21
-
22
- /** A signed amount read on one side. */
23
- function onSide(amount: number, side: AxisSide | undefined): number {
24
- return side === '-' ? -amount : amount;
25
- }
26
-
27
- function mouseValue(state: DeviceState, mapping: MouseMapping): number {
28
- let strongest = 0;
29
- if (mapping.button !== undefined && state.heldMouseButtons.has(mapping.button)) {
30
- strongest = 1;
31
- }
32
- if (mapping.dblclick === true && state.isPulseActive('mouse:dbl')) {
33
- strongest = 1;
34
- }
35
- if (mapping.centerAxis !== undefined) {
36
- strongest = Math.max(strongest, centreValue(state, mapping.centerAxis, mapping.centerSide));
37
- }
38
- if (mapping.moveAxis !== undefined) {
39
- const moved = mapping.moveAxis === 'x' ? state.movement.dx : state.movement.dy;
40
- strongest = Math.max(strongest, clampUnit(onSide(moved, mapping.moveSign) * (mapping.moveScale ?? 0.1)));
41
- }
42
- if (mapping.wheel !== undefined) {
43
- strongest = Math.max(strongest, clampUnit(onSide(state.wheel.dy, mapping.wheel)));
44
- }
45
- return strongest;
46
- }
47
-
48
- function gamepadValue(state: DeviceState, mapping: GamepadMapping): number {
49
- let strongest = 0;
50
- for (const pad of state.gamepads) {
51
- if (mapping.button !== undefined) {
52
- const button = pad.buttons[mapping.button];
53
- if (button !== undefined) {
54
- strongest = Math.max(strongest, button.value > 0 ? button.value : Number(button.pressed));
55
- }
56
- }
57
- if (mapping.axis !== undefined) {
58
- const [axisIndex, half] = mapping.axis;
59
- const deflection = pad.axes[axisIndex] ?? 0;
60
- const reading = half === '±' ? Math.abs(deflection) : Math.max(0, half === '+' ? deflection : -deflection);
61
- if (Math.abs(deflection) > AXIS_DEADZONE) {
62
- strongest = Math.max(strongest, clampUnit(reading));
63
- }
64
- }
65
- }
66
- return strongest;
67
- }
68
-
69
- function stickValue(state: DeviceState, mapping: TouchMapping): number {
70
- const stick = mapping.stick === undefined ? undefined : state.sticks.get(mapping.stick);
71
- if (stick === undefined) {
72
- return 0;
73
- }
74
- const readings: Record<NonNullable<TouchMapping['axis']>, number> = {
75
- 'x+': Math.max(0, stick.outX),
76
- 'x-': Math.max(0, -stick.outX),
77
- 'y+': Math.max(0, stick.outY),
78
- 'y-': Math.max(0, -stick.outY),
79
- x: Math.abs(stick.outX),
80
- y: Math.abs(stick.outY),
81
- };
82
- return mapping.axis === undefined ? 0 : readings[mapping.axis];
83
- }
84
-
85
- function touchValue(state: DeviceState, mapping: TouchMapping): number {
86
- let strongest = clampUnit(stickValue(state, mapping));
87
- if (mapping.button !== undefined && state.pressedVirtualButtons.has(mapping.button)) {
88
- strongest = 1;
89
- }
90
- if (mapping.tap === true && state.isPulseActive('tap')) {
91
- strongest = 1;
92
- }
93
- if (mapping.centerAxis !== undefined) {
94
- // The last touch position (touch moves the pointer too).
95
- strongest = Math.max(strongest, centreValue(state, mapping.centerAxis, mapping.centerSide));
96
- }
97
- return strongest;
98
- }
99
-
100
- /** An input's raw value now, given the active pointer. */
101
- export function readRawValue(state: DeviceState, definition: InputDef, activeSource: InputSource): number {
102
- let strongest = 0;
103
- if (definition.keys?.some((key) => state.heldKeys.has(key.toLowerCase())) === true) {
104
- strongest = 1;
105
- }
106
- if (definition.mouse !== undefined && activeSource === 'mouse') {
107
- strongest = Math.max(strongest, mouseValue(state, definition.mouse));
108
- }
109
- if (definition.gamepad !== undefined && activeSource === 'gamepad') {
110
- strongest = Math.max(strongest, gamepadValue(state, definition.gamepad));
111
- }
112
- if (definition.touch !== undefined) {
113
- strongest = Math.max(strongest, touchValue(state, definition.touch));
114
- }
115
- return clampUnit(strongest);
116
- }
1
+ /**
2
+ * An input's raw 0..1 value from the device state: the strongest of its
3
+ * sources. Keyboard and touch always count; mouse and gamepad only while
4
+ * they are the active pointer.
5
+ */
6
+ import type { DeviceState } from './device-state';
7
+ import { AXIS_DEADZONE } from './gamepad';
8
+ import type { AxisSide, GamepadMapping, InputDef, InputSource, MouseMapping, TouchMapping } from './types';
9
+
10
+ /** Clamp to 0..1. */
11
+ export function clampUnit(amount: number): number {
12
+ return Math.min(1, Math.max(0, amount));
13
+ }
14
+
15
+ /** The pointer's position from screen centre on an axis, as 0..1 on one side. */
16
+ function centreValue(state: DeviceState, axis: 'x' | 'y', side: AxisSide | undefined): number {
17
+ const fraction = axis === 'x' ? state.pointerX / window.innerWidth : state.pointerY / window.innerHeight;
18
+ const signed = (fraction - 0.5) * 2;
19
+ return clampUnit(side === '-' ? -signed : signed);
20
+ }
21
+
22
+ /** A signed amount read on one side. */
23
+ function onSide(amount: number, side: AxisSide | undefined): number {
24
+ return side === '-' ? -amount : amount;
25
+ }
26
+
27
+ function mouseValue(state: DeviceState, mapping: MouseMapping): number {
28
+ let strongest = 0;
29
+ if (mapping.button !== undefined && state.heldMouseButtons.has(mapping.button)) {
30
+ strongest = 1;
31
+ }
32
+ if (mapping.dblclick === true && state.isPulseActive('mouse:dbl')) {
33
+ strongest = 1;
34
+ }
35
+ if (mapping.centerAxis !== undefined) {
36
+ strongest = Math.max(strongest, centreValue(state, mapping.centerAxis, mapping.centerSide));
37
+ }
38
+ if (mapping.moveAxis !== undefined) {
39
+ const moved = mapping.moveAxis === 'x' ? state.movement.dx : state.movement.dy;
40
+ strongest = Math.max(strongest, clampUnit(onSide(moved, mapping.moveSign) * (mapping.moveScale ?? 0.1)));
41
+ }
42
+ if (mapping.wheel !== undefined) {
43
+ strongest = Math.max(strongest, clampUnit(onSide(state.wheel.dy, mapping.wheel)));
44
+ }
45
+ return strongest;
46
+ }
47
+
48
+ function gamepadValue(state: DeviceState, mapping: GamepadMapping): number {
49
+ let strongest = 0;
50
+ for (const pad of state.gamepads) {
51
+ if (mapping.button !== undefined) {
52
+ const button = pad.buttons[mapping.button];
53
+ if (button !== undefined) {
54
+ strongest = Math.max(strongest, button.value > 0 ? button.value : Number(button.pressed));
55
+ }
56
+ }
57
+ if (mapping.axis !== undefined) {
58
+ const [axisIndex, half] = mapping.axis;
59
+ const deflection = pad.axes[axisIndex] ?? 0;
60
+ const reading = half === '±' ? Math.abs(deflection) : Math.max(0, half === '+' ? deflection : -deflection);
61
+ if (Math.abs(deflection) > AXIS_DEADZONE) {
62
+ strongest = Math.max(strongest, clampUnit(reading));
63
+ }
64
+ }
65
+ }
66
+ return strongest;
67
+ }
68
+
69
+ function stickValue(state: DeviceState, mapping: TouchMapping): number {
70
+ const stick = mapping.stick === undefined ? undefined : state.sticks.get(mapping.stick);
71
+ if (stick === undefined) {
72
+ return 0;
73
+ }
74
+ const readings: Record<NonNullable<TouchMapping['axis']>, number> = {
75
+ 'x+': Math.max(0, stick.outX),
76
+ 'x-': Math.max(0, -stick.outX),
77
+ 'y+': Math.max(0, stick.outY),
78
+ 'y-': Math.max(0, -stick.outY),
79
+ x: Math.abs(stick.outX),
80
+ y: Math.abs(stick.outY),
81
+ };
82
+ return mapping.axis === undefined ? 0 : readings[mapping.axis];
83
+ }
84
+
85
+ function touchValue(state: DeviceState, mapping: TouchMapping): number {
86
+ let strongest = clampUnit(stickValue(state, mapping));
87
+ if (mapping.button !== undefined && state.pressedVirtualButtons.has(mapping.button)) {
88
+ strongest = 1;
89
+ }
90
+ if (mapping.tap === true && state.isPulseActive('tap')) {
91
+ strongest = 1;
92
+ }
93
+ if (mapping.centerAxis !== undefined) {
94
+ // The last touch position (touch moves the pointer too).
95
+ strongest = Math.max(strongest, centreValue(state, mapping.centerAxis, mapping.centerSide));
96
+ }
97
+ return strongest;
98
+ }
99
+
100
+ /** An input's raw value now, given the active pointer. */
101
+ export function readRawValue(state: DeviceState, definition: InputDef, activeSource: InputSource): number {
102
+ let strongest = 0;
103
+ if (definition.keys?.some((key) => state.heldKeys.has(key.toLowerCase())) === true) {
104
+ strongest = 1;
105
+ }
106
+ if (definition.mouse !== undefined && activeSource === 'mouse') {
107
+ strongest = Math.max(strongest, mouseValue(state, definition.mouse));
108
+ }
109
+ if (definition.gamepad !== undefined && activeSource === 'gamepad') {
110
+ strongest = Math.max(strongest, gamepadValue(state, definition.gamepad));
111
+ }
112
+ if (definition.touch !== undefined) {
113
+ strongest = Math.max(strongest, touchValue(state, definition.touch));
114
+ }
115
+ return clampUnit(strongest);
116
+ }
@@ -1,47 +1,47 @@
1
- /**
2
- * Spatial navigation: from one rectangle, the nearest one strictly in a
3
- * direction. Shared by navigable groups, the hub menu, the catalog launcher
4
- * and the on-screen keyboard.
5
- */
6
- import type { Direction } from './gamepad';
7
- import type { Rect } from './types';
8
-
9
- /** Centres closer than this on the travel axis don't count as "in that direction". */
10
- const MIN_TRAVEL_PX = 4;
11
-
12
- /** The rectangle of a DOM element. */
13
- export function rectOf(element: Element): Rect {
14
- const bounds = element.getBoundingClientRect();
15
- return { x: bounds.left, y: bounds.top, width: bounds.width, height: bounds.height };
16
- }
17
-
18
- /** Index of the rectangle nearest `rects[fromIndex]` strictly in `direction`, or -1. Off-axis distance counts double. */
19
- export function findNearestInDirection(rects: readonly Rect[], fromIndex: number, direction: Direction): number {
20
- const from = rects[fromIndex];
21
- if (from === undefined) {
22
- return -1;
23
- }
24
- const fromCentreX = from.x + from.width / 2;
25
- const fromCentreY = from.y + from.height / 2;
26
- const isVertical = direction === 'up' || direction === 'down';
27
- let bestIndex = -1;
28
- let bestScore = Infinity;
29
- rects.forEach((rect, index) => {
30
- if (index === fromIndex) {
31
- return;
32
- }
33
- const dx = rect.x + rect.width / 2 - fromCentreX;
34
- const dy = rect.y + rect.height / 2 - fromCentreY;
35
- const travel = { up: -dy, down: dy, left: -dx, right: dx }[direction];
36
- if (travel <= MIN_TRAVEL_PX) {
37
- return;
38
- }
39
- const across = isVertical ? Math.abs(dx) : Math.abs(dy);
40
- const score = travel + across * 2;
41
- if (score < bestScore) {
42
- bestScore = score;
43
- bestIndex = index;
44
- }
45
- });
46
- return bestIndex;
47
- }
1
+ /**
2
+ * Spatial navigation: from one rectangle, the nearest one strictly in a
3
+ * direction. Shared by navigable groups, the hub menu, the catalog launcher
4
+ * and the on-screen keyboard.
5
+ */
6
+ import type { Direction } from './gamepad';
7
+ import type { Rect } from './types';
8
+
9
+ /** Centres closer than this on the travel axis don't count as "in that direction". */
10
+ const MIN_TRAVEL_PX = 4;
11
+
12
+ /** The rectangle of a DOM element. */
13
+ export function rectOf(element: Element): Rect {
14
+ const bounds = element.getBoundingClientRect();
15
+ return { x: bounds.left, y: bounds.top, width: bounds.width, height: bounds.height };
16
+ }
17
+
18
+ /** Index of the rectangle nearest `rects[fromIndex]` strictly in `direction`, or -1. Off-axis distance counts double. */
19
+ export function findNearestInDirection(rects: readonly Rect[], fromIndex: number, direction: Direction): number {
20
+ const from = rects[fromIndex];
21
+ if (from === undefined) {
22
+ return -1;
23
+ }
24
+ const fromCentreX = from.x + from.width / 2;
25
+ const fromCentreY = from.y + from.height / 2;
26
+ const isVertical = direction === 'up' || direction === 'down';
27
+ let bestIndex = -1;
28
+ let bestScore = Infinity;
29
+ rects.forEach((rect, index) => {
30
+ if (index === fromIndex) {
31
+ return;
32
+ }
33
+ const dx = rect.x + rect.width / 2 - fromCentreX;
34
+ const dy = rect.y + rect.height / 2 - fromCentreY;
35
+ const travel = { up: -dy, down: dy, left: -dx, right: dx }[direction];
36
+ if (travel <= MIN_TRAVEL_PX) {
37
+ return;
38
+ }
39
+ const across = isVertical ? Math.abs(dx) : Math.abs(dy);
40
+ const score = travel + across * 2;
41
+ if (score < bestScore) {
42
+ bestScore = score;
43
+ bestIndex = index;
44
+ }
45
+ });
46
+ return bestIndex;
47
+ }