@benev/tact 0.1.0-1 → 0.1.0-3

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 (213) hide show
  1. package/README.md +265 -140
  2. package/package.json +5 -3
  3. package/s/{station/parts → core/bindings}/action.ts +3 -9
  4. package/s/core/bindings/parts/defaults.ts +29 -0
  5. package/s/{station/parts/routines/lensing_algorithm.ts → core/bindings/parts/lens-algo.ts} +15 -14
  6. package/s/core/bindings/resolver.ts +96 -0
  7. package/s/core/bindings/types.ts +57 -0
  8. package/s/core/controllers/controller.ts +7 -0
  9. package/s/core/controllers/infra/group.ts +17 -0
  10. package/s/{station/devices → core/controllers}/infra/sampler.ts +3 -3
  11. package/s/{station/devices → core/controllers/standard}/gamepad.ts +5 -9
  12. package/s/core/controllers/standard/index.ts +7 -0
  13. package/s/{station/devices → core/controllers/standard}/keyboard.ts +3 -6
  14. package/s/{station/devices → core/controllers/standard}/pointer.ts +31 -37
  15. package/s/{nubs/stick/device.ts → core/controllers/standard/stick.ts} +3 -3
  16. package/s/{nubs/virtual-gamepad/device.ts → core/controllers/standard/virtual-gamepad.ts} +5 -5
  17. package/s/core/controllers/types.ts +4 -0
  18. package/s/core/core.test.ts +90 -0
  19. package/s/core/deck/deck.ts +40 -0
  20. package/s/core/deck/parts/bindings-depot.ts +24 -0
  21. package/s/core/deck/parts/local-storage-kv.ts +7 -0
  22. package/s/core/hub/auto-gamepads.ts +8 -0
  23. package/s/core/hub/bindings.ts +18 -0
  24. package/s/core/hub/hub.ts +102 -0
  25. package/s/core/hub/types.ts +14 -0
  26. package/s/core/index.ts +21 -0
  27. package/s/core/port/port.ts +34 -0
  28. package/s/core/port/utils/aggregate_samples_into_map.ts +20 -0
  29. package/s/core/port/utils/wipe_samples_map.ts +8 -0
  30. package/s/core/testing/testing.ts +50 -0
  31. package/s/demo/main.bundle.ts +2 -2
  32. package/s/index.ts +1 -11
  33. package/s/nubs/stick/view.ts +2 -2
  34. package/s/nubs/virtual-gamepad/view.ts +6 -6
  35. package/s/tests.test.ts +2 -4
  36. package/s/{station/utils/tmin.ts → utils/quick-math.ts} +6 -0
  37. package/x/{station/parts → core/bindings}/action.d.ts +1 -3
  38. package/x/{station/parts → core/bindings}/action.js +3 -10
  39. package/x/core/bindings/action.js.map +1 -0
  40. package/x/core/bindings/parts/defaults.d.ts +3 -0
  41. package/x/core/bindings/parts/defaults.js +23 -0
  42. package/x/core/bindings/parts/defaults.js.map +1 -0
  43. package/x/core/bindings/parts/is-pressed.js.map +1 -0
  44. package/x/core/bindings/parts/lens-algo.d.ts +2 -0
  45. package/x/{station/parts/routines/lensing_algorithm.js → core/bindings/parts/lens-algo.js} +12 -11
  46. package/x/core/bindings/parts/lens-algo.js.map +1 -0
  47. package/x/core/bindings/resolver.d.ts +8 -0
  48. package/x/core/bindings/resolver.js +87 -0
  49. package/x/core/bindings/resolver.js.map +1 -0
  50. package/x/core/bindings/types.d.ts +38 -0
  51. package/x/core/bindings/types.js +4 -0
  52. package/x/core/bindings/types.js.map +1 -0
  53. package/x/core/controllers/controller.d.ts +4 -0
  54. package/x/core/controllers/controller.js +3 -0
  55. package/x/core/controllers/controller.js.map +1 -0
  56. package/x/core/controllers/infra/group.d.ts +7 -0
  57. package/x/core/controllers/infra/group.js +13 -0
  58. package/x/core/controllers/infra/group.js.map +1 -0
  59. package/x/core/controllers/infra/sampler.d.ts +8 -0
  60. package/x/{station/devices → core/controllers}/infra/sampler.js +2 -2
  61. package/x/core/controllers/infra/sampler.js.map +1 -0
  62. package/x/core/controllers/standard/gamepad.d.ts +9 -0
  63. package/x/{station/devices → core/controllers/standard}/gamepad.js +4 -8
  64. package/x/core/controllers/standard/gamepad.js.map +1 -0
  65. package/x/core/controllers/standard/index.d.ts +5 -0
  66. package/x/core/controllers/standard/index.js +6 -0
  67. package/x/core/controllers/standard/index.js.map +1 -0
  68. package/x/{station/devices → core/controllers/standard}/keyboard.d.ts +3 -3
  69. package/x/{station/devices → core/controllers/standard}/keyboard.js +3 -6
  70. package/x/core/controllers/standard/keyboard.js.map +1 -0
  71. package/x/{station/devices → core/controllers/standard}/pointer.d.ts +3 -3
  72. package/x/{station/devices → core/controllers/standard}/pointer.js +26 -31
  73. package/x/core/controllers/standard/pointer.js.map +1 -0
  74. package/x/{nubs/stick/device.d.ts → core/controllers/standard/stick.d.ts} +2 -2
  75. package/x/{nubs/stick/device.js → core/controllers/standard/stick.js} +4 -4
  76. package/x/core/controllers/standard/stick.js.map +1 -0
  77. package/x/core/controllers/standard/virtual-gamepad.d.ts +7 -0
  78. package/x/{nubs/virtual-gamepad/device.js → core/controllers/standard/virtual-gamepad.js} +6 -6
  79. package/x/core/controllers/standard/virtual-gamepad.js.map +1 -0
  80. package/x/core/controllers/types.d.ts +2 -0
  81. package/x/core/controllers/types.js +2 -0
  82. package/x/core/controllers/types.js.map +1 -0
  83. package/x/core/core.test.d.ts +11 -0
  84. package/x/core/core.test.js +84 -0
  85. package/x/core/core.test.js.map +1 -0
  86. package/x/core/deck/deck.d.ts +16 -0
  87. package/x/core/deck/deck.js +31 -0
  88. package/x/core/deck/deck.js.map +1 -0
  89. package/x/core/deck/parts/bindings-depot.d.ts +9 -0
  90. package/x/core/deck/parts/bindings-depot.js +19 -0
  91. package/x/core/deck/parts/bindings-depot.js.map +1 -0
  92. package/x/core/deck/parts/local-storage-kv.d.ts +2 -0
  93. package/x/core/deck/parts/local-storage-kv.js +5 -0
  94. package/x/core/deck/parts/local-storage-kv.js.map +1 -0
  95. package/x/core/hub/auto-gamepads.d.ts +2 -0
  96. package/x/core/hub/auto-gamepads.js +6 -0
  97. package/x/core/hub/auto-gamepads.js.map +1 -0
  98. package/x/core/hub/bindings.d.ts +2 -0
  99. package/x/core/hub/bindings.js +16 -0
  100. package/x/core/hub/bindings.js.map +1 -0
  101. package/x/core/hub/hub.d.ts +28 -0
  102. package/x/core/hub/hub.js +91 -0
  103. package/x/core/hub/hub.js.map +1 -0
  104. package/x/core/hub/types.d.ts +9 -0
  105. package/x/core/hub/types.js +2 -0
  106. package/x/core/hub/types.js.map +1 -0
  107. package/x/core/index.d.ts +15 -0
  108. package/x/core/index.js +16 -0
  109. package/x/core/index.js.map +1 -0
  110. package/x/core/port/port.d.ts +12 -0
  111. package/x/core/port/port.js +25 -0
  112. package/x/core/port/port.js.map +1 -0
  113. package/x/core/port/utils/aggregate_samples_into_map.d.ts +3 -0
  114. package/x/core/port/utils/aggregate_samples_into_map.js +11 -0
  115. package/x/core/port/utils/aggregate_samples_into_map.js.map +1 -0
  116. package/x/core/port/utils/wipe_samples_map.d.ts +2 -0
  117. package/x/core/port/utils/wipe_samples_map.js +5 -0
  118. package/x/core/port/utils/wipe_samples_map.js.map +1 -0
  119. package/x/core/testing/testing.d.ts +38 -0
  120. package/x/core/testing/testing.js +42 -0
  121. package/x/core/testing/testing.js.map +1 -0
  122. package/x/demo/main.bundle.js +2 -2
  123. package/x/demo/main.bundle.js.map +1 -1
  124. package/x/demo/main.bundle.min.js +1 -1
  125. package/x/demo/main.bundle.min.js.map +4 -4
  126. package/x/index.d.ts +1 -11
  127. package/x/index.html +3 -3
  128. package/x/index.js +1 -11
  129. package/x/index.js.map +1 -1
  130. package/x/nubs/stick/view.d.ts +2 -2
  131. package/x/nubs/stick/view.js.map +1 -1
  132. package/x/nubs/virtual-gamepad/view.d.ts +2 -2
  133. package/x/nubs/virtual-gamepad/view.js +5 -5
  134. package/x/nubs/virtual-gamepad/view.js.map +1 -1
  135. package/x/tests.test.js +2 -4
  136. package/x/tests.test.js.map +1 -1
  137. package/x/{station/utils/tmax.d.ts → utils/quick-math.d.ts} +1 -0
  138. package/x/utils/quick-math.js +11 -0
  139. package/x/utils/quick-math.js.map +1 -0
  140. package/s/station/devices/infra/device.ts +0 -7
  141. package/s/station/devices/infra/group.ts +0 -17
  142. package/s/station/parts/defaults.ts +0 -28
  143. package/s/station/parts/resolver.ts +0 -73
  144. package/s/station/parts/routines/aggregate_samples_into_map.ts +0 -20
  145. package/s/station/parts/routines/build_updatable_actions_structure.ts +0 -29
  146. package/s/station/parts/switchboard-bindings.ts +0 -21
  147. package/s/station/station.test.ts +0 -86
  148. package/s/station/station.ts +0 -47
  149. package/s/station/switchboard.ts +0 -107
  150. package/s/station/testing/testing.ts +0 -47
  151. package/s/station/types.ts +0 -72
  152. package/s/station/utils/modprefix.ts +0 -16
  153. package/s/station/utils/tmax.ts +0 -7
  154. package/x/nubs/stick/device.js.map +0 -1
  155. package/x/nubs/virtual-gamepad/device.d.ts +0 -7
  156. package/x/nubs/virtual-gamepad/device.js.map +0 -1
  157. package/x/station/devices/gamepad.d.ts +0 -10
  158. package/x/station/devices/gamepad.js.map +0 -1
  159. package/x/station/devices/infra/device.d.ts +0 -4
  160. package/x/station/devices/infra/device.js +0 -3
  161. package/x/station/devices/infra/device.js.map +0 -1
  162. package/x/station/devices/infra/group.d.ts +0 -7
  163. package/x/station/devices/infra/group.js +0 -13
  164. package/x/station/devices/infra/group.js.map +0 -1
  165. package/x/station/devices/infra/sampler.d.ts +0 -8
  166. package/x/station/devices/infra/sampler.js.map +0 -1
  167. package/x/station/devices/keyboard.js.map +0 -1
  168. package/x/station/devices/pointer.js.map +0 -1
  169. package/x/station/parts/action.js.map +0 -1
  170. package/x/station/parts/defaults.d.ts +0 -5
  171. package/x/station/parts/defaults.js +0 -22
  172. package/x/station/parts/defaults.js.map +0 -1
  173. package/x/station/parts/resolver.d.ts +0 -10
  174. package/x/station/parts/resolver.js +0 -63
  175. package/x/station/parts/resolver.js.map +0 -1
  176. package/x/station/parts/routines/aggregate_samples_into_map.d.ts +0 -3
  177. package/x/station/parts/routines/aggregate_samples_into_map.js +0 -11
  178. package/x/station/parts/routines/aggregate_samples_into_map.js.map +0 -1
  179. package/x/station/parts/routines/build_updatable_actions_structure.d.ts +0 -5
  180. package/x/station/parts/routines/build_updatable_actions_structure.js +0 -18
  181. package/x/station/parts/routines/build_updatable_actions_structure.js.map +0 -1
  182. package/x/station/parts/routines/lensing_algorithm.d.ts +0 -2
  183. package/x/station/parts/routines/lensing_algorithm.js.map +0 -1
  184. package/x/station/parts/switchboard-bindings.d.ts +0 -2
  185. package/x/station/parts/switchboard-bindings.js +0 -19
  186. package/x/station/parts/switchboard-bindings.js.map +0 -1
  187. package/x/station/station.d.ts +0 -15
  188. package/x/station/station.js +0 -35
  189. package/x/station/station.js.map +0 -1
  190. package/x/station/station.test.d.ts +0 -11
  191. package/x/station/station.test.js +0 -80
  192. package/x/station/station.test.js.map +0 -1
  193. package/x/station/switchboard.d.ts +0 -30
  194. package/x/station/switchboard.js +0 -90
  195. package/x/station/switchboard.js.map +0 -1
  196. package/x/station/testing/testing.d.ts +0 -58
  197. package/x/station/testing/testing.js +0 -39
  198. package/x/station/testing/testing.js.map +0 -1
  199. package/x/station/types.d.ts +0 -56
  200. package/x/station/types.js +0 -5
  201. package/x/station/types.js.map +0 -1
  202. package/x/station/utils/is-pressed.js.map +0 -1
  203. package/x/station/utils/modprefix.d.ts +0 -1
  204. package/x/station/utils/modprefix.js +0 -16
  205. package/x/station/utils/modprefix.js.map +0 -1
  206. package/x/station/utils/tmax.js +0 -6
  207. package/x/station/utils/tmax.js.map +0 -1
  208. package/x/station/utils/tmin.d.ts +0 -1
  209. package/x/station/utils/tmin.js +0 -6
  210. package/x/station/utils/tmin.js.map +0 -1
  211. /package/s/{station/utils → core/bindings/parts}/is-pressed.ts +0 -0
  212. /package/x/{station/utils → core/bindings/parts}/is-pressed.d.ts +0 -0
  213. /package/x/{station/utils → core/bindings/parts}/is-pressed.js +0 -0
