@antha/input 0.17.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,62 @@
1
+ import { type AnthaReadRawInputModState } from '../raw-inputs/antha-read-raw-input.mod.js';
2
+ import { type MenuNavModState } from './antha-menu-nav.mod.js';
3
+ /**
4
+ * Menu navigation state whose return path lists parent menus from the outermost to the nearest.
5
+ *
6
+ * @category Internal
7
+ */
8
+ export type AnthaMenuState<MenuKey extends string = string> = {
9
+ activeMenu: MenuKey | undefined;
10
+ returnTo: MenuKey[];
11
+ };
12
+ /**
13
+ * State consumed or updated by {@link createAnthaMenuStateMod}.
14
+ *
15
+ * @category Internal
16
+ */
17
+ export type AnthaMenuStateModState<MenuKey extends string = string> = MenuNavModState & AnthaReadRawInputModState & {
18
+ menuState: AnthaMenuState<MenuKey> | undefined;
19
+ };
20
+ /**
21
+ * Returns to the nearest parent menu and removes it from the path, or closes menu mode when the
22
+ * path is empty.
23
+ *
24
+ * @category Internal
25
+ */
26
+ export declare function getAnthaMenuReturnState<MenuKey extends string>(currentState: Readonly<AnthaMenuState<MenuKey>> | undefined): AnthaMenuState<MenuKey>;
27
+ /**
28
+ * Resolves pause and back inputs into a menu-state transition.
29
+ *
30
+ * @category Internal
31
+ */
32
+ export declare function getAnthaMenuStateForNavigation<MenuKey extends string>({ menuExitWasTriggered, menuState, openPauseMenuWasTriggered, pauseMenu, }: Readonly<{
33
+ menuExitWasTriggered: boolean;
34
+ menuState: Readonly<AnthaMenuState<MenuKey>> | undefined;
35
+ openPauseMenuWasTriggered: boolean;
36
+ pauseMenu: MenuKey;
37
+ }>): AnthaMenuState<MenuKey> | undefined;
38
+ /**
39
+ * Switches the raw-input consumer and menu-navigation state on pause/back inputs. Add this before
40
+ * `createAnthaMenuNavMod` so navigation sees the updated `isInMenu` value.
41
+ *
42
+ * @category Pre-Built Mods
43
+ */
44
+ export declare function createAnthaMenuStateMod<MenuKey extends string = string, InputConsumer extends string = string>({ gameInputConsumerName, menuInputConsumerName, pauseMenuKey, }: Readonly<{
45
+ /**
46
+ * The name assigned to inputs consumed by the game so that they don't get consumed by menus at
47
+ * the same time.
48
+ */
49
+ gameInputConsumerName: NoInfer<InputConsumer>;
50
+ /**
51
+ * The name assigned to inputs consumed by menus so that they don't get consumed by the game at
52
+ * the same time.
53
+ */
54
+ menuInputConsumerName: NoInfer<InputConsumer>;
55
+ pauseMenuKey: NoInfer<MenuKey>;
56
+ }>): import("@antha/engine").AnthaMod<NoInfer<AnthaMenuStateModState<NoInfer<MenuKey>>>>;
57
+ /**
58
+ * The mod created by {@link createAnthaMenuStateMod}.
59
+ *
60
+ * @category Internal
61
+ */
62
+ export type AnthaMenuStateMod = ReturnType<typeof createAnthaMenuStateMod>;
@@ -0,0 +1,86 @@
1
+ import { defineAnthaMod } from '@antha/engine';
2
+ import { check } from '@augment-vir/assert';
3
+ import { getObjectTypedEntries } from '@augment-vir/common';
4
+ import { isPlayerMenuNavigationAllowed, MenuNavBinding, } from './antha-menu-nav.mod.js';
5
+ /**
6
+ * Returns to the nearest parent menu and removes it from the path, or closes menu mode when the
7
+ * path is empty.
8
+ *
9
+ * @category Internal
10
+ */
11
+ export function getAnthaMenuReturnState(currentState) {
12
+ return {
13
+ activeMenu: currentState?.returnTo.at(-1),
14
+ returnTo: currentState?.returnTo.slice(0, -1) || [],
15
+ };
16
+ }
17
+ /**
18
+ * Resolves pause and back inputs into a menu-state transition.
19
+ *
20
+ * @category Internal
21
+ */
22
+ export function getAnthaMenuStateForNavigation({ menuExitWasTriggered, menuState, openPauseMenuWasTriggered, pauseMenu, }) {
23
+ const activeMenu = menuState?.activeMenu;
24
+ if (!check.isDefined(activeMenu)) {
25
+ return openPauseMenuWasTriggered
26
+ ? {
27
+ activeMenu: pauseMenu,
28
+ returnTo: [],
29
+ }
30
+ : undefined;
31
+ }
32
+ return openPauseMenuWasTriggered || menuExitWasTriggered
33
+ ? getAnthaMenuReturnState(menuState)
34
+ : undefined;
35
+ }
36
+ /**
37
+ * Switches the raw-input consumer and menu-navigation state on pause/back inputs. Add this before
38
+ * `createAnthaMenuNavMod` so navigation sees the updated `isInMenu` value.
39
+ *
40
+ * @category Pre-Built Mods
41
+ */
42
+ export function createAnthaMenuStateMod({ gameInputConsumerName, menuInputConsumerName, pauseMenuKey, }) {
43
+ return defineAnthaMod({
44
+ modName: 'antha-menu-state',
45
+ execute({ state }) {
46
+ const nextMenuState = state.activeBindings
47
+ ? getObjectTypedEntries(state.activeBindings)
48
+ .map(([playerPosition, playerActiveBindings,]) => {
49
+ if (!isPlayerMenuNavigationAllowed({
50
+ allowedPlayerMenuNavigation: state.allowedPlayerMenuNavigation,
51
+ playerPosition,
52
+ })) {
53
+ return undefined;
54
+ }
55
+ const openPauseMenuBinding = playerActiveBindings[MenuNavBinding.OpenPauseMenu];
56
+ const menuExitBinding = playerActiveBindings[MenuNavBinding.MenuExit];
57
+ const nextMenuState = getAnthaMenuStateForNavigation({
58
+ menuExitWasTriggered: !!menuExitBinding && !menuExitBinding.actCount,
59
+ menuState: state.menuState,
60
+ openPauseMenuWasTriggered: !!openPauseMenuBinding && !openPauseMenuBinding.actCount,
61
+ pauseMenu: pauseMenuKey,
62
+ });
63
+ if (!nextMenuState) {
64
+ return undefined;
65
+ }
66
+ [
67
+ openPauseMenuBinding,
68
+ menuExitBinding,
69
+ ].forEach((menuBinding) => {
70
+ if (menuBinding && !menuBinding.actCount) {
71
+ menuBinding.actCount = 1;
72
+ menuBinding.lastActDuration = menuBinding.holdDuration;
73
+ }
74
+ });
75
+ return nextMenuState;
76
+ })
77
+ .find(check.isDefined)
78
+ : undefined;
79
+ if (nextMenuState) {
80
+ state.menuState = nextMenuState;
81
+ }
82
+ state.isInMenu = check.isDefined(state.menuState?.activeMenu);
83
+ state.rawInputConsumer = state.isInMenu ? menuInputConsumerName : gameInputConsumerName;
84
+ },
85
+ });
86
+ }
@@ -0,0 +1,40 @@
1
+ import { type ActiveBindings } from './player-bindings.js';
2
+ /**
3
+ * Names the bindings that control a two-dimensional direction.
4
+ *
5
+ * @category Internal
6
+ */
7
+ export type DirectionalBindingNames<BindingName extends string> = {
8
+ down: BindingName;
9
+ left: BindingName;
10
+ right: BindingName;
11
+ up: BindingName;
12
+ };
13
+ /** @category Internal */
14
+ export type DirectionalInput = {
15
+ durationMs: number;
16
+ value: number;
17
+ };
18
+ /** @category Internal */
19
+ export declare function getDirectionalInput<BindingName extends string>({ activeBindings, bindingName, }: Readonly<{
20
+ activeBindings: ActiveBindings<BindingName> | undefined;
21
+ bindingName: BindingName;
22
+ }>): DirectionalInput;
23
+ /** @category Internal */
24
+ export declare function getAxisValue({ negative, positive, }: Readonly<{
25
+ negative: DirectionalInput;
26
+ positive: DirectionalInput;
27
+ }>): number;
28
+ /**
29
+ * Converts directional bindings into a normalized vector and favors the newest opposing input.
30
+ * Useful for determining player input from binding assignments.
31
+ *
32
+ * @category Util
33
+ */
34
+ export declare function getDirectionalInputVector<BindingName extends string>({ activeBindings, bindingNames, }: Readonly<{
35
+ activeBindings: ActiveBindings<BindingName> | undefined;
36
+ bindingNames: Readonly<DirectionalBindingNames<BindingName>>;
37
+ }>): {
38
+ x: number;
39
+ y: number;
40
+ } | undefined;
@@ -0,0 +1,52 @@
1
+ /** @category Internal */
2
+ export function getDirectionalInput({ activeBindings, bindingName, }) {
3
+ const activeBinding = activeBindings?.[bindingName];
4
+ return {
5
+ durationMs: activeBinding?.holdDuration.milliseconds ?? Infinity,
6
+ value: activeBinding?.value || 0,
7
+ };
8
+ }
9
+ /** @category Internal */
10
+ export function getAxisValue({ negative, positive, }) {
11
+ return negative.value && negative.durationMs < positive.durationMs
12
+ ? -negative.value
13
+ : positive.value && positive.durationMs < negative.durationMs
14
+ ? positive.value
15
+ : 0;
16
+ }
17
+ /**
18
+ * Converts directional bindings into a normalized vector and favors the newest opposing input.
19
+ * Useful for determining player input from binding assignments.
20
+ *
21
+ * @category Util
22
+ */
23
+ export function getDirectionalInputVector({ activeBindings, bindingNames, }) {
24
+ const movementX = getAxisValue({
25
+ negative: getDirectionalInput({
26
+ activeBindings,
27
+ bindingName: bindingNames.left,
28
+ }),
29
+ positive: getDirectionalInput({
30
+ activeBindings,
31
+ bindingName: bindingNames.right,
32
+ }),
33
+ });
34
+ const movementY = getAxisValue({
35
+ negative: getDirectionalInput({
36
+ activeBindings,
37
+ bindingName: bindingNames.up,
38
+ }),
39
+ positive: getDirectionalInput({
40
+ activeBindings,
41
+ bindingName: bindingNames.down,
42
+ }),
43
+ });
44
+ const magnitude = Math.hypot(movementX, movementY);
45
+ if (!magnitude) {
46
+ return undefined;
47
+ }
48
+ return {
49
+ x: (movementX / magnitude) * Math.min(magnitude, 1),
50
+ y: (movementY / magnitude) * Math.min(magnitude, 1),
51
+ };
52
+ }
@@ -0,0 +1,18 @@
1
+ import { type GamepadInputDeviceKey } from 'input-device-handler';
2
+ import { type MenuNavBinding } from './antha-menu-nav.mod.js';
3
+ import { type DirectionalBindingNames } from './directional-input.js';
4
+ import { type PlayerPosition, type PlayersBindingAssignments } from './player-bindings.js';
5
+ /**
6
+ * Builds per-slot gamepad movement and menu bindings. Keyboard controls default to slot `'1'`; set
7
+ * `keyboardPlayerPosition` to `undefined` to disable them.
8
+ *
9
+ * @category Util
10
+ */
11
+ export declare function createDefaultLocalPlayerBindings<BindingName extends string>({ directionalBindingNames, playerGamepads, ...keyboardOptions }: Readonly<{
12
+ directionalBindingNames: Readonly<DirectionalBindingNames<BindingName>>;
13
+ keyboardPlayerPosition?: PlayerPosition | undefined;
14
+ playerGamepads: ReadonlyArray<Readonly<{
15
+ playerPosition: PlayerPosition;
16
+ gamepadDeviceKey: GamepadInputDeviceKey;
17
+ }>>;
18
+ }>): PlayersBindingAssignments<MenuNavBinding | BindingName>;
@@ -0,0 +1,185 @@
1
+ import { KnownInput } from '@antha/gamepad-type';
2
+ import { arrayToObject, mapObjectValues } from '@augment-vir/common';
3
+ import { InputDirection } from '../raw-inputs/raw-input.js';
4
+ import { defaultMenuNavBindings } from './antha-menu-nav.mod.js';
5
+ import { AnyGamepad, } from './player-bindings.js';
6
+ const keyboardDirectionalBindings = {
7
+ down: [
8
+ {
9
+ deviceKey: 'keyboard',
10
+ direction: InputDirection.Positive,
11
+ inputName: 'button-KeyS',
12
+ },
13
+ {
14
+ deviceKey: 'keyboard',
15
+ direction: InputDirection.Positive,
16
+ inputName: 'button-ArrowDown',
17
+ },
18
+ ],
19
+ left: [
20
+ {
21
+ deviceKey: 'keyboard',
22
+ direction: InputDirection.Positive,
23
+ inputName: 'button-KeyA',
24
+ },
25
+ {
26
+ deviceKey: 'keyboard',
27
+ direction: InputDirection.Positive,
28
+ inputName: 'button-ArrowLeft',
29
+ },
30
+ ],
31
+ right: [
32
+ {
33
+ deviceKey: 'keyboard',
34
+ direction: InputDirection.Positive,
35
+ inputName: 'button-KeyD',
36
+ },
37
+ {
38
+ deviceKey: 'keyboard',
39
+ direction: InputDirection.Positive,
40
+ inputName: 'button-ArrowRight',
41
+ },
42
+ ],
43
+ up: [
44
+ {
45
+ deviceKey: 'keyboard',
46
+ direction: InputDirection.Positive,
47
+ inputName: 'button-KeyW',
48
+ },
49
+ {
50
+ deviceKey: 'keyboard',
51
+ direction: InputDirection.Positive,
52
+ inputName: 'button-ArrowUp',
53
+ },
54
+ ],
55
+ };
56
+ function createDirectionalBindings({ directionalBindingNames, gamepadDeviceKey, includeKeyboard, }) {
57
+ return arrayToObject([
58
+ {
59
+ bindingName: directionalBindingNames.down,
60
+ gamepadBindings: [
61
+ {
62
+ direction: InputDirection.Positive,
63
+ inputName: KnownInput.DPadDown,
64
+ },
65
+ {
66
+ direction: InputDirection.Positive,
67
+ inputName: KnownInput.LeftStickY,
68
+ },
69
+ {
70
+ direction: InputDirection.Positive,
71
+ inputName: KnownInput.RightStickY,
72
+ },
73
+ ],
74
+ keyboardBindings: keyboardDirectionalBindings.down,
75
+ },
76
+ {
77
+ bindingName: directionalBindingNames.left,
78
+ gamepadBindings: [
79
+ {
80
+ direction: InputDirection.Positive,
81
+ inputName: KnownInput.DPadLeft,
82
+ },
83
+ {
84
+ direction: InputDirection.Negative,
85
+ inputName: KnownInput.LeftStickX,
86
+ },
87
+ {
88
+ direction: InputDirection.Negative,
89
+ inputName: KnownInput.RightStickX,
90
+ },
91
+ ],
92
+ keyboardBindings: keyboardDirectionalBindings.left,
93
+ },
94
+ {
95
+ bindingName: directionalBindingNames.right,
96
+ gamepadBindings: [
97
+ {
98
+ direction: InputDirection.Positive,
99
+ inputName: KnownInput.DPadRight,
100
+ },
101
+ {
102
+ direction: InputDirection.Positive,
103
+ inputName: KnownInput.LeftStickX,
104
+ },
105
+ {
106
+ direction: InputDirection.Positive,
107
+ inputName: KnownInput.RightStickX,
108
+ },
109
+ ],
110
+ keyboardBindings: keyboardDirectionalBindings.right,
111
+ },
112
+ {
113
+ bindingName: directionalBindingNames.up,
114
+ gamepadBindings: [
115
+ {
116
+ direction: InputDirection.Positive,
117
+ inputName: KnownInput.DPadUp,
118
+ },
119
+ {
120
+ direction: InputDirection.Negative,
121
+ inputName: KnownInput.LeftStickY,
122
+ },
123
+ {
124
+ direction: InputDirection.Negative,
125
+ inputName: KnownInput.RightStickY,
126
+ },
127
+ ],
128
+ keyboardBindings: keyboardDirectionalBindings.up,
129
+ },
130
+ ], ({ bindingName, gamepadBindings, keyboardBindings }) => {
131
+ return {
132
+ key: bindingName,
133
+ value: [
134
+ ...gamepadBindings.map((gamepadBinding) => {
135
+ return {
136
+ ...gamepadBinding,
137
+ deviceKey: gamepadDeviceKey,
138
+ };
139
+ }),
140
+ ...(includeKeyboard ? keyboardBindings : []),
141
+ ],
142
+ };
143
+ });
144
+ }
145
+ function createMenuNavBindings({ gamepadDeviceKey, includeKeyboard, }) {
146
+ return mapObjectValues(defaultMenuNavBindings, (_bindingName, assignments) => {
147
+ return assignments.flatMap((assignment) => {
148
+ if (assignment.deviceKey === AnyGamepad) {
149
+ return [
150
+ {
151
+ ...assignment,
152
+ deviceKey: gamepadDeviceKey,
153
+ },
154
+ ];
155
+ }
156
+ return includeKeyboard ? [assignment] : [];
157
+ });
158
+ });
159
+ }
160
+ /**
161
+ * Builds per-slot gamepad movement and menu bindings. Keyboard controls default to slot `'1'`; set
162
+ * `keyboardPlayerPosition` to `undefined` to disable them.
163
+ *
164
+ * @category Util
165
+ */
166
+ export function createDefaultLocalPlayerBindings({ directionalBindingNames, playerGamepads, ...keyboardOptions }) {
167
+ const keyboardPlayerPosition = 'keyboardPlayerPosition' in keyboardOptions ? keyboardOptions.keyboardPlayerPosition : '1';
168
+ return playerGamepads.reduce((playerBindingAssignments, { gamepadDeviceKey, playerPosition }) => {
169
+ const includeKeyboard = playerPosition === keyboardPlayerPosition;
170
+ return {
171
+ ...playerBindingAssignments,
172
+ [playerPosition]: {
173
+ ...createDirectionalBindings({
174
+ directionalBindingNames,
175
+ gamepadDeviceKey,
176
+ includeKeyboard,
177
+ }),
178
+ ...createMenuNavBindings({
179
+ gamepadDeviceKey,
180
+ includeKeyboard,
181
+ }),
182
+ },
183
+ };
184
+ }, {});
185
+ }
@@ -55,7 +55,7 @@ export type PlayerPosition = `${number}`;
55
55
  * @category Internal
