import { LiteralUnion } from 'type-fest'; import { GamepadButtonName } from '../gamepad'; import { AllKeyCodes } from './keyCodes'; import { StoreProvider } from './store'; /** @partial */ export interface InputCommandOptions { /** @default true */ preventDefault?: boolean; /** * Temporary disable emitting events * @default false */ disabled?: boolean; } /** resolved command from schema */ export type SchemaCommand = InputCommandOptions & { keys: AllKeyCodes[]; gamepad: GamepadButtonName[]; }; export type KeysGroup = 'WASD' | 'Arrows' | 'Digits' | 'NumpadDigits' | 'AnyDigits' | 'Modifiers' | 'FKeys' | 'PageMoveKeys'; type KeyboardKey = AllKeyCodes | null; export type SchemaCommandInput = [KeyboardKey | AllKeyCodes[], GamepadButtonName?, InputCommandOptions?] | null; export type InputCommandsSchema = { [category: string]: { [command: string]: SchemaCommandInput; }; }; type GamepadButtons = GamepadButtonName[]; export type InputGroupedCommandsSchema = { [category: string]: { [groupedCommand: string]: [KeysGroup | AllKeyCodes[], GamepadButtons, InputCommandOptions?]; }; }; export type InputSchema = { /** * By default, we're automatically listening to these keys and update movement vector. Specify null to disable for keyboard * @default null */ movementKeymap?: 'WASD' | 'WASDArrows' | 'Arrows' | null; }; export interface InputSchemaArg extends InputSchema { commands: T; groupedCommands?: K; } export type MovementVector2d = { x: number; z: number; }; export type MovementVector3d = { x: number; y: number; z: number; }; type SubPath = Key extends string ? T[Key] extends Record ? `${Key}.${Exclude & string}` : never : never; export type AllSchemaCommands = SubPath; export type CommandEventArgument = { command: AllSchemaCommands; }; export type GroupedCommandEventDeviceType = // TODO doc: should rely on index rather than button or key { type: 'keyboard'; key: AllKeyCodes; } | { type: 'gamepad'; gamepadIndex: number; button: GamepadButtonName; }; export type GroupCommandEventArgument = { command: AllSchemaCommands; schema: InputGroupedCommandsSchema['']['']; /** Keyboard key or gamepad button index */ index: number; } & GroupedCommandEventDeviceType; export type SourceType = { /** Who emitted event: Gamepad with index or keyboard if `undefined` */ gamepadIndex?: number; }; export type ControEvents = { trigger: (arg0: CommandEventArgument) => void; triggerGrouped: (arg0: GroupCommandEventArgument) => void; release: (arg0: CommandEventArgument) => void; releaseGrouped: (arg0: GroupCommandEventArgument) => void; stickMovement: (arg0: { stick: 'left' | 'right'; vector: MovementVector2d; }) => void; movementUpdate: (arg0: { vector: M extends '3d' ? MovementVector3d : MovementVector2d; soleVector: M extends '3d' ? MovementVector3d : MovementVector2d; } & SourceType) => void; /** usually to switch slots */ mouseWheel: (arg0: { direction: -1 | 1; }) => void; pressedKeyOrButtonChanged: (arg0: ({ code: AllKeyCodes; } | { gamepadIndex: number; button: GamepadButtonName; }) & { state: boolean; }) => void; userConfigResolve: () => void; }; type KeyboardTarget = Record<'addEventListener' | 'removeEventListener', (...args: ['keydown' | 'keyup', (e: KeyboardEvent) => void]) => unknown>; export interface CreateControlsSchemaOptions { /** To what bind events * @default window */ target?: KeyboardTarget; /** * Sometimes target is dynamic, so you use global target and filter events here * * @param e undefined if is gamepad * @returns continue */ captureEvents?: (e?: KeyboardEvent) => boolean; /** * If true, then events that fired programmatically will be ignored * @default false */ requireTrusted?: boolean; /** Use additional DOM events to workaround certain bugs with keyboard * @default true */ additionalEventHandlers?: boolean; /** * - `only-first-gamepad`: listen for first only connected gamepad * - `only-last-gamepad`: listen for last only connected gamepad * - `all-gamepads`: listen for buttons and sticks from all connected gamepads * - `none`: temporary disabling gamepads support * @default all-gamepads */ listenGamepadsStrategy?: 'only-first-gamepad' | 'only-last-gamepad' | 'all-gamepads' | 'none'; defaultControlOptions?: InputCommandOptions; /** * Default is precise enough * @default 250 */ gamepadPollingInterval?: LiteralUnion<500 | 250, number>; /** * - all - emit buttons * - split - * @default all */ /** * - all - sum movement from keyboard and all listening gamepads (controlled by `listenGamepadsStrategy`) * - split - emit independent movement vectors for keyboard and each gamepads. Useful for split-screen games * @default all */ emitMovement?: 'all' | 'split'; storeProvider?: StoreProvider; } export {};