package/README.md CHANGED
@@ -1,50 +1,126 @@
1
1
 
2
- # @benev/tact
3
- > user input keybindings and gamepad support for web games
2
+ # 🎮 @benev/tact
3
+ > *web game input library, from keypress to couch co-op*
4
4
 
5
5
  ```ts
6
6
  npm install @benev/tact
7
7
  ```
8
8
 
9
- - 🛰️ **devices** are for sampling user inputs
10
- - 🔗 **bindings** describe how samples influence actions
11
- - 📡 **station** runs device samples through bindings, updating actions
12
- - 🔌 **switchboard** assigns devices to stations (multi-gamepad couch co-op!)
13
- - 🔘 **nubs** is mobile ui virtual gamepad stuff
9
+ - 🛹 **deck** full setup with localstorate persistence
10
+ - 🎮 **controllers** produce user input samples
11
+ - 🧩 **bindings** describe how actions interpret samples
12
+ - 🔌 **port** updates actions by interpreting samples
13
+ - 🛞 **hub** plugs controllers into ports (multi-gamepad couch co-op!)
14
+ - 📱 **nubs** is mobile ui virtual gamepad stuff
15
+
16
+
17
+
18
+ <br/><br/>
19
+
20
+ ## 🍋 tact deck
21
+ > *the full setup*
22
+
23
+ the deck is the heart of tact, tying all the pieces together and putting a bow in it for you.
24
+
25
+ ### 🛹 deck setup
26
+ - **import stuff from tact**
27
+ ```ts
28
+ import * as tact from "@benev/tact"
29
+ ```
30
+ - **setup your deck, and your game's bindings**
31
+ ```ts
32
+ const deck = await tact.Deck.load({
33
+
34
+ // how many players ports are possible? 1 is fine..
35
+ portCount: 4,
36
+
37
+ // where to store the user-customized bindings
38
+ kv: tact.localStorageKv(),
39
+
40
+ // default archetypal bindings for your game
41
+ bindings: {
42
+ ...tact.hubBindings(),
43
+ walking: {
44
+ forward: "KeyW",
45
+ jump: "Space",
46
+ },
47
+ gunning: {
48
+ shoot: ["or", "pointer.button.left", "gamepad.trigger.right"],
49
+ },
50
+ },
51
+ })
52
+ ```
53
+
54
+ ### 🛹 plug controllers into the hub
55
+ - **plug a keyboard/mouse player into the hub**
56
+ ```ts
57
+ deck.hub.plug(
58
+ new tact.GroupController(
59
+ new tact.KeyboardController(),
60
+ new tact.PointerController(),
61
+ new tact.VirtualGamepadController(),
62
+ )
63
+ )
64
+ ```
65
+ - **auto connect/disconnect gamepads as they come and go**
66
+ ```ts
67
+ tact.autoGamepads(deck.hub.plug)
68
+ ```
69
+
70
+ ### 🛹 do your gameplay
71
+ - **start with what modes you want enabled in your game**
72
+ ```ts
73
+ for (const port of deck.ports)
74
+ port.modes.adds("walking", "gunning")
75
+ ```
76
+ - **poll the deck, interrogate actions for your gameplay**
77
+ ```ts
78
+ onEachTickInYourGame(() => {
79
+
80
+ // do your polling
81
+ const [p1, p2, p3, p4] = deck.hub.poll()
82
+
83
+ // check if the first player is pressing "forward" action
84
+ p1.walking.forward.pressed // true
85
+
86
+ // check how hard the second player is pulling that trigger
87
+ p2.gunning.shoot.value // 0.123
88
+ })
89
+ ```
90
+
91
+
14
92
 