56
56
  */
57
57
  export declare const bindingAssignmentShape: import("object-shape-tester").Shape<{
58
- deviceKey: import("object-shape-tester").Shape<import("@sinclair/typebox").TUnion<(import("@sinclair/typebox").TLiteral<"0"> | import("@sinclair/typebox").TLiteral<"1"> | import("@sinclair/typebox").TLiteral<"2"> | import("@sinclair/typebox").TLiteral<"3"> | import("@sinclair/typebox").TLiteral<"mouse"> | import("@sinclair/typebox").TLiteral<"keyboard"> | import("@sinclair/typebox").TLiteral<"any-gamepad">)[]>>;
58
+ deviceKey: import("object-shape-tester").Shape<import("@sinclair/typebox").TUnion<(import("@sinclair/typebox").TLiteral<"0"> | import("@sinclair/typebox").TLiteral<"1"> | import("@sinclair/typebox").TLiteral<"2"> | import("@sinclair/typebox").TLiteral<"3"> | import("@sinclair/typebox").TLiteral<"keyboard"> | import("@sinclair/typebox").TLiteral<"mouse"> | import("@sinclair/typebox").TLiteral<"any-gamepad">)[]>>;
59
59
  direction: import("object-shape-tester").Shape<import("@sinclair/typebox").TUnion<(import("@sinclair/typebox").TLiteral<InputDirection.Flat> | import("@sinclair/typebox").TLiteral<InputDirection.Negative> | import("@sinclair/typebox").TLiteral<InputDirection.Positive>)[]>>;
60
60
  gamepadBrand: import("object-shape-tester").Shape<import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnsafe<string>>>;
61
61
  inputName: import("object-shape-tester").Shape<import("@sinclair/typebox").TUnsafe<string>>;
@@ -67,7 +67,7 @@ export declare const bindingAssignmentShape: import("object-shape-tester").Shape
67
67
  */
68
68
  export declare const playersBindingAssignmentsShape: import("object-shape-tester").Shape<import("@sinclair/typebox").TUnsafe<Partial<Record<`${number}`, Partial<Record<string, {
69
69
  gamepadBrand?: string;
70
- deviceKey: "0" | "1" | "2" | "3" | "mouse" | "keyboard" | "any-gamepad";
70
+ deviceKey: "0" | "1" | "2" | "3" | "keyboard" | "mouse" | "any-gamepad";
71
71
  inputName: string;
72
72
  direction: InputDirection;
73
73
  }[]>>>>>>;
package/dist/index.d.ts CHANGED
@@ -1,11 +1,14 @@
1
+ export * from '@antha/gamepad-type';
2
+ export * from 'input-device-handler';
1
3
  export * from './bindings/antha-active-bindings-debug.element.js';
2
4
  export * from './bindings/antha-binding-assignments-debug.element.js';
3
5
  export * from './bindings/antha-input-bindings.mod.js';
4
6
  export * from './bindings/antha-menu-nav.mod.js';
7
+ export * from './bindings/antha-menu-state.mod.js';
8
+ export * from './bindings/directional-input.js';
9
+ export * from './bindings/local-player-bindings.js';
5
10
  export * from './bindings/player-bindings.js';
6
11
  export * from './elements/antha-keyboard.element.js';
7
12
  export * from './raw-inputs/antha-raw-input-debug.element.js';
8
13
  export * from './raw-inputs/antha-read-raw-input.mod.js';
9
14
  export * from './raw-inputs/raw-input.js';
10
- export * from '@antha/gamepad-type';
11
- export * from 'input-device-handler';
package/dist/index.js CHANGED
@@ -1,11 +1,14 @@
1
+ export * from '@antha/gamepad-type';
2
+ export * from 'input-device-handler';
1
3
  export * from './bindings/antha-active-bindings-debug.element.js';
2
4
  export * from './bindings/antha-binding-assignments-debug.element.js';
3
5
  export * from './bindings/antha-input-bindings.mod.js';
4
6
  export * from './bindings/antha-menu-nav.mod.js';
7
+ export * from './bindings/antha-menu-state.mod.js';
8
+ export * from './bindings/directional-input.js';
9
+ export * from './bindings/local-player-bindings.js';
5
10
  export * from './bindings/player-bindings.js';
6
11
  export * from './elements/antha-keyboard.element.js';
7
12
  export * from './raw-inputs/antha-raw-input-debug.element.js';
8
13
  export * from './raw-inputs/antha-read-raw-input.mod.js';
9
14
  export * from './raw-inputs/raw-input.js';
10
- export * from '@antha/gamepad-type';
11
- export * from 'input-device-handler';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antha/input",
3
- "version": "0.17.0",
3
+ "version": "0.19.0",
4
4
  "description": "An Antha mod for handling inputs.",
5
5
  "keywords": [
6
6
  "vir",
@@ -33,7 +33,6 @@
33
33
  "test:docs": "virmator docs check"
34
34
  },
35
35
  "dependencies": {
36
- "@antha/gamepad-type": "^0.17.0",
37
36
  "@augment-vir/assert": "^32.3.0",
38
37
  "@augment-vir/common": "^32.3.0",
39
38
  "date-vir": "^9.1.1",
@@ -43,6 +42,8 @@
43
42
  "vira": "^32.0.0"
44
43
  },
45
44
  "devDependencies": {
45
+ "@antha/engine": "^0.19.0",
46
+ "@antha/gamepad-type": "^0.19.0",
46
47
  "@augment-vir/test": "^32.3.0",
47
48
  "@web/dev-server-esbuild": "^2.0.0",
48
49
  "@web/test-runner": "^1.0.0",
@@ -53,7 +54,8 @@
53
54
  "typedoc": "^0.28.20"
54
55
  },
55
56
  "peerDependencies": {
56
- "@antha/engine": "^0.17.0",
57
+ "@antha/engine": "^0.19.0",
58
+ "@antha/gamepad-type": "^0.19.0",
57
59
  "element-vir": "^27"
58
60
  },
59
61
  "engines": {