@benev/tact 0.1.0-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +23 -0
- package/README.md +299 -0
- package/package.json +60 -0
- package/s/demo/main.bundle.ts +14 -0
- package/s/demo/main.css +57 -0
- package/s/index.html.ts +42 -0
- package/s/index.ts +16 -0
- package/s/nubs/lookpad/styles.ts +21 -0
- package/s/nubs/lookpad/utils/listeners.ts +53 -0
- package/s/nubs/lookpad/view.ts +32 -0
- package/s/nubs/stick/device.ts +30 -0
- package/s/nubs/stick/styles.ts +22 -0
- package/s/nubs/stick/utils/calculate_new_vector_from_pointer_position.ts +27 -0
- package/s/nubs/stick/utils/find_closest_point_on_circle.ts +15 -0
- package/s/nubs/stick/utils/make_pointer_listeners.ts +50 -0
- package/s/nubs/stick/utils/within_radius.ts +6 -0
- package/s/nubs/stick/view.ts +50 -0
- package/s/nubs/stick-graphic/styles.ts +38 -0
- package/s/nubs/stick-graphic/types/basis.ts +5 -0
- package/s/nubs/stick-graphic/utils/calculate_basis.ts +19 -0
- package/s/nubs/stick-graphic/utils/stick_vector_to_pixels.ts +13 -0
- package/s/nubs/stick-graphic/utils/transform.ts +10 -0
- package/s/nubs/stick-graphic/view.ts +43 -0
- package/s/nubs/virtual-gamepad/device.ts +25 -0
- package/s/nubs/virtual-gamepad/styles.css.ts +133 -0
- package/s/nubs/virtual-gamepad/utils/gamepad-inputs.ts +42 -0
- package/s/nubs/virtual-gamepad/utils/prevent-default-touch-shenanigans.ts +12 -0
- package/s/nubs/virtual-gamepad/utils/touch-tracking.ts +75 -0
- package/s/nubs/virtual-gamepad/view.ts +139 -0
- package/s/station/devices/gamepad.ts +81 -0
- package/s/station/devices/infra/device.ts +7 -0
- package/s/station/devices/infra/group.ts +17 -0
- package/s/station/devices/infra/sampler.ts +22 -0
- package/s/station/devices/keyboard.ts +53 -0
- package/s/station/devices/pointer.ts +95 -0
- package/s/station/parts/action.ts +26 -0
- package/s/station/parts/defaults.ts +28 -0
- package/s/station/parts/resolver.ts +73 -0
- package/s/station/parts/routines/aggregate_samples_into_map.ts +20 -0
- package/s/station/parts/routines/build_updatable_actions_structure.ts +29 -0
- package/s/station/parts/routines/lensing_algorithm.ts +74 -0
- package/s/station/parts/switchboard-bindings.ts +21 -0
- package/s/station/station.test.ts +86 -0
- package/s/station/station.ts +47 -0
- package/s/station/switchboard.ts +107 -0
- package/s/station/testing/testing.ts +47 -0
- package/s/station/types.ts +72 -0
- package/s/station/utils/is-pressed.ts +5 -0
- package/s/station/utils/modprefix.ts +16 -0
- package/s/station/utils/tmax.ts +7 -0
- package/s/station/utils/tmin.ts +7 -0
- package/s/tests.test.ts +8 -0
- package/s/utils/evergreen.ts +10 -0
- package/s/utils/gamepads.ts +41 -0
- package/s/utils/split-axis.ts +7 -0
- package/x/demo/main.bundle.d.ts +1 -0
- package/x/demo/main.bundle.js +11 -0
- package/x/demo/main.bundle.js.map +1 -0
- package/x/demo/main.bundle.min.js +139 -0
- package/x/demo/main.bundle.min.js.map +7 -0
- package/x/demo/main.css +57 -0
- package/x/index.d.ts +13 -0
- package/x/index.html +97 -0
- package/x/index.html.d.ts +2 -0
- package/x/index.html.js +37 -0
- package/x/index.html.js.map +1 -0
- package/x/index.js +14 -0
- package/x/index.js.map +1 -0
- package/x/nubs/lookpad/styles.d.ts +1 -0
- package/x/nubs/lookpad/styles.js +21 -0
- package/x/nubs/lookpad/styles.js.map +1 -0
- package/x/nubs/lookpad/utils/listeners.d.ts +19 -0
- package/x/nubs/lookpad/utils/listeners.js +37 -0
- package/x/nubs/lookpad/utils/listeners.js.map +1 -0
- package/x/nubs/lookpad/view.d.ts +1 -0
- package/x/nubs/lookpad/view.js +24 -0
- package/x/nubs/lookpad/view.js.map +1 -0
- package/x/nubs/stick/device.d.ts +15 -0
- package/x/nubs/stick/device.js +27 -0
- package/x/nubs/stick/device.js.map +1 -0
- package/x/nubs/stick/styles.d.ts +1 -0
- package/x/nubs/stick/styles.js +22 -0
- package/x/nubs/stick/styles.js.map +1 -0
- package/x/nubs/stick/utils/calculate_new_vector_from_pointer_position.d.ts +3 -0
- package/x/nubs/stick/utils/calculate_new_vector_from_pointer_position.js +16 -0
- package/x/nubs/stick/utils/calculate_new_vector_from_pointer_position.js.map +1 -0
- package/x/nubs/stick/utils/find_closest_point_on_circle.d.ts +2 -0
- package/x/nubs/stick/utils/find_closest_point_on_circle.js +6 -0
- package/x/nubs/stick/utils/find_closest_point_on_circle.js.map +1 -0
- package/x/nubs/stick/utils/make_pointer_listeners.d.ts +16 -0
- package/x/nubs/stick/utils/make_pointer_listeners.js +34 -0
- package/x/nubs/stick/utils/make_pointer_listeners.js.map +1 -0
- package/x/nubs/stick/utils/within_radius.d.ts +2 -0
- package/x/nubs/stick/utils/within_radius.js +4 -0
- package/x/nubs/stick/utils/within_radius.js.map +1 -0
- package/x/nubs/stick/view.d.ts +2 -0
- package/x/nubs/stick/view.js +38 -0
- package/x/nubs/stick/view.js.map +1 -0
- package/x/nubs/stick-graphic/styles.d.ts +1 -0
- package/x/nubs/stick-graphic/styles.js +38 -0
- package/x/nubs/stick-graphic/styles.js.map +1 -0
- package/x/nubs/stick-graphic/types/basis.d.ts +4 -0
- package/x/nubs/stick-graphic/types/basis.js +2 -0
- package/x/nubs/stick-graphic/types/basis.js.map +1 -0
- package/x/nubs/stick-graphic/utils/calculate_basis.d.ts +2 -0
- package/x/nubs/stick-graphic/utils/calculate_basis.js +10 -0
- package/x/nubs/stick-graphic/utils/calculate_basis.js.map +1 -0
- package/x/nubs/stick-graphic/utils/stick_vector_to_pixels.d.ts +2 -0
- package/x/nubs/stick-graphic/utils/stick_vector_to_pixels.js +7 -0
- package/x/nubs/stick-graphic/utils/stick_vector_to_pixels.js.map +1 -0
- package/x/nubs/stick-graphic/utils/transform.d.ts +2 -0
- package/x/nubs/stick-graphic/utils/transform.js +7 -0
- package/x/nubs/stick-graphic/utils/transform.js.map +1 -0
- package/x/nubs/stick-graphic/view.d.ts +3 -0
- package/x/nubs/stick-graphic/view.js +30 -0
- package/x/nubs/stick-graphic/view.js.map +1 -0
- package/x/nubs/virtual-gamepad/device.d.ts +7 -0
- package/x/nubs/virtual-gamepad/device.js +20 -0
- package/x/nubs/virtual-gamepad/device.js.map +1 -0
- package/x/nubs/virtual-gamepad/styles.css.d.ts +2 -0
- package/x/nubs/virtual-gamepad/styles.css.js +133 -0
- package/x/nubs/virtual-gamepad/styles.css.js.map +1 -0
- package/x/nubs/virtual-gamepad/utils/gamepad-inputs.d.ts +29 -0
- package/x/nubs/virtual-gamepad/utils/gamepad-inputs.js +31 -0
- package/x/nubs/virtual-gamepad/utils/gamepad-inputs.js.map +1 -0
- package/x/nubs/virtual-gamepad/utils/prevent-default-touch-shenanigans.d.ts +1 -0
- package/x/nubs/virtual-gamepad/utils/prevent-default-touch-shenanigans.js +9 -0
- package/x/nubs/virtual-gamepad/utils/prevent-default-touch-shenanigans.js.map +1 -0
- package/x/nubs/virtual-gamepad/utils/touch-tracking.d.ts +6 -0
- package/x/nubs/virtual-gamepad/utils/touch-tracking.js +55 -0
- package/x/nubs/virtual-gamepad/utils/touch-tracking.js.map +1 -0
- package/x/nubs/virtual-gamepad/view.d.ts +2 -0
- package/x/nubs/virtual-gamepad/view.js +120 -0
- package/x/nubs/virtual-gamepad/view.js.map +1 -0
- package/x/station/devices/gamepad.d.ts +10 -0
- package/x/station/devices/gamepad.js +70 -0
- package/x/station/devices/gamepad.js.map +1 -0
- package/x/station/devices/infra/device.d.ts +4 -0
- package/x/station/devices/infra/device.js +3 -0
- package/x/station/devices/infra/device.js.map +1 -0
- package/x/station/devices/infra/group.d.ts +7 -0
- package/x/station/devices/infra/group.js +13 -0
- package/x/station/devices/infra/group.js.map +1 -0
- package/x/station/devices/infra/sampler.d.ts +8 -0
- package/x/station/devices/infra/sampler.js +17 -0
- package/x/station/devices/infra/sampler.js.map +1 -0
- package/x/station/devices/keyboard.d.ts +9 -0
- package/x/station/devices/keyboard.js +42 -0
- package/x/station/devices/keyboard.js.map +1 -0
- package/x/station/devices/pointer.d.ts +11 -0
- package/x/station/devices/pointer.js +79 -0
- package/x/station/devices/pointer.js.map +1 -0
- package/x/station/parts/action.d.ts +12 -0
- package/x/station/parts/action.js +23 -0
- package/x/station/parts/action.js.map +1 -0
- package/x/station/parts/defaults.d.ts +5 -0
- package/x/station/parts/defaults.js +22 -0
- package/x/station/parts/defaults.js.map +1 -0
- package/x/station/parts/resolver.d.ts +10 -0
- package/x/station/parts/resolver.js +63 -0
- package/x/station/parts/resolver.js.map +1 -0
- package/x/station/parts/routines/aggregate_samples_into_map.d.ts +3 -0
- package/x/station/parts/routines/aggregate_samples_into_map.js +11 -0
- package/x/station/parts/routines/aggregate_samples_into_map.js.map +1 -0
- package/x/station/parts/routines/build_updatable_actions_structure.d.ts +5 -0
- package/x/station/parts/routines/build_updatable_actions_structure.js +18 -0
- package/x/station/parts/routines/build_updatable_actions_structure.js.map +1 -0
- package/x/station/parts/routines/lensing_algorithm.d.ts +2 -0
- package/x/station/parts/routines/lensing_algorithm.js +42 -0
- package/x/station/parts/routines/lensing_algorithm.js.map +1 -0
- package/x/station/parts/switchboard-bindings.d.ts +2 -0
- package/x/station/parts/switchboard-bindings.js +19 -0
- package/x/station/parts/switchboard-bindings.js.map +1 -0
- package/x/station/station.d.ts +15 -0
- package/x/station/station.js +35 -0
- package/x/station/station.js.map +1 -0
- package/x/station/station.test.d.ts +11 -0
- package/x/station/station.test.js +80 -0
- package/x/station/station.test.js.map +1 -0
- package/x/station/switchboard.d.ts +30 -0
- package/x/station/switchboard.js +90 -0
- package/x/station/switchboard.js.map +1 -0
- package/x/station/testing/testing.d.ts +58 -0
- package/x/station/testing/testing.js +39 -0
- package/x/station/testing/testing.js.map +1 -0
- package/x/station/types.d.ts +56 -0
- package/x/station/types.js +5 -0
- package/x/station/types.js.map +1 -0
- package/x/station/utils/is-pressed.d.ts +1 -0
- package/x/station/utils/is-pressed.js +4 -0
- package/x/station/utils/is-pressed.js.map +1 -0
- package/x/station/utils/modprefix.d.ts +1 -0
- package/x/station/utils/modprefix.js +16 -0
- package/x/station/utils/modprefix.js.map +1 -0
- package/x/station/utils/tmax.d.ts +1 -0
- package/x/station/utils/tmax.js +6 -0
- package/x/station/utils/tmax.js.map +1 -0
- package/x/station/utils/tmin.d.ts +1 -0
- package/x/station/utils/tmin.js +6 -0
- package/x/station/utils/tmin.js.map +1 -0
- package/x/tests.test.d.ts +1 -0
- package/x/tests.test.js +6 -0
- package/x/tests.test.js.map +1 -0
- package/x/utils/evergreen.d.ts +1 -0
- package/x/utils/evergreen.js +10 -0
- package/x/utils/evergreen.js.map +1 -0
- package/x/utils/gamepads.d.ts +14 -0
- package/x/utils/gamepads.js +40 -0
- package/x/utils/gamepads.js.map +1 -0
- package/x/utils/split-axis.d.ts +1 -0
- package/x/utils/split-axis.js +6 -0
- package/x/utils/split-axis.js.map +1 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
|
|
2
|
+
import {Science, test, expect} from "@e280/science"
|
|
3
|
+
import {SamplerDevice} from "./devices/infra/sampler.js"
|
|
4
|
+
import {testConnect, testSetupAlpha, testSetupBravo} from "./testing/testing.js"
|
|
5
|
+
|
|
6
|
+
export default Science.suite({
|
|
7
|
+
"sample to action value": test(async() => {
|
|
8
|
+
const {station, device, time} = testSetupAlpha()
|
|
9
|
+
expect(station.actions.basic.jump.value).is(0)
|
|
10
|
+
expect(station.actions.basic.shoot.value).is(0)
|
|
11
|
+
device.setSample("Space", 1)
|
|
12
|
+
station.poll(time.now)
|
|
13
|
+
expect(station.actions.basic.jump.value).is(1)
|
|
14
|
+
expect(station.actions.basic.shoot.value).is(0)
|
|
15
|
+
device.setSample("Space", 2)
|
|
16
|
+
device.setSample("pointer.button.left", 3)
|
|
17
|
+
station.poll(time.now)
|
|
18
|
+
expect(station.actions.basic.jump.value).is(2)
|
|
19
|
+
expect(station.actions.basic.shoot.value).is(3)
|
|
20
|
+
}),
|
|
21
|
+
|
|
22
|
+
"switchboard": Science.suite({
|
|
23
|
+
"player1 inputs work": test(async() => {
|
|
24
|
+
const {switchboard, time} = testSetupBravo()
|
|
25
|
+
const [s1, s2] = switchboard.stations
|
|
26
|
+
const d1 = testConnect(switchboard, new SamplerDevice())
|
|
27
|
+
expect(s1.actions.basic.jump.value).is(0)
|
|
28
|
+
expect(s2.actions.basic.jump.value).is(0)
|
|
29
|
+
d1.setSample("Space", 1)
|
|
30
|
+
switchboard.poll(time.now)
|
|
31
|
+
expect(s1.actions.basic.jump.value).is(1)
|
|
32
|
+
expect(s2.actions.basic.jump.value).is(0)
|
|
33
|
+
}),
|
|
34
|
+
|
|
35
|
+
"two devices playing on separate stations": test(async() => {
|
|
36
|
+
const {switchboard, time} = testSetupBravo()
|
|
37
|
+
const [s1, s2] = switchboard.stations
|
|
38
|
+
const d1 = testConnect(switchboard, new SamplerDevice())
|
|
39
|
+
const d2 = testConnect(switchboard, new SamplerDevice())
|
|
40
|
+
d1.setSample("Space", 1)
|
|
41
|
+
d2.setSample("Space", 2)
|
|
42
|
+
switchboard.poll(time.now)
|
|
43
|
+
expect(s1.actions.basic.jump.value).is(1)
|
|
44
|
+
expect(s2.actions.basic.jump.value).is(2)
|
|
45
|
+
}),
|
|
46
|
+
|
|
47
|
+
"player can shimmy": test(async() => {
|
|
48
|
+
const {switchboard, time} = testSetupBravo()
|
|
49
|
+
const [s1, s2] = switchboard.stations
|
|
50
|
+
const d1 = testConnect(switchboard, new SamplerDevice())
|
|
51
|
+
switchboard.shimmy(d1, 1)
|
|
52
|
+
d1.setSample("Space", 1)
|
|
53
|
+
switchboard.poll(time.now)
|
|
54
|
+
expect(s1.actions.basic.jump.value).is(0)
|
|
55
|
+
expect(s2.actions.basic.jump.value).is(1)
|
|
56
|
+
}),
|
|
57
|
+
|
|
58
|
+
"two players can share a station": test(async() => {
|
|
59
|
+
const {switchboard, time} = testSetupBravo()
|
|
60
|
+
const [s1, s2] = switchboard.stations
|
|
61
|
+
const d1 = testConnect(switchboard, new SamplerDevice())
|
|
62
|
+
const d2 = testConnect(switchboard, new SamplerDevice())
|
|
63
|
+
switchboard.shimmy(d2, -1)
|
|
64
|
+
expect(switchboard.stationByDevice(d1)).is(switchboard.stationByDevice(d2))
|
|
65
|
+
d1.setSample("Space", 1)
|
|
66
|
+
switchboard.poll(time.now)
|
|
67
|
+
expect(s1.actions.basic.jump.value).is(1)
|
|
68
|
+
expect(s2.actions.basic.jump.value).is(0)
|
|
69
|
+
d2.setSample("Space", 1)
|
|
70
|
+
switchboard.poll(time.now)
|
|
71
|
+
expect(s1.actions.basic.jump.value).is(1)
|
|
72
|
+
expect(s2.actions.basic.jump.value).is(0)
|
|
73
|
+
d1.setSample("Space", 1)
|
|
74
|
+
d2.setSample("Space", 2)
|
|
75
|
+
switchboard.poll(time.now)
|
|
76
|
+
expect(s1.actions.basic.jump.value).is(2)
|
|
77
|
+
expect(s2.actions.basic.jump.value).is(0)
|
|
78
|
+
d1.setSample("Space", 2)
|
|
79
|
+
d2.setSample("Space", 1)
|
|
80
|
+
switchboard.poll(time.now)
|
|
81
|
+
expect(s1.actions.basic.jump.value).is(2)
|
|
82
|
+
expect(s2.actions.basic.jump.value).is(0)
|
|
83
|
+
}),
|
|
84
|
+
}),
|
|
85
|
+
})
|
|
86
|
+
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
|
|
2
|
+
import {SetG} from "@e280/stz"
|
|
3
|
+
import {Resolver} from "./parts/resolver.js"
|
|
4
|
+
import {Bindings, SampleMap} from "./types.js"
|
|
5
|
+
import {Device} from "./devices/infra/device.js"
|
|
6
|
+
import {aggregate_samples_into_map} from "./parts/routines/aggregate_samples_into_map.js"
|
|
7
|
+
|
|
8
|
+
export class Station<B extends Bindings> {
|
|
9
|
+
readonly modes = new SetG<keyof B>()
|
|
10
|
+
readonly devices = new SetG<Device>()
|
|
11
|
+
|
|
12
|
+
#resolver: Resolver<B>
|
|
13
|
+
#samples: SampleMap = new Map()
|
|
14
|
+
|
|
15
|
+
constructor(bindings: B) {
|
|
16
|
+
this.#resolver = new Resolver(bindings, this.modes, this.#samples)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
get actions() {
|
|
20
|
+
return this.#resolver.actions
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
get bindings() {
|
|
24
|
+
return this.#resolver.bindings
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
set bindings(bindings: B) {
|
|
28
|
+
this.#resolver.bindings = bindings
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
addModes(...modes: (keyof B)[]) {
|
|
32
|
+
this.modes.adds(...modes)
|
|
33
|
+
return this
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
addDevices(...devices: Device[]) {
|
|
37
|
+
this.devices.adds(...devices)
|
|
38
|
+
return this
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
poll(now: number) {
|
|
42
|
+
this.#samples.clear()
|
|
43
|
+
aggregate_samples_into_map(this.devices, this.#samples)
|
|
44
|
+
this.#resolver.poll(now)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
|
|
2
|
+
import {SetG} from "@e280/stz"
|
|
3
|
+
import {Scalar} from "@benev/math"
|
|
4
|
+
import {Station} from "./station.js"
|
|
5
|
+
import {Device} from "./devices/infra/device.js"
|
|
6
|
+
import {switchboardMode, SwitchboardBindings} from "./types.js"
|
|
7
|
+
import {switchboardBindings} from "./parts/switchboard-bindings.js"
|
|
8
|
+
|
|
9
|
+
export class Switchboard<B extends SwitchboardBindings> {
|
|
10
|
+
static readonly mode = switchboardMode
|
|
11
|
+
static readonly bindings = switchboardBindings
|
|
12
|
+
|
|
13
|
+
readonly devices = new SetG<Device>()
|
|
14
|
+
|
|
15
|
+
constructor(public readonly stations: Station<B>[]) {
|
|
16
|
+
for (const station of stations) {
|
|
17
|
+
const fn = (delta: 1 | -1) => () => {
|
|
18
|
+
const device = this.deviceByStation(station)
|
|
19
|
+
if (device) this.shimmy(device, delta)
|
|
20
|
+
}
|
|
21
|
+
station.actions.switchboard.shimmyNext.onDown(fn(1))
|
|
22
|
+
station.actions.switchboard.shimmyPrevious.onDown(fn(-1))
|
|
23
|
+
station.modes.add(Switchboard.mode)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
*[Symbol.iterator]() {
|
|
28
|
+
yield* this.stations.values()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
*entries() {
|
|
32
|
+
yield* this.stations.entries()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** poll all the stations */
|
|
36
|
+
poll(now: number) {
|
|
37
|
+
for (const station of this.stations)
|
|
38
|
+
station.poll(now)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** check if a station has a known switchboard device assigned */
|
|
42
|
+
isActive(station: Station<B>) {
|
|
43
|
+
return !!this.deviceByStation(station)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
stationByIndex(index: number) {
|
|
47
|
+
const station = this.stations.at(index)
|
|
48
|
+
if (!station) throw new Error(`station ${index} not found`)
|
|
49
|
+
return station
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
stationByDevice(device: Device) {
|
|
53
|
+
for (const station of this.stations) {
|
|
54
|
+
if (station.devices.has(device))
|
|
55
|
+
return station
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
deviceByStation(station: Station<B>) {
|
|
60
|
+
for (const device of this.devices) {
|
|
61
|
+
if (station.devices.has(device))
|
|
62
|
+
return device
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** move a player's device to the next or previous station */
|
|
67
|
+
shimmy(device: Device, indexDelta: 1 | -1) {
|
|
68
|
+
const oldIndex = this.stations.findIndex(station => station.devices.has(device))
|
|
69
|
+
const maxIndex = Math.max(0, this.stations.length - 1)
|
|
70
|
+
const newIndex = Scalar.clamp(oldIndex + indexDelta, 0, maxIndex)
|
|
71
|
+
const station = this.stationByIndex(newIndex)
|
|
72
|
+
this.connect(device, station)
|
|
73
|
+
return station
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** connect-or-assign a device to a station */
|
|
77
|
+
connect<D extends Device>(
|
|
78
|
+
device: D,
|
|
79
|
+
station: Station<B> = this.chooseLonelyStation(),
|
|
80
|
+
) {
|
|
81
|
+
this.#removeDeviceFromAllStations(device)
|
|
82
|
+
this.devices.add(device)
|
|
83
|
+
station.devices.add(device)
|
|
84
|
+
return () => this.disconnect(device)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** unplug a player's device */
|
|
88
|
+
disconnect(device: Device) {
|
|
89
|
+
this.#removeDeviceFromAllStations(device)
|
|
90
|
+
this.devices.delete(device)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** returns an unassigned station (otherwise the last one) */
|
|
94
|
+
chooseLonelyStation() {
|
|
95
|
+
for (const station of this.stations) {
|
|
96
|
+
if (!this.deviceByStation(station))
|
|
97
|
+
return station
|
|
98
|
+
}
|
|
99
|
+
const index = Math.max(0, this.stations.length - 1)
|
|
100
|
+
return this.stationByIndex(index)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
#removeDeviceFromAllStations(device: Device) {
|
|
104
|
+
this.stations.forEach(station => station.devices.delete(device))
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
|
|
2
|
+
import {Station} from "../station.js"
|
|
3
|
+
import {asBindings} from "../types.js"
|
|
4
|
+
import {Switchboard} from "../switchboard.js"
|
|
5
|
+
import {Device} from "../devices/infra/device.js"
|
|
6
|
+
import {SamplerDevice} from "../devices/infra/sampler.js"
|
|
7
|
+
import {switchboardBindings} from "../parts/switchboard-bindings.js"
|
|
8
|
+
|
|
9
|
+
export class TestTime {
|
|
10
|
+
frame = 0
|
|
11
|
+
|
|
12
|
+
get now() {
|
|
13
|
+
return (this.frame++) * (1000 / 60)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function testBindings() {
|
|
18
|
+
return asBindings({
|
|
19
|
+
basic: {
|
|
20
|
+
jump: [{lenses: [{code: "Space"}]}],
|
|
21
|
+
shoot: [{lenses: [{code: "pointer.button.left"}]}],
|
|
22
|
+
},
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function testConnect<D extends Device>(switchboard: Switchboard<any>, device: D) {
|
|
27
|
+
switchboard.connect(device)
|
|
28
|
+
return device
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function testSetupAlpha() {
|
|
32
|
+
const time = new TestTime()
|
|
33
|
+
const device = new SamplerDevice()
|
|
34
|
+
const station = new Station(testBindings())
|
|
35
|
+
.addModes("basic")
|
|
36
|
+
.addDevices(device)
|
|
37
|
+
return {device, station, time}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function testSetupBravo() {
|
|
41
|
+
const time = new TestTime()
|
|
42
|
+
const station = () => new Station(switchboardBindings(testBindings()))
|
|
43
|
+
.addModes(Switchboard.mode, "basic")
|
|
44
|
+
const switchboard = new Switchboard([station(), station(), station(), station()])
|
|
45
|
+
return {switchboard, time}
|
|
46
|
+
}
|
|
47
|
+
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
|
|
2
|
+
import {Action} from "./parts/action.js"
|
|
3
|
+
|
|
4
|
+
export type Actions<B extends Bindings> = {
|
|
5
|
+
[Mode in keyof B]: {
|
|
6
|
+
[K in keyof B[Mode]]: Action
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type Sample = [code: string, value: number]
|
|
11
|
+
export type SampleMap = Map<string, number>
|
|
12
|
+
|
|
13
|
+
export type Timing = (
|
|
14
|
+
| DirectTiming
|
|
15
|
+
| TapTiming
|
|
16
|
+
| HoldTiming
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
export type DirectTiming = {style: "direct"}
|
|
20
|
+
export type TapTiming = {style: "tap", holdTime?: number}
|
|
21
|
+
export type HoldTiming = {style: "hold", holdTime?: number}
|
|
22
|
+
|
|
23
|
+
export type LensSettings = {
|
|
24
|
+
scale: number
|
|
25
|
+
invert: boolean
|
|
26
|
+
deadzone: number
|
|
27
|
+
timing: Timing
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type Lens = {
|
|
31
|
+
code: string
|
|
32
|
+
settings?: Partial<LensSettings>
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type LensState = {
|
|
36
|
+
lastValue: number
|
|
37
|
+
holdStart: number
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type Spoon = {
|
|
41
|
+
lenses: Lens[]
|
|
42
|
+
required?: Lens[]
|
|
43
|
+
forbidden?: Lens[]
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type Fork = Spoon[]
|
|
47
|
+
|
|
48
|
+
export type Bracket = {
|
|
49
|
+
[action: string]: Spoon[]
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type Bindings = {
|
|
53
|
+
[mode: string]: Bracket
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export type AsBindings<B extends Bindings> = B
|
|
57
|
+
|
|
58
|
+
export function asBindings<B extends Bindings>(bindings: B) {
|
|
59
|
+
return bindings
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const switchboardMode = "switchboard" as const
|
|
63
|
+
|
|
64
|
+
export type AsSwitchboardBindings<B extends Bindings> = {
|
|
65
|
+
[switchboardMode]: {
|
|
66
|
+
shimmyNext: Spoon[],
|
|
67
|
+
shimmyPrevious: Spoon[],
|
|
68
|
+
}
|
|
69
|
+
} & B
|
|
70
|
+
|
|
71
|
+
export type SwitchboardBindings = AsSwitchboardBindings<Bindings>
|
|
72
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
|
|
2
|
+
export function modprefix(event: KeyboardEvent | PointerEvent | WheelEvent, code: string) {
|
|
3
|
+
const modifiers: string[] = []
|
|
4
|
+
|
|
5
|
+
if (event.ctrlKey) modifiers.push("ctrl")
|
|
6
|
+
if (event.altKey) modifiers.push("alt")
|
|
7
|
+
if (event.metaKey) modifiers.push("meta")
|
|
8
|
+
if (event.shiftKey) modifiers.push("shift")
|
|
9
|
+
|
|
10
|
+
const prefix = modifiers.length > 0
|
|
11
|
+
? [...modifiers].join("-")
|
|
12
|
+
: "x"
|
|
13
|
+
|
|
14
|
+
return `${prefix}-${code}`
|
|
15
|
+
}
|
|
16
|
+
|
package/s/tests.test.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
|
|
2
|
+
import {Disposable, ev, MapG} from "@e280/stz"
|
|
3
|
+
import {evergreen} from "./evergreen.js"
|
|
4
|
+
|
|
5
|
+
/** stable reference to a gamepad, has a getter to get the latest gamepad snapshot */
|
|
6
|
+
export class Pad {
|
|
7
|
+
constructor(private get: () => Gamepad) {}
|
|
8
|
+
|
|
9
|
+
get gamepad() {
|
|
10
|
+
return this.get()
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** track gamepad lifecycles as they connect or disconnect */
|
|
15
|
+
export function gamepads(on: (pad: Pad) => () => void) {
|
|
16
|
+
const pads = new MapG<number, {pad: Pad, dispose: () => void}>()
|
|
17
|
+
return ev(window, {
|
|
18
|
+
gamepadconnected: ({gamepad}: GamepadEvent) => {
|
|
19
|
+
const get = () => navigator.getGamepads().at(gamepad.index)
|
|
20
|
+
const pad = new Pad(evergreen(gamepad, get))
|
|
21
|
+
const dispose = on(pad)
|
|
22
|
+
pads.set(gamepad.index, {pad, dispose})
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
gamepaddisconnected: ({gamepad}: GamepadEvent) => {
|
|
26
|
+
const result = pads.get(gamepad.index)
|
|
27
|
+
if (result) {
|
|
28
|
+
pads.delete(gamepad.index)
|
|
29
|
+
result.dispose()
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export class Gamepads implements Disposable {
|
|
36
|
+
dispose: () => void
|
|
37
|
+
constructor(public on: (pad: Pad) => () => void) {
|
|
38
|
+
this.dispose = gamepads(on)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { $, view } from "@e280/sly";
|
|
2
|
+
import { NubStick } from "../nubs/stick/view.js";
|
|
3
|
+
import { StickDevice } from "../nubs/stick/device.js";
|
|
4
|
+
$.register({
|
|
5
|
+
TactDemo: view.component(use => {
|
|
6
|
+
const stick = use.once(() => new StickDevice());
|
|
7
|
+
return NubStick(stick);
|
|
8
|
+
}),
|
|
9
|
+
});
|
|
10
|
+
console.log("tact");
|
|
11
|
+
//# sourceMappingURL=main.bundle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.bundle.js","sourceRoot":"","sources":["../../s/demo/main.bundle.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,CAAC,EAAE,IAAI,EAAC,MAAM,WAAW,CAAA;AACjC,OAAO,EAAC,QAAQ,EAAC,MAAM,uBAAuB,CAAA;AAC9C,OAAO,EAAC,WAAW,EAAC,MAAM,yBAAyB,CAAA;AAEnD,CAAC,CAAC,QAAQ,CAAC;IACV,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE;QAC9B,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,WAAW,EAAE,CAAC,CAAA;QAC/C,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAA;IACvB,CAAC,CAAC;CACF,CAAC,CAAA;AAEF,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA"}
|