15
93
  <br/><br/>
16
94
 
17
- ## 🍋 tact devices
18
- > produces user input "samples"
95
+ ## 🍋 tact controllers
96
+ > *sources of user input "samples"*
19
97
 
20
- ### 🥝 polling is good, actually
98
+ ### 🎮 polling is good, actually
21
99
  - tact operates on the basis of *polling*
22
100
  - *"but polling is bad"* says you — but no — you're wrong — polling is unironically *based,* and you *should* do it
23
101
  - in a game, we want to be processing our inputs *every frame*
24
102
  - the gift of polling is total control over *when* inputs are processed
25
103
  - i will elaborate no further 🗿
26
104
 
27
- ### 🥝 basically how a device works
28
- - make a keyboard device
105
+ ### 🎮 basically how a controller works
106
+ - make a controller
29
107
  ```ts
30
- import {KeyboardDevice} from "@benev/tact"
31
-
32
- const device = new KeyboardDevice(window)
108
+ const keyboard = new tact.KeyboardController()
33
109
  ```
34
110
  - take samples each frame
35
111
  ```ts
36
- const samples = device.takeSamples()
112
+ const samples = keyboard.takeSamples()
37
113
  // [
38
114
  // ["KeyA", 1],
39
115
  // ["Space", 0]
40
116
  // ]
41
117
  ```
