@antha/input 0.8.0 → 0.10.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.
- package/dist/bindings/antha-input-bindings.mod.js +2 -0
- package/dist/bindings/antha-menu-nav.mod.d.ts +15 -1
- package/dist/bindings/antha-menu-nav.mod.js +27 -5
- package/dist/bindings/player-bindings.d.ts +26 -2
- package/dist/bindings/player-bindings.js +31 -0
- package/dist/raw-inputs/antha-read-raw-input.mod.d.ts +4 -0
- package/dist/raw-inputs/antha-read-raw-input.mod.js +17 -8
- package/dist/raw-inputs/raw-input.d.ts +4 -0
- package/package.json +3 -3
|
@@ -65,6 +65,7 @@ export function createAnthaInputBindingsMod(options = {}) {
|
|
|
65
65
|
const gamepadInputDeviceKeys = getEnumValues(GamepadInputDeviceKey);
|
|
66
66
|
function filterRawInput(entry, binding) {
|
|
67
67
|
return (entry?.direction === binding.direction &&
|
|
68
|
+
!entry.isIgnoredByConsumer &&
|
|
68
69
|
(binding.gamepadBrand ? binding.gamepadBrand === entry.mapped.gamepadBrand : true));
|
|
69
70
|
}
|
|
70
71
|
function readPlayerBindings({ bindingsMap, activeBindingsMap, rawInputs, msSinceLastExecute, gamepadKeyMap, }) {
|
|
@@ -103,6 +104,7 @@ function readPlayerBindings({ bindingsMap, activeBindingsMap, rawInputs, msSince
|
|
|
103
104
|
holdDuration: {
|
|
104
105
|
milliseconds: Math.round(durationMs),
|
|
105
106
|
},
|
|
107
|
+
rawInputs: matchingInputs,
|
|
106
108
|
value,
|
|
107
109
|
actCount: previousActiveBinding?.actCount || 0,
|
|
108
110
|
lastActDuration: previousActiveBinding?.lastActDuration || {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type AnyDuration } from 'date-vir';
|
|
2
2
|
import { NavController, type NavControllerOptions } from 'device-navigation';
|
|
3
|
-
import { type BindingAssignments, type PlayersActiveBindings } from './player-bindings.js';
|
|
3
|
+
import { type BindingAssignments, type PlayerPosition, type PlayersActiveBindings } from './player-bindings.js';
|
|
4
4
|
export { nav, navAttribute, NavController } from 'device-navigation';
|
|
5
5
|
/**
|
|
6
6
|
* All supported menu navigation bindings. To ignore any, simply don't allow players to bind to
|
|
@@ -87,6 +87,11 @@ export type MenuNavOptions = Readonly<Partial<{
|
|
|
87
87
|
export type MenuNavModState = {
|
|
88
88
|
/** Set to true to enable menu navigation. */
|
|
89
89
|
isInMenu: boolean;
|
|
90
|
+
/**
|
|
91
|
+
* When defined, only players explicitly set to true may use menu navigation. Omit this or set
|
|
92
|
+
* to `undefined` to allow every player to run menu navigation.
|
|
93
|
+
*/
|
|
94
|
+
allowedPlayerMenuNavigation: Partial<Record<PlayerPosition, boolean>> | undefined;
|
|
90
95
|
/** Omit or set to `undefined` to disable menu nav. */
|
|
91
96
|
menuNavOptions: Required<MenuNavOptions> | undefined;
|
|
92
97
|
/** All active bindings for all players. */
|
|
@@ -102,6 +107,15 @@ export declare const defaultMenuNavOptions: Required<MenuNavOptions>;
|
|
|
102
107
|
* @category Pre-Built Mods
|
|
103
108
|
*/
|
|
104
109
|
export declare function createAnthaMenuNavMod(options?: Readonly<MenuNavOptions & NavControllerOptions>): import("@antha/engine").AnthaMod<NoInfer<MenuNavModState>>;
|
|
110
|
+
/**
|
|
111
|
+
* Checks whether the given player may use menu navigation under the current allowlist.
|
|
112
|
+
*
|
|
113
|
+
* @category Internal
|
|
114
|
+
*/
|
|
115
|
+
export declare function isPlayerMenuNavigationAllowed({ allowedPlayerMenuNavigation, playerPosition, }: Readonly<{
|
|
116
|
+
allowedPlayerMenuNavigation: MenuNavModState['allowedPlayerMenuNavigation'];
|
|
117
|
+
playerPosition: PlayerPosition;
|
|
118
|
+
}>): boolean;
|
|
105
119
|
/**
|
|
106
120
|
* The mod created by {@link createAnthaMenuNavMod}.
|
|
107
121
|
*
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { defineAnthaMod } from '@antha/engine';
|
|
2
2
|
import { KnownInput } from '@antha/gamepad-type';
|
|
3
3
|
import { check } from '@augment-vir/assert';
|
|
4
|
-
import { getObjectTypedEntries
|
|
4
|
+
import { getObjectTypedEntries } from '@augment-vir/common';
|
|
5
5
|
import { convertDuration } from 'date-vir';
|
|
6
6
|
import { NavController, NavDirection, NavValue } from 'device-navigation';
|
|
7
7
|
import { InputDirection } from '../raw-inputs/raw-input.js';
|
|
@@ -262,6 +262,7 @@ export function createAnthaMenuNavMod(options = {}) {
|
|
|
262
262
|
else if (!state.isInMenu) {
|
|
263
263
|
consumeInactiveMenuActivationBindings({
|
|
264
264
|
activeBindings: state.activeBindings,
|
|
265
|
+
allowedPlayerMenuNavigation: state.allowedPlayerMenuNavigation,
|
|
265
266
|
});
|
|
266
267
|
return;
|
|
267
268
|
}
|
|
@@ -274,7 +275,13 @@ export function createAnthaMenuNavMod(options = {}) {
|
|
|
274
275
|
const minimumDirectionalInputValue = state.menuNavOptions.minimumDirectionalInputValue;
|
|
275
276
|
const bindingsToAct = {};
|
|
276
277
|
const activeMenuBindings = {};
|
|
277
|
-
|
|
278
|
+
getObjectTypedEntries(state.activeBindings).forEach(([playerPosition, playerActiveBindings,]) => {
|
|
279
|
+
if (!isPlayerMenuNavigationAllowed({
|
|
280
|
+
allowedPlayerMenuNavigation: state.allowedPlayerMenuNavigation,
|
|
281
|
+
playerPosition,
|
|
282
|
+
})) {
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
278
285
|
getObjectTypedEntries(playerActiveBindings).forEach(([bindingName, activeBinding,]) => {
|
|
279
286
|
if (!check.isEnumValue(bindingName, MenuNavBinding)) {
|
|
280
287
|
return;
|
|
@@ -284,7 +291,8 @@ export function createAnthaMenuNavMod(options = {}) {
|
|
|
284
291
|
activeBinding.value >= minimumDirectionalInputValue) &&
|
|
285
292
|
(!activeBinding.actCount ||
|
|
286
293
|
(directionalMenuNavBindings.includes(bindingName) &&
|
|
287
|
-
activeBinding.holdDuration.milliseconds >=
|
|
294
|
+
activeBinding.holdDuration.milliseconds >=
|
|
295
|
+
repeatThreshold &&
|
|
288
296
|
activeBinding.holdDuration.milliseconds -
|
|
289
297
|
activeBinding.lastActDuration.milliseconds >
|
|
290
298
|
repeatInterval))) {
|
|
@@ -352,8 +360,14 @@ export function createAnthaMenuNavMod(options = {}) {
|
|
|
352
360
|
},
|
|
353
361
|
});
|
|
354
362
|
}
|
|
355
|
-
function consumeInactiveMenuActivationBindings({ activeBindings, }) {
|
|
356
|
-
|
|
363
|
+
function consumeInactiveMenuActivationBindings({ activeBindings, allowedPlayerMenuNavigation, }) {
|
|
364
|
+
getObjectTypedEntries(activeBindings).forEach(([playerPosition, playerActiveBindings,]) => {
|
|
365
|
+
if (!isPlayerMenuNavigationAllowed({
|
|
366
|
+
allowedPlayerMenuNavigation,
|
|
367
|
+
playerPosition,
|
|
368
|
+
})) {
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
357
371
|
[
|
|
358
372
|
MenuNavBinding.MenuEnter,
|
|
359
373
|
MenuNavBinding.MenuExit,
|
|
@@ -367,3 +381,11 @@ function consumeInactiveMenuActivationBindings({ activeBindings, }) {
|
|
|
367
381
|
});
|
|
368
382
|
});
|
|
369
383
|
}
|
|
384
|
+
/**
|
|
385
|
+
* Checks whether the given player may use menu navigation under the current allowlist.
|
|
386
|
+
*
|
|
387
|
+
* @category Internal
|
|
388
|
+
*/
|
|
389
|
+
export function isPlayerMenuNavigationAllowed({ allowedPlayerMenuNavigation, playerPosition, }) {
|
|
390
|
+
return (allowedPlayerMenuNavigation == undefined || !!allowedPlayerMenuNavigation[playerPosition]);
|
|
391
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { type
|
|
1
|
+
import { InputDeviceKey, type GamepadInputDeviceKey } from 'input-device-handler';
|
|
2
|
+
import { InputDirection, type RawInput } from '../raw-inputs/raw-input.js';
|
|
3
3
|
/**
|
|
4
4
|
* A mapping of gamepad keys that simply allows them to be interpreted as different keys. This is
|
|
5
5
|
* useful for mapping a controller in any port to any other port. For example, mapping the
|
|
@@ -49,6 +49,28 @@ export type BindingAssignment = {
|
|
|
49
49
|
* @category Internal
|
|
50
50
|
*/
|
|
51
51
|
export type PlayerPosition = `${number}`;
|
|
52
|
+
/**
|
|
53
|
+
* Shape definition for {@link BindingAssignment}.
|
|
54
|
+
*
|
|
55
|
+
* @category Internal
|
|
56
|
+
*/
|
|
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">)[]>>;
|
|
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
|
+
gamepadBrand: import("object-shape-tester").Shape<import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnsafe<string>>>;
|
|
61
|
+
inputName: import("object-shape-tester").Shape<import("@sinclair/typebox").TUnsafe<string>>;
|
|
62
|
+
}>;
|
|
63
|
+
/**
|
|
64
|
+
* Shape definition for {@link PlayersBindingAssignments}.
|
|
65
|
+
*
|
|
66
|
+
* @category Internal
|
|
67
|
+
*/
|
|
68
|
+
export declare const playersBindingAssignmentsShape: import("object-shape-tester").Shape<import("@sinclair/typebox").TUnsafe<Partial<Record<`${number}`, Partial<Record<string, {
|
|
69
|
+
gamepadBrand?: string;
|
|
70
|
+
deviceKey: "0" | "1" | "2" | "3" | "mouse" | "keyboard" | "any-gamepad";
|
|
71
|
+
inputName: string;
|
|
72
|
+
direction: InputDirection;
|
|
73
|
+
}[]>>>>>>;
|
|
52
74
|
/**
|
|
53
75
|
* A collection of bindings for a single player. Used in `createAnthaReadBindingsMod` and
|
|
54
76
|
* {@link PlayersBindingAssignments}.
|
|
@@ -69,6 +91,8 @@ export type PlayersBindingAssignments<BindingNames extends string = string> = Re
|
|
|
69
91
|
* @category Internal
|
|
70
92
|
*/
|
|
71
93
|
export type ActiveBinding = {
|
|
94
|
+
/** The currently active raw inputs that contribute to this binding. */
|
|
95
|
+
rawInputs: RawInput[];
|
|
72
96
|
/**
|
|
73
97
|
* The full duration for which the current binding has been active in its current direction.
|
|
74
98
|
* When an active is first pressed, this will be 0 milliseconds.
|
|
@@ -1,6 +1,37 @@
|
|
|
1
|
+
import { InputDeviceKey } from 'input-device-handler';
|
|
2
|
+
import { defineShape, enumShape, nonEmptyStringShape, optionalShape, recordShape, typedStringShape, } from 'object-shape-tester';
|
|
3
|
+
import { InputDirection } from '../raw-inputs/raw-input.js';
|
|
1
4
|
/**
|
|
2
5
|
* A binding assignment device key that maps the input to _any_ connected controller.
|
|
3
6
|
*
|
|
4
7
|
* @category Internal
|
|
5
8
|
*/
|
|
6
9
|
export const AnyGamepad = 'any-gamepad';
|
|
10
|
+
/**
|
|
11
|
+
* Shape definition for {@link BindingAssignment}.
|
|
12
|
+
*
|
|
13
|
+
* @category Internal
|
|
14
|
+
*/
|
|
15
|
+
export const bindingAssignmentShape = defineShape({
|
|
16
|
+
deviceKey: enumShape({
|
|
17
|
+
...InputDeviceKey,
|
|
18
|
+
AnyGamepad,
|
|
19
|
+
}),
|
|
20
|
+
direction: enumShape(InputDirection),
|
|
21
|
+
gamepadBrand: optionalShape(nonEmptyStringShape()),
|
|
22
|
+
inputName: nonEmptyStringShape(),
|
|
23
|
+
});
|
|
24
|
+
/**
|
|
25
|
+
* Shape definition for {@link PlayersBindingAssignments}.
|
|
26
|
+
*
|
|
27
|
+
* @category Internal
|
|
28
|
+
*/
|
|
29
|
+
export const playersBindingAssignmentsShape = recordShape({
|
|
30
|
+
keys: typedStringShape(),
|
|
31
|
+
partial: true,
|
|
32
|
+
values: recordShape({
|
|
33
|
+
keys: '',
|
|
34
|
+
partial: true,
|
|
35
|
+
values: [bindingAssignmentShape],
|
|
36
|
+
}),
|
|
37
|
+
});
|
|
@@ -20,6 +20,7 @@ export type AnthaReadRawInputModOptions = PartialWithUndefined<{
|
|
|
20
20
|
*/
|
|
21
21
|
deviceHandlerOptions: Omit<InputDeviceHandlerOptions, 'startLoopImmediately'>;
|
|
22
22
|
debugRawInputs: AnthaReadRawInputModState['debugRawInputs'];
|
|
23
|
+
startRawInputConsumer: AnthaReadRawInputModState['rawInputConsumer'];
|
|
23
24
|
}>;
|
|
24
25
|
/**
|
|
25
26
|
* All state used by and set by `createAnthaReadRawInputMod`.
|
|
@@ -29,6 +30,8 @@ export type AnthaReadRawInputModOptions = PartialWithUndefined<{
|
|
|
29
30
|
export type AnthaReadRawInputModState = {
|
|
30
31
|
deviceHandler: Pick<InputDeviceHandler, 'readAllDevices'>;
|
|
31
32
|
rawInputs: RawInputs;
|
|
33
|
+
/** Identifies the part of the game that owns newly started raw inputs. */
|
|
34
|
+
rawInputConsumer: string | undefined;
|
|
32
35
|
currentInputDevices: SimpleInputDevicesMap;
|
|
33
36
|
/** The engine time when a timed input lock expires. */
|
|
34
37
|
inputDisableEndsAt: EngineTime | undefined;
|
|
@@ -78,6 +81,7 @@ export type AnthaReadRawInputMod = ReturnType<typeof createAnthaReadRawInputMod>
|
|
|
78
81
|
*/
|
|
79
82
|
export declare function readRawInputs(state: SetRequired<SelectFrom<Partial<AnthaReadRawInputModState>, {
|
|
80
83
|
rawInputs: true;
|
|
84
|
+
rawInputConsumer: true;
|
|
81
85
|
deviceHandler: true;
|
|
82
86
|
gamepadLayouts: true;
|
|
83
87
|
gamepadBrandMap: true;
|
|
@@ -17,6 +17,7 @@ export function createAnthaReadRawInputMod(options = {}) {
|
|
|
17
17
|
initState: {
|
|
18
18
|
debugRawInputs: !!options.debugRawInputs,
|
|
19
19
|
inputDisableEndsAt: undefined,
|
|
20
|
+
rawInputConsumer: options.startRawInputConsumer,
|
|
20
21
|
},
|
|
21
22
|
execute({ engine, state, msSinceLastExecute }) {
|
|
22
23
|
if (state.inputDisableEndsAt != undefined &&
|
|
@@ -67,14 +68,18 @@ export function readRawInputs(state, { msSinceLastExecute, }) {
|
|
|
67
68
|
const deviceInputs = {};
|
|
68
69
|
Object.values(device.currentInputs).forEach((currentInput) => {
|
|
69
70
|
const direction = calculateInputDirection(currentInput.inputValue);
|
|
70
|
-
const
|
|
71
|
-
const
|
|
72
|
-
?
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
71
|
+
const potentialPreviousRawInput = state.rawInputs?.[deviceKey]?.[currentInput.inputName];
|
|
72
|
+
const previousRawInput = potentialPreviousRawInput?.direction === direction
|
|
73
|
+
? potentialPreviousRawInput
|
|
74
|
+
: undefined;
|
|
75
|
+
const consumedBy = previousRawInput
|
|
76
|
+
? previousRawInput.consumedBy
|
|
77
|
+
: state.rawInputConsumer;
|
|
78
|
+
const duration = {
|
|
79
|
+
milliseconds: previousRawInput
|
|
80
|
+
? Math.round(previousRawInput.duration.milliseconds + msSinceLastExecute)
|
|
81
|
+
: 0,
|
|
82
|
+
};
|
|
78
83
|
const layout = isGamepadDeviceKey(deviceKey)
|
|
79
84
|
? findMatchingGamepadLayout({
|
|
80
85
|
layouts: state.gamepadLayouts || defaultGamepadLayouts,
|
|
@@ -99,6 +104,7 @@ export function readRawInputs(state, { msSinceLastExecute, }) {
|
|
|
99
104
|
gamepadModel: model?.gamepadModel,
|
|
100
105
|
});
|
|
101
106
|
const rawInput = {
|
|
107
|
+
consumedBy,
|
|
102
108
|
mapped: {
|
|
103
109
|
deviceName: model?.gamepadModel || device.deviceName,
|
|
104
110
|
gamepadBrand: model?.gamepadBrand,
|
|
@@ -111,6 +117,9 @@ export function readRawInputs(state, { msSinceLastExecute, }) {
|
|
|
111
117
|
duration,
|
|
112
118
|
inputName: currentInput.inputName,
|
|
113
119
|
inputValue: currentInput.inputValue,
|
|
120
|
+
isIgnoredByConsumer: !!state.rawInputConsumer &&
|
|
121
|
+
!!consumedBy &&
|
|
122
|
+
consumedBy !== state.rawInputConsumer,
|
|
114
123
|
};
|
|
115
124
|
if (mappedInputName) {
|
|
116
125
|
deviceInputs[mappedInputName] = rawInput;
|
|
@@ -24,6 +24,10 @@ export declare function calculateInputDirection(inputValue: number): InputDirect
|
|
|
24
24
|
* @category Internal
|
|
25
25
|
*/
|
|
26
26
|
export type RawInput = Omit<InputValueWrapper<InputDeviceKey, any>, 'details'> & {
|
|
27
|
+
/** An identifier set by a game when it claims this input. */
|
|
28
|
+
consumedBy: string | undefined;
|
|
29
|
+
/** True when this input belongs to a different raw input consumer. */
|
|
30
|
+
isIgnoredByConsumer: boolean;
|
|
27
31
|
mapped: SelectFrom<InputValueWrapper<InputDeviceKey, any>, {
|
|
28
32
|
/**
|
|
29
33
|
* `deviceName` is mapped by `gamepadModelMap`. If no mapping exists, this will always
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antha/input",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "An Antha mod for handling inputs.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"vir",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"test:docs": "virmator docs check"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@antha/gamepad-type": "^0.
|
|
36
|
+
"@antha/gamepad-type": "^0.10.0",
|
|
37
37
|
"@augment-vir/assert": "^32.2.3",
|
|
38
38
|
"@augment-vir/common": "^32.2.3",
|
|
39
39
|
"date-vir": "^9.1.1",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"istanbul-smart-text-reporter": "^1.1.5"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
54
|
-
"@antha/engine": "^0.
|
|
54
|
+
"@antha/engine": "^0.10.0",
|
|
55
55
|
"element-vir": ">=26"
|
|
56
56
|
},
|
|
57
57
|
"engines": {
|