@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.
- package/CHANGELOG.md +163 -0
- package/LICENSE +21 -21
- package/README.md +203 -139
- package/dist/auto.d.ts +8 -8
- package/dist/auto.js +3 -3
- package/dist/avatar-pixi.d.ts +165 -0
- package/dist/avatar-pixi.d.ts.map +1 -0
- package/dist/avatar-pixi.js +299 -0
- package/dist/avatar-pixi.js.map +1 -0
- package/dist/avatar-svg.d.ts +4 -0
- package/dist/avatar-svg.js +5 -0
- package/dist/avatar-three.d.ts +69 -0
- package/dist/avatar-three.d.ts.map +1 -0
- package/dist/avatar-three.js +49 -0
- package/dist/avatar-three.js.map +1 -0
- package/dist/avatar.d.ts +7 -0
- package/dist/avatar.js +500 -0
- package/dist/avatar.js.map +1 -0
- package/dist/daily.d.ts +2 -2
- package/dist/doctor/cli.mjs +119 -28
- package/dist/doctor/cli.mjs.map +1 -1
- package/dist/index.d.ts +8 -8
- package/dist/index.js +3 -3
- package/dist/input.d.ts +2 -2
- package/dist/input.js +2 -2
- package/dist/mp.d.ts +3 -3
- package/dist/race.d.ts +1 -1
- package/dist/rooms.d.ts +3 -3
- package/dist/rooms.js +1 -1
- package/dist/shared/animator.d.ts +104 -0
- package/dist/shared/animator.d.ts.map +1 -0
- package/dist/shared/art.js +1946 -0
- package/dist/shared/art.js.map +1 -0
- package/dist/shared/atlas.d.ts +138 -0
- package/dist/shared/atlas.d.ts.map +1 -0
- package/dist/shared/avatar.d.ts +123 -0
- package/dist/shared/avatar.d.ts.map +1 -0
- package/dist/shared/bridge.js +18 -4
- package/dist/shared/bridge.js.map +1 -1
- package/dist/shared/client-version.gen.js +1 -1
- package/dist/shared/daily.d.ts +145 -19
- package/dist/shared/daily.d.ts.map +1 -1
- package/dist/shared/daily.js +138 -25
- package/dist/shared/daily.js.map +1 -1
- package/dist/shared/defineProperty.js +39 -0
- package/dist/shared/describe.d.ts +32 -0
- package/dist/shared/describe.d.ts.map +1 -0
- package/dist/shared/describe.js +109 -0
- package/dist/shared/describe.js.map +1 -0
- package/dist/shared/globals.js +9 -42
- package/dist/shared/globals.js.map +1 -1
- package/dist/shared/index.d.ts +1122 -46
- package/dist/shared/index.d.ts.map +1 -1
- package/dist/shared/input.d.ts +74 -14
- package/dist/shared/input.d.ts.map +1 -1
- package/dist/shared/input.js +179 -25
- package/dist/shared/input.js.map +1 -1
- package/dist/shared/json-guards.js +48 -0
- package/dist/shared/json-guards.js.map +1 -0
- package/dist/shared/latest-state.js +1 -1
- package/dist/shared/live-canvas.js +62 -0
- package/dist/shared/live-canvas.js.map +1 -0
- package/dist/shared/mp.js +10 -2
- package/dist/shared/mp.js.map +1 -1
- package/dist/shared/multiplayer-types.d.ts +15 -2
- package/dist/shared/multiplayer-types.d.ts.map +1 -1
- package/dist/shared/overlays.js +800 -0
- package/dist/shared/overlays.js.map +1 -0
- package/dist/shared/page-stack.js +2 -1
- package/dist/shared/page-stack.js.map +1 -1
- package/dist/shared/pose-key.js +249 -0
- package/dist/shared/pose-key.js.map +1 -0
- package/dist/shared/race-types.d.ts +1 -1
- package/dist/shared/race.js +1 -1
- package/dist/shared/room-types.d.ts +28 -3
- package/dist/shared/room-types.d.ts.map +1 -1
- package/dist/shared/rooms.d.ts.map +1 -1
- package/dist/shared/rooms.js +23 -4
- package/dist/shared/rooms.js.map +1 -1
- package/dist/shared/runtime-opener.js +1 -1
- package/dist/shared/scene.d.ts +90 -0
- package/dist/shared/scene.d.ts.map +1 -0
- package/dist/shared/social.d.ts +90 -9
- package/dist/shared/social.d.ts.map +1 -1
- package/dist/shared/social.js +6 -1
- package/dist/shared/social.js.map +1 -1
- package/dist/shared/src.js +4563 -667
- package/dist/shared/src.js.map +1 -1
- package/dist/shared/svg-markup.js +2354 -0
- package/dist/shared/svg-markup.js.map +1 -0
- package/dist/shared/svg.js +27 -0
- package/dist/shared/svg.js.map +1 -0
- package/dist/shared/types.d.ts +174 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/dist/shared/wire-types.d.ts +6 -2
- package/dist/shared/wire-types.d.ts.map +1 -1
- package/dist/social.d.ts +5 -5
- package/dist/social.js +2 -2
- package/dist/turns-rules.d.ts +2 -0
- package/dist/turns-rules.js +1 -0
- package/dist/turns.d.ts +61 -0
- package/dist/turns.d.ts.map +1 -0
- package/dist/turns.js +183 -0
- package/dist/turns.js.map +1 -0
- package/package.json +125 -89
- package/src/auto.ts +39 -39
- package/src/avatar/adapters/pixi-avatar.ts +73 -0
- package/src/avatar/adapters/pixi-crowd.ts +329 -0
- package/src/avatar/adapters/pixi-types.ts +84 -0
- package/src/avatar/adapters/three.ts +126 -0
- package/src/avatar/animator/animator.ts +285 -0
- package/src/avatar/animator/reactions.ts +44 -0
- package/src/avatar/animator/schedule.ts +73 -0
- package/src/avatar/art/beard.ts +49 -0
- package/src/avatar/art/glasses.ts +67 -0
- package/src/avatar/art/hair.ts +254 -0
- package/src/avatar/art/headwear.ts +75 -0
- package/src/avatar/art/index.ts +21 -0
- package/src/avatar/art/shape.ts +87 -0
- package/src/avatar/art-store.ts +36 -0
- package/src/avatar/atlas.ts +246 -0
- package/src/avatar/catalog-view.ts +14 -0
- package/src/avatar/colour.ts +109 -0
- package/src/avatar/describe.ts +79 -0
- package/src/avatar/face.ts +187 -0
- package/src/avatar/geom.ts +173 -0
- package/src/avatar/layout.ts +47 -0
- package/src/avatar/look-like.ts +52 -0
- package/src/avatar/outputs/bitmap.ts +75 -0
- package/src/avatar/outputs/canvas-paint.ts +177 -0
- package/src/avatar/outputs/canvas.ts +62 -0
- package/src/avatar/outputs/custom-element.ts +140 -0
- package/src/avatar/outputs/element.ts +58 -0
- package/src/avatar/outputs/live-canvas.ts +57 -0
- package/src/avatar/outputs/overlay-svg.ts +22 -0
- package/src/avatar/outputs/pose-key.ts +30 -0
- package/src/avatar/outputs/svg-markup.ts +113 -0
- package/src/avatar/outputs/svg.ts +41 -0
- package/src/avatar/overlays.ts +128 -0
- package/src/avatar/render/body.ts +116 -0
- package/src/avatar/render/colours.ts +81 -0
- package/src/avatar/render/compose.ts +320 -0
- package/src/avatar/render/context.ts +117 -0
- package/src/avatar/render/eyes.ts +78 -0
- package/src/avatar/render/eyewear.ts +118 -0
- package/src/avatar/render/frame.ts +73 -0
- package/src/avatar/render/head.ts +81 -0
- package/src/avatar/render/mouth.ts +86 -0
- package/src/avatar/render/props.ts +91 -0
- package/src/avatar/render/types.ts +93 -0
- package/src/avatar/render/walker-parts.ts +121 -0
- package/src/avatar/render/walker.ts +199 -0
- package/src/avatar/scene.ts +119 -0
- package/src/avatar-pixi.ts +20 -0
- package/src/avatar-svg.ts +21 -0
- package/src/avatar-three.ts +18 -0
- package/src/avatar.ts +63 -0
- package/src/core/avatar/avatar-cache.ts +95 -0
- package/src/core/avatar/avatar-ref.ts +34 -0
- package/src/core/avatar/avatar-types.ts +43 -0
- package/src/core/avatar/hub-avatar.ts +308 -0
- package/src/core/connection.ts +408 -299
- package/src/core/errors.ts +160 -147
- package/src/core/game-data.ts +40 -38
- package/src/core/hub-economy.ts +216 -179
- package/src/core/hub-progress.ts +269 -205
- package/src/core/hub.ts +352 -131
- package/src/core/menu-control.ts +61 -61
- package/src/core/offline/cloud-saves.ts +62 -0
- package/src/core/offline/events.ts +50 -50
- package/src/core/offline/favorites.ts +21 -21
- package/src/core/offline/hub-kinds/avatar-kind.ts +42 -0
- package/src/core/offline/hub-kinds/daily-kind.ts +68 -0
- package/src/core/offline/hub-kinds/economy-kinds.ts +86 -0
- package/src/core/offline/hub-kinds/game-fields.ts +2 -0
- package/src/core/offline/hub-kinds/index.ts +52 -0
- package/src/core/offline/hub-kinds/legacy-ops.ts +37 -0
- package/src/core/offline/hub-kinds/progress-kinds.ts +165 -0
- package/src/core/offline/hub-kinds/resume-kind.ts +108 -0
- package/src/core/offline/hub-kinds/saved-kind.ts +93 -0
- package/src/core/offline/kind-adapters.ts +48 -0
- package/src/core/offline/local-inventory.ts +64 -0
- package/src/core/offline/local-store.ts +304 -261
- package/src/core/offline/offline-api.ts +65 -0
- package/src/core/offline/outbox-entry.ts +59 -0
- package/src/core/offline/outbox-kinds.ts +78 -0
- package/src/core/offline/outbox-merge.ts +39 -0
- package/src/core/offline/outbox-replayer.ts +141 -0
- package/src/core/offline/outbox-store.ts +85 -0
- package/src/core/offline/outbox.ts +232 -0
- package/src/core/offline/queue.ts +126 -171
- package/src/core/offline/replay-lease.ts +87 -0
- package/src/core/offline/replay-policy.ts +52 -0
- package/src/core/page-account.ts +121 -0
- package/src/core/page.ts +87 -74
- package/src/core/rest/endpoint-types.ts +22 -22
- package/src/core/rest/rest-client.ts +163 -152
- package/src/core/rest/routes.gen.ts +144 -88
- package/src/core/resume/hub-resume.ts +286 -0
- package/src/core/resume/local-resume.ts +130 -0
- package/src/core/resume/resume-list.ts +65 -0
- package/src/core/resume/resume-request.ts +52 -0
- package/src/core/resume/resume-types.ts +64 -0
- package/src/core/saved/daily-saves.ts +196 -0
- package/src/core/saved/daily-seed.ts +16 -0
- package/src/core/saved/hub-saved.ts +221 -0
- package/src/core/saved/local-saved.ts +122 -0
- package/src/core/saved/saved-list.ts +129 -0
- package/src/core/saved/saved-open.ts +104 -0
- package/src/core/saved/saved-order.ts +49 -0
- package/src/core/saved/saved-types.ts +24 -0
- package/src/core/storage/json-guards.ts +80 -74
- package/src/core/storage/key-value.ts +66 -66
- package/src/core/ticket.ts +43 -43
- package/src/core/turns/hub-turns.ts +161 -0
- package/src/core/turns/turn-game.ts +510 -0
- package/src/core/turns/your-turn-buzz.ts +69 -0
- package/src/core/types.ts +134 -134
- package/src/core/version/client-version.gen.ts +2 -2
- package/src/core/version/hub-version.ts +62 -62
- package/src/core/version/semver.ts +97 -97
- package/src/daily/daily-system.ts +276 -161
- package/src/daily/page-daily.ts +36 -36
- package/src/daily/types.ts +105 -67
- package/src/daily.ts +10 -10
- package/src/doctor/checks.ts +86 -76
- package/src/doctor/cli.ts +101 -106
- package/src/doctor/hub-version-fetch.ts +43 -0
- package/src/doctor/tsconfig.json +12 -12
- package/src/doctor/world-check.ts +61 -0
- package/src/globals.ts +115 -110
- package/src/index.ts +68 -50
- package/src/input/device-state.ts +146 -146
- package/src/input/edges.ts +55 -55
- package/src/input/focus-ring.ts +13 -0
- package/src/input/gamepad.ts +61 -61
- package/src/input/input-events.ts +32 -32
- package/src/input/input-registry.ts +171 -164
- package/src/input/input-system.ts +406 -393
- package/src/input/legacy-controls.ts +64 -64
- package/src/input/modality.ts +122 -0
- package/src/input/nav-state.ts +200 -191
- package/src/input/navigator.ts +182 -171
- package/src/input/on-screen-controls.ts +101 -101
- package/src/input/page-control.ts +43 -43
- package/src/input/page-input.ts +193 -193
- package/src/input/player-remaps.ts +77 -77
- package/src/input/source-values.ts +116 -116
- package/src/input/spatial.ts +47 -47
- package/src/input/types.ts +331 -331
- package/src/input/zero-config.ts +164 -177
- package/src/input.ts +26 -22
- package/src/mp.ts +10 -10
- package/src/overlay/controls-panel.ts +163 -163
- package/src/overlay/dom.ts +46 -46
- package/src/overlay/layout-clusters.ts +162 -162
- package/src/overlay/layout-geometry.ts +136 -136
- package/src/overlay/layout.ts +203 -203
- package/src/overlay/listener-scope.ts +23 -23
- package/src/overlay/on-screen-keyboard.ts +137 -137
- package/src/overlay/overlay.ts +234 -208
- package/src/overlay/styles.ts +13 -12
- package/src/overlay/virtual-controls.ts +262 -262
- package/src/race.ts +10 -10
- package/src/realtime/bridge.ts +126 -121
- package/src/realtime/cross-bundle.ts +29 -29
- package/src/realtime/fallback-notice.ts +48 -48
- package/src/realtime/hub-link.ts +65 -65
- package/src/realtime/latest-state.ts +33 -33
- package/src/realtime/multiplayer-forwarder.ts +40 -28
- package/src/realtime/multiplayer-types.ts +66 -52
- package/src/realtime/page-runtime.ts +60 -60
- package/src/realtime/race-forwarder.ts +59 -59
- package/src/realtime/race-types.ts +132 -132
- package/src/realtime/room-types.ts +151 -125
- package/src/realtime/rooms-forwarder.ts +104 -79
- package/src/realtime/runtime-api.ts +74 -49
- package/src/realtime/runtime-opener.ts +61 -61
- package/src/realtime/social-forwarder.ts +77 -72
- package/src/realtime/social-types.ts +86 -78
- package/src/realtime/types.ts +54 -53
- package/src/realtime/wire-types.ts +79 -72
- package/src/realtime/world-types.ts +20 -0
- package/src/rooms.ts +10 -10
- package/src/social.ts +19 -18
- package/src/turns-kit/check-rules.ts +281 -0
- package/src/turns-rules.ts +12 -0
- package/src/turns.ts +14 -0
- package/src/types.ts +13 -13
- package/src/uistack/page-stack.ts +45 -45
- package/src/uistack/play-suspension.ts +82 -82
- package/src/uistack/types.ts +76 -76
- package/src/uistack/ui-stack.ts +144 -144
|
@@ -1,164 +1,171 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The declared inputs, groups and axes, and their readings: each input's raw
|
|
3
|
-
* value this frame and last frame, from which edges, axes and vectors follow.
|
|
4
|
-
*/
|
|
5
|
-
import type { DeviceState } from './device-state';
|
|
6
|
-
import { clampUnit, readRawValue } from './source-values';
|
|
7
|
-
import type { AxisDef, InputConfig, InputDef, InputReading, InputSource, Vector } from './types';
|
|
8
|
-
|
|
9
|
-
/** One input's live state. */
|
|
10
|
-
interface LiveInput {
|
|
11
|
-
definition: InputDef;
|
|
12
|
-
readonly group: string;
|
|
13
|
-
raw: number;
|
|
14
|
-
previous: number;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/** Inputs cross this to count as pressed. */
|
|
18
|
-
const PRESS_THRESHOLD = 0.5;
|
|
19
|
-
|
|
20
|
-
/** One side of an axis definition as -1..1. */
|
|
21
|
-
function readAxisSide(side: AxisDef['x'], valueOf: (name: string) => number): number {
|
|
22
|
-
if (side === undefined) {
|
|
23
|
-
return 0;
|
|
24
|
-
}
|
|
25
|
-
if (typeof side === 'string') {
|
|
26
|
-
return valueOf(side);
|
|
27
|
-
}
|
|
28
|
-
return clampUnit(valueOf(side[1])) - clampUnit(valueOf(side[0]));
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/** The declared inputs and their readings. */
|
|
32
|
-
export class InputRegistry {
|
|
33
|
-
private readonly inputs = new Map<string, LiveInput>();
|
|
34
|
-
private readonly enabledGroups = new Map<string, boolean>();
|
|
35
|
-
private readonly axes = new Map<string, AxisDef>();
|
|
36
|
-
|
|
37
|
-
/** Replace the declared inputs. Groups keep an enabled state set before; new ones start as declared. */
|
|
38
|
-
declare(config: InputConfig, bindingOf: (name: string, declared: InputDef) => InputDef): void {
|
|
39
|
-
this.inputs.clear();
|
|
40
|
-
this.axes.clear();
|
|
41
|
-
for (const [group, groupDefinition] of Object.entries(config.groups)) {
|
|
42
|
-
if (!this.enabledGroups.has(group)) {
|
|
43
|
-
this.enabledGroups.set(group, groupDefinition.enabled ?? false);
|
|
44
|
-
}
|
|
45
|
-
for (const [name, definition] of Object.entries(groupDefinition.inputs)) {
|
|
46
|
-
this.inputs.set(name, { definition: bindingOf(name, definition), group, raw: 0, previous: 0 });
|
|
47
|
-
}
|
|
48
|
-
for (const [name, axis] of Object.entries(groupDefinition.axes ?? {})) {
|
|
49
|
-
this.axes.set(name, axis);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** The group an input belongs to. */
|
|
55
|
-
groupOf(name: string): string | undefined {
|
|
56
|
-
return this.inputs.get(name)?.group;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/** Replace one input's sources (after a rebind). */
|
|
60
|
-
redefine(name: string, definition: InputDef): void {
|
|
61
|
-
const input = this.inputs.get(name);
|
|
62
|
-
if (input !== undefined) {
|
|
63
|
-
input.definition = definition;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/** Turn a group on or off. On: held sources don't fire a phantom `down`. Off: its inputs read 0. */
|
|
68
|
-
setGroupEnabled(group: string, isEnabled: boolean, devices: DeviceState, source: InputSource): void {
|
|
69
|
-
this.enabledGroups.set(group, isEnabled);
|
|
70
|
-
for (const input of this.inputs.values()) {
|
|
71
|
-
if (input.group !== group) {
|
|
72
|
-
continue;
|
|
73
|
-
}
|
|
74
|
-
input.raw = isEnabled ? readRawValue(devices, input.definition, source) : 0;
|
|
75
|
-
if (isEnabled) {
|
|
76
|
-
input.previous = input.raw;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/** Whether a group is on. */
|
|
82
|
-
isEnabled(group: string): boolean {
|
|
83
|
-
return this.enabledGroups.get(group) === true;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/** The groups that are on. */
|
|
87
|
-
enabledGroupNames(): Set<string> {
|
|
88
|
-
return new Set([...this.enabledGroups].filter(([, isOn]) => isOn).map(([group]) => group));
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/** Take this frame's readings (inputs of disabled groups read 0). */
|
|
92
|
-
sample(devices: DeviceState, source: InputSource): void {
|
|
93
|
-
for (const input of this.inputs.values()) {
|
|
94
|
-
input.previous = input.raw;
|
|
95
|
-
input.raw = this.isEnabled(input.group) ? readRawValue(devices, input.definition, source) : 0;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/** Every input reads 0 this frame (the hub menu is open). */
|
|
100
|
-
silence(): void {
|
|
101
|
-
for (const input of this.inputs.values()) {
|
|
102
|
-
input.previous = input.raw;
|
|
103
|
-
input.raw = 0;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/** An input's value, 0..1. */
|
|
108
|
-
value(name: string): number {
|
|
109
|
-
return this.inputs.get(name)?.raw ?? 0;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/** Whether an input crossed the press threshold upward this frame. */
|
|
113
|
-
wentDown(name: string): boolean {
|
|
114
|
-
const input = this.inputs.get(name);
|
|
115
|
-
return input !== undefined && input.previous < PRESS_THRESHOLD && input.raw >= PRESS_THRESHOLD;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/** Whether an input crossed the press threshold downward this frame. */
|
|
119
|
-
wentUp(name: string): boolean {
|
|
120
|
-
const input = this.inputs.get(name);
|
|
121
|
-
return input !== undefined && input.previous >= PRESS_THRESHOLD && input.raw < PRESS_THRESHOLD;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/** Whether an input is held. */
|
|
125
|
-
isPressed(name: string): boolean {
|
|
126
|
-
return this.value(name) >= PRESS_THRESHOLD;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/** An input's full reading. */
|
|
130
|
-
reading(name: string): InputReading {
|
|
131
|
-
const raw = this.value(name);
|
|
132
|
-
return { value: raw, raw, isDown: this.wentDown(name), isUp: this.wentUp(name) };
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* The declared inputs, groups and axes, and their readings: each input's raw
|
|
3
|
+
* value this frame and last frame, from which edges, axes and vectors follow.
|
|
4
|
+
*/
|
|
5
|
+
import type { DeviceState } from './device-state';
|
|
6
|
+
import { clampUnit, readRawValue } from './source-values';
|
|
7
|
+
import type { AxisDef, InputConfig, InputDef, InputReading, InputSource, Vector } from './types';
|
|
8
|
+
|
|
9
|
+
/** One input's live state. */
|
|
10
|
+
interface LiveInput {
|
|
11
|
+
definition: InputDef;
|
|
12
|
+
readonly group: string;
|
|
13
|
+
raw: number;
|
|
14
|
+
previous: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Inputs cross this to count as pressed. */
|
|
18
|
+
const PRESS_THRESHOLD = 0.5;
|
|
19
|
+
|
|
20
|
+
/** One side of an axis definition as -1..1. */
|
|
21
|
+
function readAxisSide(side: AxisDef['x'], valueOf: (name: string) => number): number {
|
|
22
|
+
if (side === undefined) {
|
|
23
|
+
return 0;
|
|
24
|
+
}
|
|
25
|
+
if (typeof side === 'string') {
|
|
26
|
+
return valueOf(side);
|
|
27
|
+
}
|
|
28
|
+
return clampUnit(valueOf(side[1])) - clampUnit(valueOf(side[0]));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** The declared inputs and their readings. */
|
|
32
|
+
export class InputRegistry {
|
|
33
|
+
private readonly inputs = new Map<string, LiveInput>();
|
|
34
|
+
private readonly enabledGroups = new Map<string, boolean>();
|
|
35
|
+
private readonly axes = new Map<string, AxisDef>();
|
|
36
|
+
|
|
37
|
+
/** Replace the declared inputs. Groups keep an enabled state set before; new ones start as declared. */
|
|
38
|
+
declare(config: InputConfig, bindingOf: (name: string, declared: InputDef) => InputDef): void {
|
|
39
|
+
this.inputs.clear();
|
|
40
|
+
this.axes.clear();
|
|
41
|
+
for (const [group, groupDefinition] of Object.entries(config.groups)) {
|
|
42
|
+
if (!this.enabledGroups.has(group)) {
|
|
43
|
+
this.enabledGroups.set(group, groupDefinition.enabled ?? false);
|
|
44
|
+
}
|
|
45
|
+
for (const [name, definition] of Object.entries(groupDefinition.inputs)) {
|
|
46
|
+
this.inputs.set(name, { definition: bindingOf(name, definition), group, raw: 0, previous: 0 });
|
|
47
|
+
}
|
|
48
|
+
for (const [name, axis] of Object.entries(groupDefinition.axes ?? {})) {
|
|
49
|
+
this.axes.set(name, axis);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** The group an input belongs to. */
|
|
55
|
+
groupOf(name: string): string | undefined {
|
|
56
|
+
return this.inputs.get(name)?.group;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Replace one input's sources (after a rebind). */
|
|
60
|
+
redefine(name: string, definition: InputDef): void {
|
|
61
|
+
const input = this.inputs.get(name);
|
|
62
|
+
if (input !== undefined) {
|
|
63
|
+
input.definition = definition;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Turn a group on or off. On: held sources don't fire a phantom `down`. Off: its inputs read 0. */
|
|
68
|
+
setGroupEnabled(group: string, isEnabled: boolean, devices: DeviceState, source: InputSource): void {
|
|
69
|
+
this.enabledGroups.set(group, isEnabled);
|
|
70
|
+
for (const input of this.inputs.values()) {
|
|
71
|
+
if (input.group !== group) {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
input.raw = isEnabled ? readRawValue(devices, input.definition, source) : 0;
|
|
75
|
+
if (isEnabled) {
|
|
76
|
+
input.previous = input.raw;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Whether a group is on. */
|
|
82
|
+
isEnabled(group: string): boolean {
|
|
83
|
+
return this.enabledGroups.get(group) === true;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** The groups that are on. */
|
|
87
|
+
enabledGroupNames(): Set<string> {
|
|
88
|
+
return new Set([...this.enabledGroups].filter(([, isOn]) => isOn).map(([group]) => group));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Take this frame's readings (inputs of disabled groups read 0). */
|
|
92
|
+
sample(devices: DeviceState, source: InputSource): void {
|
|
93
|
+
for (const input of this.inputs.values()) {
|
|
94
|
+
input.previous = input.raw;
|
|
95
|
+
input.raw = this.isEnabled(input.group) ? readRawValue(devices, input.definition, source) : 0;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Every input reads 0 this frame (the hub menu is open). */
|
|
100
|
+
silence(): void {
|
|
101
|
+
for (const input of this.inputs.values()) {
|
|
102
|
+
input.previous = input.raw;
|
|
103
|
+
input.raw = 0;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** An input's value, 0..1. */
|
|
108
|
+
value(name: string): number {
|
|
109
|
+
return this.inputs.get(name)?.raw ?? 0;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Whether an input crossed the press threshold upward this frame. */
|
|
113
|
+
wentDown(name: string): boolean {
|
|
114
|
+
const input = this.inputs.get(name);
|
|
115
|
+
return input !== undefined && input.previous < PRESS_THRESHOLD && input.raw >= PRESS_THRESHOLD;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Whether an input crossed the press threshold downward this frame. */
|
|
119
|
+
wentUp(name: string): boolean {
|
|
120
|
+
const input = this.inputs.get(name);
|
|
121
|
+
return input !== undefined && input.previous >= PRESS_THRESHOLD && input.raw < PRESS_THRESHOLD;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Whether an input is held. */
|
|
125
|
+
isPressed(name: string): boolean {
|
|
126
|
+
return this.value(name) >= PRESS_THRESHOLD;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** An input's full reading. */
|
|
130
|
+
reading(name: string): InputReading {
|
|
131
|
+
const raw = this.value(name);
|
|
132
|
+
return { value: raw, raw, isDown: this.wentDown(name), isUp: this.wentUp(name) };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* A named axis (or an ad-hoc negative/positive pair) as -1..1: its `x`, or
|
|
137
|
+
* its `y` when it declares only `y` (a vertical one-axis control, like a
|
|
138
|
+
* paddle).
|
|
139
|
+
*/
|
|
140
|
+
axis(name: string, positive?: string): number {
|
|
141
|
+
if (positive !== undefined) {
|
|
142
|
+
return clampUnit(this.value(positive)) - clampUnit(this.value(name));
|
|
143
|
+
}
|
|
144
|
+
const axis = this.axes.get(name);
|
|
145
|
+
if (axis === undefined) {
|
|
146
|
+
return this.value(name);
|
|
147
|
+
}
|
|
148
|
+
return readAxisSide(axis.x ?? axis.y, (input) => this.value(input));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** A named axis pair as a vector clamped to the unit circle. */
|
|
152
|
+
vector(name: string): Vector {
|
|
153
|
+
const axis = this.axes.get(name);
|
|
154
|
+
if (axis === undefined) {
|
|
155
|
+
return { x: 0, y: 0, mag: 0 };
|
|
156
|
+
}
|
|
157
|
+
let horizontal = readAxisSide(axis.x, (input) => this.value(input));
|
|
158
|
+
let vertical = readAxisSide(axis.y, (input) => this.value(input));
|
|
159
|
+
const magnitude = Math.hypot(horizontal, vertical);
|
|
160
|
+
if (magnitude > 1) {
|
|
161
|
+
horizontal /= magnitude;
|
|
162
|
+
vertical /= magnitude;
|
|
163
|
+
}
|
|
164
|
+
return { x: horizontal, y: vertical, mag: Math.min(magnitude, 1) };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** An input's current sources (for the binding label). */
|
|
168
|
+
definitionOf(name: string): InputDef | undefined {
|
|
169
|
+
return this.inputs.get(name)?.definition;
|
|
170
|
+
}
|
|
171
|
+
}
|