42
- - dispose the device when you're done with it
118
+ - some controllers have disposers to call when you're done with them
43
119
  ```ts
44
- device.dispose()
120
+ keyboard.dispose()
45
121
  ```
46
122
 
47
- ### 🥝 samples explained
123
+ ### 🎮 samples explained
48
124
  - a sample is a raw input of type `[code: string, value: number]`
49
125
  - a sample has a `code` string
50
126
  - it's either a [standard keycode](https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_code_values), like `KeyA`
@@ -57,37 +133,21 @@ npm install @benev/tact
57
133
  - sometimes we use numbers greater then `1`, like for dots of pointer movement like in `pointer.move.up`
58
134
  - don't worry about sensitivity, deadzones, values like `0.00001` — actions will account for all that using bindings later on
59
135
 
60
- ### 🥝 sample code modprefixes
61
- - here at tact, we have this nifty `modprefix` convention
62
- - consider a keycode like `KeyA`
63
- - `ctrl-KeyA` means the "ctrl" modifier was held
64
- - `alt-KeyA` means the "alt" modifier was held
65
- - `meta-KeyA` means the "meta" modifier was held (command or windows keys)
66
- - `shift-KeyA` means the "shift" modifier was held
67
- - `ctrl-alt-meta-shift-KeyA` they can stack, but always in this order (the word `cams` helps remind me the valid order)
68
- - `x-KeyA` means *no* modifier was held (exclusive)
69
- - `KeyA` doesn't care if any modifiers were held or not
70
-
71
- ### 🥝 sample code reference
72
- - **KeyboardDevice**
136
+ ### 🎮 sample code reference
137
+ - **KeyboardController**
73
138
  - any [standard keycode](https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_code_values)
74
139
  - `KeyA`
75
140
  - `Space`
76
141
  - `Digit2`
77
142
  - etc
78
- - plus the modprefixes
79
- - `ctrl-KeyA`
80
- - `alt-shift-Space`
81
- - `x-Digit2`
82
- - etc
83
- - **PointerDevice**
84
- - mouse buttons (plus modprefixes)
143
+ - **PointerController**
144
+ - mouse buttons
85
145
  - `pointer.button.left`
86
146
  - `pointer.button.right`
87
147
  - `pointer.button.middle`
88
148
  - `pointer.button.4`
89
149
  - `pointer.button.5`
90
- - mouse wheel (plus modprefixes)
150
+ - mouse wheel
91
151
  - `pointer.wheel.up`
92
152
  - `pointer.wheel.down`
93
153
  - `pointer.wheel.left`
@@ -97,7 +157,7 @@ npm install @benev/tact
97
157
  - `pointer.move.down`
98
158
  - `pointer.move.left`
99
159
  - `pointer.move.right`
100
- - **GamepadDevice**
160
+ - **GamepadController**
101
161
  - gamepad buttons
102
162
  - `gamepad.a`
103
163
  - `gamepad.b`
@@ -126,58 +186,151 @@ npm install @benev/tact
126
186
  - `gamepad.stick.right.left`
127
187
  - `gamepad.stick.right.right`
128
188
 
189
+
190
+
129
191
  <br/><br/>
130
192
 
131
193
  ## 🍋 tact bindings
132
- > keybindings! they describe how actions interpret samples
194
+ > *keybindings! they describe how actions interpret samples*
133
195
 
134
- ### 🥝 bindings example
135
- - let's start with a small example:
196
+ ### 🧩 bindings example
197
+ - **let's start with a small example:**
136
198
  ```ts
137
- import {Bindings} from "@benev/tact"
138
-
139
- const bindings = asBindings({
199
+ const bindings = tact.asBindings({
140
200
  walking: {
141
- forward: [{lenses: [{code: "KeyW"}]}],
142
- jump: [{lenses: [{code: "Space"}]}],
201
+ forward: "KeyW",
202
+ jump: "Space",
143
203
  },
144
204
  gunning: {
145
- shoot: [{lenses: [{code: "pointer.button.left"}]}],
205
+ shoot: ["or", "pointer.button.left", "gamepad.trigger.right"],
146
206
  },
147
207
  })
148
208
  ```
149
209
  - `walking` and `gunning` are **modes**
150
210
  - `forward`, `jump`, and `shoot` are **actions**
151
- - data like `[{lenses: [{code: "KeyW"}]}]` are the rules for triggering the action.
152
- - 🚨 TODO okay these rules are complex and i'm gonna rework them soon
153
- - whole modes can be enabled or disabled
211
+ - note that whole modes can be enabled or disabled during gameplay
212
+
213
+ ### 🧩 bindings are a lispy domain-specific-language
214
+ - **you can do complex stuff**
215
+ ```ts
216
+ ["or",
217
+ "KeyQ",
218
+ ["and",
219
+ "KeyA",
220
+ "KeyD",
221
+ ["not", "KeyS"],
222
+ ],
223
+ ]
224
+ ```
225
+ - press Q, or
226
+ - press A + D, while not pressing S
227
+ - **you can get really weird**
228
+ ```ts
229
+ ["cond",
230
+ ["code", "gamepad.trigger.right", {deadzone: 0.5, timing: ["tap"]}],
231
+ ["and", "gamepad.bumper.left", ["not", "gamepad.trigger.left"]],
232
+ ]
233
+ ```
234
+ - hold LB and tap RT halfway while not holding LT
235
+
236
+ ### 🧩 bindings atom reference
237
+ - **string** — strings are interpreted as "code" atoms with default settings
238
+ - **"code"** — allows you to customize the settings
239
+ ```ts
240
+ ["code", "KeyA", {
241
+ scale: 1,
242
+ invert: false,
243
+ deadzone: 0.2,
244
+ timing: ["direct"],
245
+ }]
246
+ ```
247
+ - defaults shown
248
+ - `scale` is sensitivity, the value gets multiplied by this
249
+ - `invert` will invert a value by subtracting it from 1
250
+ - `deadzone` ignores values below the threshold (and remaps to preserve the range)
251
+ - `timing` lets you specify special timing considerations
252
+ - `["direct"]` ignores timing considerations
253
+ - `["tap", 250]` only fires for taps under 250ms
254
+ - `["hold", 250]` only fires for holds over 250ms
255
+ - **"or"** — resolves to the maximum value
256
+ ```ts
257
+ ["or", "KeyA", "KeyB", "KeyC"]
258
+ ```
259
+ - **"and"** — resolves to the minimum value
260
+ ```ts
261
+ ["and", "KeyA", "KeyB", "KeyC"]
262
+ ```
263
+ - **"not"** — resolves to the opposite effect
264
+ ```ts
265
+ ["not", "KeyA"]
266
+ ```
267
+ - **"cond"** — conditional situation (example for modifiers shown)
268
+ ```ts
269
+ ["cond", "KeyA", ["and",
270
+ ["or", "ControlLeft", "ControlRight"],
271
+ ["not", ["or", "AltLeft", "AltRight"]],
272
+ ["not", ["or", "MetaLeft", "MetaRight"]],
273
+ ["not", ["or", "ShiftLeft", "ShiftRight"]],
274
+ ]]
275
+ ```
276
+ - KeyA is the value that gets used
277
+ - but only if the following condition passes
278
+ - **"mods"** — macro for modifiers
279
+ ```ts
280
+ ["mods", "KeyA", {ctrl: true}]
281
+ ```
282
+ - equivalent to the "cond" example above
283
+ - `ctrl`, `alt`, `meta`, `shift` are available
284
+
285
+
154
286
 
155
287
  <br/><br/>
156
288
 
157
- ## 🍋 tact station
158
- > polling gives you actions
289
+ ## 🍋 tact port
290
+ > *polling gives you "actions"*
159
291
 
160
- ### 🥝 creating a station
161
- - create a station
162
- ```ts
163
- import {Station} from "@benev/tact"
292
+ a port represents a single playable port, and you poll it each frame to resolve actions for you to read.
164
293
 
165
- const station = new Station(bindings)
166
- .addModes("walking")
167
- .addDevices(
168
- new KeyboardDevice(window),
169
- new PointerDevice(window),
170
- )
294
+ ### 🔌 port basics
295
+ - **make a port**
296
+ ```ts
297
+ const port = new tact.Port(bindings)
298
+ ```
299
+ - **attach some controllers to the port**
300
+ ```ts
301
+ port.controllers
302
+ .add(new tact.KeyboardController())
303
+ .add(new tact.PointerController())
304
+ .add(new tact.VirtualGamepadController())
305
+ ```
306
+ - you can add/delete controllers from the set any time
307
+ - **don't forget to enable modes!**
308
+ ```ts
309
+ port.modes.add("walking")
310
+ ```
311
+ - if you don't enable any modes, no actions will happen
312
+ - actions only happen for enabled modes
313
+ - you can toggle modes on and off by adding/deleting them from the modes set
314
+ - **you can update the bindings any time**
315
+ ```ts
316
+ port.bindings = freshBindings
317
+ ```
318
+ - **wire up gamepad auto connect/disconnect**
319
+ ```ts
320
+ tact.autoGamepads(controller => {
321
+ port.controllers.add(controller)
322
+ return () => port.controllers.delete(controller)
323
+ })
171
324
  ```
172
325
 
173
- ### 🥝 how to use actions
174
- - poll the station every frame
326
+ ### 🔌 interrogating actions
327
+ - **poll the port every frame**
175
328
  ```ts
176
- station.poll(Date.now())
329
+ const actions = port.poll()
177
330
  ```
178
- - now you use the `actions`
331
+ - **now you can inspect the `actions`**
179
332
  ```ts
180
- station.actions.walking.forward.value // 1
333
+ actions.walking.forward.value // 1
181
334
  ```
182
335
  - `walking` is a `mode`
183
336
  - `forward` is an `action`
@@ -187,113 +340,85 @@ npm install @benev/tact
187
340
  - `action.pressed` — true if the value > 0
188
341
  - `action.down` — true for one frame when the key goes from up to down
189
342
  - `action.up` — true for one frame when the key goes from down to up
190
- - `action.on(action => {})` — react to changes ([`@e280/stz`](https://github.com/e280/stz) sub fn)
191
- - `action.onDown(action => {})` — react only on down ([`@e280/stz`](https://github.com/e280/stz) sub fn)
192
343
 
193
- ### 🥝 more about station
194
- - you can enable/disable modes like this (it's a set)
195
- ```ts
196
- station.modes.add("gunning")
197
- // now the "gunning" actions can fire
198
344
 
199
- station.modes.delete("walking")
200
- // now the "walking" actions *cannot* fire
201
- ```
202
- - you can add/remove devices from the set any time
203
- ```ts
204
- station.devices.add(new GamepadDevice(pad))
205
- ```
206
- - you can update the bindings at runtime
207
- ```ts
208
- station.bindings = bindings2
209
- ```
210
345
 
211
346
  <br/><br/>
212
347
 
213
- ## 🍋 tact switchboard
214
- > multiple gamepads! couch co-op is so back
348
+ ## 🍋 tact hub
349
+ > *multiple gamepads! couch co-op is so back*
215
350
 
216
- ### 🥝 feel the vibes
217
- - you know the way old timey game consoles had controller ports? and then players could plug their controller into whatever port they wanted? and then you could unplug and replug your controller into a different port? yeah — that's what switchboard does.
218
- - with the switchboard, think of the "stations" as controller ports, and the "devices" as player controllers.
219
- - import stuff:
220
- ```ts
221
- import {
222
- Switchboard, Station,
223
- GroupDevice, GamepadDevice, KeyboardDevice, PointerDevice,
224
- } from "@benev/tact"
225
- ```
351
+ you know the way old-timey game consoles had four controller ports on the front?
352
+
353
+ the hub embraces that analogy, helping you coordinate the plugging and unplugging of virtual controllers into its virtual ports.
226
354
 
227
- ### 🥝 create a switchboard with stations
228
- - **adopt standard switchboard bindings**
355
+ ### 🛞 create a hub with ports
356
+ - **adopt standard hub bindings**
229
357
  ```ts
230
- // transform your game's bindings into switchboard-friendly bindings
231
- const sBindings = Switchboard.bindings(bindings)
358
+ const hubBindings = {
359
+ ...yourBindings,
360
+
361
+ // mixin standard hub bindings
362
+ ...tact.hubBindings(),
363
+ }
232
364
  ```
233
- - this allows players to shimmy what station their controller controls
234
- - gamepad: hold middle button and press bumpers
365
+ - hub bindings let players shimmy what port their controller is plugged into
366
+ - gamepad: hold gamma (middle button) and press bumpers
235
367
  - keyboard: left bracket or right bracket
236
- - **make switchboard with stations at the ready**
368
+ - **make hub with multiple ports at the ready**
237
369
  ```ts
238
- const switchboard = new Switchboard([
239
- new Station(sBindings),
240
- new Station(sBindings),
241
- new Station(sBindings),
242
- new Station(sBindings),
370
+ const hub = new tact.Hub([
371
+ new tact.Port(hubBindings),
372
+ new tact.Port(hubBindings),
373
+ new tact.Port(hubBindings),
374
+ new tact.Port(hubBindings),
243
375
  ])
244
376
  ```
245
- - yes that's right — each player can have their own bindings 🤯
377
+ - yes that's right — each player port gets its own bindings 🤯
246
378
 
247
- ### 🥝 connect player's devices to the switchboard
248
- - **let's connect the keyboard/mouse player**
379
+ ### 🛞 plug in some controllers
380
+ - **let's plug in the keyboard/mouse player**
249
381
  ```ts
250
- switchboard.connect(
251
- new GroupDevice(
252
- new KeyboardDevice(window),
253
- new PointerDevice(window),
382
+ hub.plug(
383
+ new tact.GroupController(
384
+ new tact.KeyboardController(),
385
+ new tact.PointerController(),
386
+ new tact.VirtualGamepadController(),
254
387
  )
255
388
  )
256
389
  ```
257
- - the switchboard assumes a single device represents a single player, thus we can use a `GroupDevice` to combine multple devices into one
390
+ - the hub requires a single controller to represent a player, thus we can use a `GroupController` to combine multple controllers into one
258
391
  - **wire up gamepad auto connect/disconnect**
259
392
  ```ts
260
- GamepadDevice.on(device => switchboard.connect(device))
393
+ tact.autoGamepads(hub.plug)
261
394
  ```
262
395
 
263
- ### 🥝 you're ready!
396
+ ### 🛞 it's gaming time
264
397
  - **do your polling, interrogate those actions**
265
398
  ```ts
266
- const [p1, p2, p3, p4] = switchboard.stations
267
-
268
- // poll them all
269
- switchboard.poll(Date.now())
399
+ const [p1, p2, p3, p4] = hub.poll()
270
400
 
271
- p1.actions.walking.jump.value // 1
272
- p2.actions.walking.jump.value // 0
401
+ p1.walking.jump.value // 1
402
+ p2.walking.jump.value // 0
273
403
  ```
274
404
 
275
- ### 🥝 the more you know, about the switchboard
276
- - `switchboard.stations` — direct access to the array of stations
277
- - `switchboard.isActive(p1)` — check if there's a switchboard-connected-device assigned to this station
278
- - `switchboard.shimmy(device, 1)` — shimmy a device forward one station
279
- - `switchboard.shimmy(device, -1)` — shimmy a device backward one station
280
- - `switchboard.connect(device, p4)` — connect-or-reassign a device to a specific station
281
- - `switchboard.connect(device)` — connect a device to the next unassigned station (or the last station)
282
- - `switchboard.disconnect(device)` — unassign a device and forget about it
405
+
283
406
 
284
407
  <br/><br/>
285
408
 
286
409
  ## 🍋 tact nubs
287
- > mobile ui like virtual thumbsticks and buttons
410
+ > *mobile ui like virtual thumbsticks and buttons*
288
411
 
289
- ### 🥝 nub stick
412
+ ### 📱 nub stick
290
413
  > TODO lol need to write docs
291
414
 
292
- ### 🥝 nub virtual gamepad
415
+ ### 📱 nub virtual gamepad
293
416
  > TODO lol need to write docs
294
417
 
418
+
419
+
295
420
  <br/><br/>
296
421
 
297
422
  ## 🍋 tact is by https://benevolent.games/
298
- > building the future of web games
423
+ > *building the future of web games*
299
424
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@benev/tact",
3
- "version": "0.1.0-1",
3
+ "version": "0.1.0-3",
4
4
  "description": "keybindings and gamepad support for web games",
5
5
  "license": "MIT",
6
6
  "author": "Chase Moskal <chasemoskal@gmail.com>",
@@ -8,7 +8,8 @@
8
8
  "main": "./x/index.js",
9
9
  "sideEffects": false,
10
10
  "exports": {
11
- ".": "./x/index.js"
11
+ ".": "./x/index.js",
12
+ "./core": "./x/core/index.js"
12
13
  },
13
14
  "files": [
14
15
  "x",
@@ -32,9 +33,10 @@
32
33
  },
33
34
  "dependencies": {
34
35
  "@benev/math": "^0.2.0-1",
36
+ "@e280/kv": "^0.1.0",
35
37
  "@e280/sly": "^0.1.1",
36
38
  "@e280/strata": "^0.1.0",
37
- "@e280/stz": "^0.2.0-4"
39
+ "@e280/stz": "^0.2.0-5"
38
40
  },
39
41
  "devDependencies": {
40
42
  "@e280/science": "^0.1.1",
@@ -1,21 +1,15 @@
1
1
 
2
- import {sub} from "@e280/stz"
3
- import {isPressed} from "../utils/is-pressed.js"
2
+ import {isPressed} from "./parts/is-pressed.js"
4
3
 
5
4
  export class Action {
6
5
  #value = 0
7
6
  #previous = 0
8
7
 
9
- static updateValue(action: Action, newValue: number) {
8
+ static update(action: Action, value: number) {
10
9
  action.#previous = action.#value
11
- action.#value = newValue
12
- if (action.changed) action.on.pub(action)
13
- if (action.down) action.onDown.pub(action)
10
+ action.#value = value
14
11
  }
15
12
 
16
- readonly on = sub<[Action]>()
17
- readonly onDown = sub<[Action]>()
18
-
19
13
  get value() { return this.#value }
20
14
  get previous() { return this.#previous }
21
15
  get changed() { return this.#value !== this.#previous }
@@ -0,0 +1,29 @@
1
+
2
+ import {Code, CodeSettings, CodeState} from "../types.js"
3
+
4
+ export const defaultHoldTime = 250
5
+
6
+ export function defaultCodeState([,,settings]: Code): CodeState {
7
+ return {
8
+ lastValue: 0,
9
+ holdStart: 0,
10
+ settings: defaultifyCodeSettings(settings),
11
+ }
12
+ }
13
+
14
+ function defaultCodeSettings(): CodeSettings {
15
+ return {
16
+ scale: 1,
17
+ invert: false,
18
+ deadzone: 0.2,
19
+ timing: ["direct"],
20
+ }
21
+ }
22
+
23
+ function defaultifyCodeSettings(partial: Partial<CodeSettings> = {}): CodeSettings {
24
+ return {
25
+ ...defaultCodeSettings(),
26
+ ...partial,
27
+ }
28
+ }
29
+