@utsp/input 0.10.0-nightly.20251211194839.e68c16e → 0.10.0-nightly.20251213135145.115c488

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,563 @@
1
+ import { Vector2, Vector3, IGamepadVibrationProcessor, GamepadVibrationOptions } from '@utsp/types';
2
+ export { GamepadInput, InputDeviceType, Vector2 } from '@utsp/types';
3
+
4
+ /**
5
+ * Base interface for all input management systems
6
+ * This provides a common API for keyboard, mouse, gamepad, and touch inputs
7
+ */
8
+ interface IInputs {
9
+ /**
10
+ * Start listening to input events
11
+ */
12
+ start(): void;
13
+ /**
14
+ * Stop listening to input events and cleanup
15
+ */
16
+ stop(): void;
17
+ /**
18
+ * Reset all input states to default
19
+ */
20
+ reset(): void;
21
+ /**
22
+ * Check if the input system is currently active/listening
23
+ */
24
+ isListening(): boolean;
25
+ /**
26
+ * Cleanup and destroy the instance, removing all event listeners
27
+ */
28
+ destroy(): void;
29
+ /**
30
+ * Reset delta values (call at the end of each frame/update)
31
+ */
32
+ resetDelta(): void;
33
+ /**
34
+ * Set or update event callbacks
35
+ */
36
+ setCallbacks(callbacks: unknown): void;
37
+ /**
38
+ * Remove all event callbacks
39
+ */
40
+ clearCallbacks(): void;
41
+ /**
42
+ * Check if a key is currently pressed (Keyboard)
43
+ */
44
+ isKeyPressed?(key: string): boolean;
45
+ /**
46
+ * Check if left mouse button is pressed (Mouse)
47
+ */
48
+ isLeftMousePressed?(): boolean;
49
+ /**
50
+ * Get mouse position (Mouse)
51
+ */
52
+ getMousePosition?(): Vector2 | null;
53
+ /**
54
+ * Get mouse movement delta (Mouse)
55
+ */
56
+ getMouseDelta?(): Vector2 | null;
57
+ /**
58
+ * Get wheel delta (Mouse)
59
+ */
60
+ getWheelDelta?(): Vector2 | null;
61
+ /**
62
+ * Check if a button is pressed (Gamepad, TVRemote)
63
+ */
64
+ isButtonPressed?(button: string | number): boolean;
65
+ /**
66
+ * Get axis value (Gamepad, TVRemote)
67
+ */
68
+ getAxis?(axis: number): number | null;
69
+ /**
70
+ * Get motion/gyroscope data (TVRemote, Mobile)
71
+ */
72
+ getMotionData?(): MotionData | null;
73
+ }
74
+ /**
75
+ * Motion/Gyroscope data interface
76
+ */
77
+ interface MotionData extends Vector3 {
78
+ timestamp?: number;
79
+ }
80
+ /**
81
+ * Common button state interface
82
+ */
83
+ interface ButtonState {
84
+ pressed: boolean;
85
+ justPressed: boolean;
86
+ justReleased: boolean;
87
+ timestamp?: number;
88
+ }
89
+ /**
90
+ * Input event types enumeration
91
+ */
92
+ declare enum InputEventType {
93
+ KeyDown = "keydown",
94
+ KeyUp = "keyup",
95
+ MouseDown = "mousedown",
96
+ MouseUp = "mouseup",
97
+ MouseMove = "mousemove",
98
+ MouseWheel = "mousewheel",
99
+ TouchStart = "touchstart",
100
+ TouchEnd = "touchend",
101
+ TouchMove = "touchmove",
102
+ GamepadConnected = "gamepadconnected",
103
+ GamepadDisconnected = "gamepaddisconnected",
104
+ GamepadButton = "gamepadbutton",
105
+ GamepadAxis = "gamepadaxis"
106
+ }
107
+ /**
108
+ * Configuration options for input systems
109
+ */
110
+ interface InputConfig {
111
+ /**
112
+ * Target element to attach listeners to (default: window)
113
+ */
114
+ targetElement?: HTMLElement | Window;
115
+ /**
116
+ * Enable/disable specific input types
117
+ */
118
+ enabled?: boolean;
119
+ /**
120
+ * Prevent default browser behaviors
121
+ */
122
+ preventDefault?: boolean;
123
+ /**
124
+ * Stop event propagation
125
+ */
126
+ stopPropagation?: boolean;
127
+ /**
128
+ * Enable debug logging
129
+ */
130
+ debug?: boolean;
131
+ }
132
+ /**
133
+ * Abstract base class for input systems
134
+ * All input classes should extend this to maintain consistency
135
+ */
136
+ declare abstract class BaseInputs implements IInputs {
137
+ protected isActive: boolean;
138
+ protected targetElement: HTMLElement | Window;
139
+ protected config: InputConfig;
140
+ protected callbacks: Record<string, ((...args: any[]) => void)[]>;
141
+ constructor(targetElement?: HTMLElement | Window, config?: InputConfig);
142
+ abstract start(): void;
143
+ abstract stop(): void;
144
+ abstract reset(): void;
145
+ abstract resetDelta(): void;
146
+ isListening(): boolean;
147
+ destroy(): void;
148
+ setCallbacks(callbacks: unknown): void;
149
+ clearCallbacks(): void;
150
+ /**
151
+ * Get the target element
152
+ */
153
+ getTargetElement(): HTMLElement | Window;
154
+ /**
155
+ * Enable the input system
156
+ */
157
+ enable(): void;
158
+ /**
159
+ * Disable the input system
160
+ */
161
+ disable(): void;
162
+ /**
163
+ * Check if the input system is enabled
164
+ */
165
+ isEnabled(): boolean;
166
+ /**
167
+ * Emit an event to all registered callbacks
168
+ */
169
+ protected emit(eventType: string, ...args: unknown[]): void;
170
+ /**
171
+ * Log debug messages if debug mode is enabled
172
+ */
173
+ protected log(...args: unknown[]): void;
174
+ }
175
+
176
+ /**
177
+ * GamepadInputs - Gamepad/Controller input management
178
+ * Handles gamepad events with support for multiple controllers, buttons, axes, and vibration
179
+ */
180
+
181
+ /**
182
+ * Gamepad button state with press detection
183
+ */
184
+ interface GamepadButtonState {
185
+ pressed: boolean;
186
+ justPressed: boolean;
187
+ justReleased: boolean;
188
+ value: number;
189
+ touched: boolean;
190
+ }
191
+ /**
192
+ * Complete state of a single gamepad
193
+ */
194
+ interface GamepadState {
195
+ id: string;
196
+ index: number;
197
+ connected: boolean;
198
+ buttons: GamepadButtonState[];
199
+ axes: number[];
200
+ timestamp: number;
201
+ mapping: GamepadMappingType;
202
+ hapticActuators?: GamepadHapticActuator[];
203
+ }
204
+ /**
205
+ * Callbacks for gamepad events
206
+ */
207
+ interface GamepadCallbacks {
208
+ onGamepadConnected?: (gamepad: GamepadState) => void;
209
+ onGamepadDisconnected?: (index: number) => void;
210
+ onButtonDown?: (gamepadIndex: number, buttonIndex: number, value: number) => void;
211
+ onButtonUp?: (gamepadIndex: number, buttonIndex: number) => void;
212
+ onAxisMove?: (gamepadIndex: number, axisIndex: number, value: number) => void;
213
+ }
214
+ /**
215
+ * Configuration for GamepadInputs
216
+ */
217
+ interface GamepadInputsConfig extends InputConfig {
218
+ /** Maximum number of gamepads to support (default: 4) */
219
+ maxGamepads?: number;
220
+ /** Deadzone for analog axes (default: 0.1) */
221
+ axisDeadzone?: number;
222
+ /** Polling interval in milliseconds (default: 16 ~= 60fps) */
223
+ pollingInterval?: number;
224
+ /** Enable automatic polling (default: true) */
225
+ autoPolling?: boolean;
226
+ }
227
+ declare class GamepadInputs extends BaseInputs implements IGamepadVibrationProcessor {
228
+ private gamepads;
229
+ private previousGamepads;
230
+ private gamepadCallbacks;
231
+ private gamepadConfig;
232
+ private pollingIntervalId;
233
+ private rafId;
234
+ private boundHandlers;
235
+ constructor(config?: GamepadInputsConfig);
236
+ start(): void;
237
+ stop(): void;
238
+ reset(): void;
239
+ resetDelta(): void;
240
+ /**
241
+ * Start automatic polling of gamepad state
242
+ */
243
+ startPolling(): void;
244
+ /**
245
+ * Stop automatic polling
246
+ */
247
+ stopPolling(): void;
248
+ /**
249
+ * Manually poll gamepad state (call this in your game loop if autoPolling is false)
250
+ *
251
+ * @example
252
+ * ```typescript
253
+ * const gamepad = new GamepadInputs({ autoPolling: false });
254
+ * gamepad.start();
255
+ *
256
+ * function gameLoop() {
257
+ * gamepad.poll(); // Manual polling
258
+ * // Check inputs...
259
+ * requestAnimationFrame(gameLoop);
260
+ * }
261
+ * ```
262
+ */
263
+ poll(): void;
264
+ /**
265
+ * Get the number of connected gamepads
266
+ *
267
+ * @returns Number of gamepads currently connected (0-4)
268
+ *
269
+ * @example
270
+ * ```typescript
271
+ * const count = gamepad.getConnectedGamepadCount();
272
+ * console.log(`${count} gamepads connected`);
273
+ * ```
274
+ */
275
+ getConnectedGamepadCount(): number;
276
+ /**
277
+ * Check if a gamepad is connected at the given index
278
+ *
279
+ * @param index - Gamepad index (0-3)
280
+ * @returns True if gamepad is connected, false otherwise
281
+ *
282
+ * @example
283
+ * ```typescript
284
+ * if (gamepad.isGamepadConnected(0)) {
285
+ * console.log('Player 1 controller ready!');
286
+ * }
287
+ * ```
288
+ */
289
+ isGamepadConnected(index: number): boolean;
290
+ /**
291
+ * Get the state of a specific gamepad
292
+ */
293
+ getGamepadState(index: number): GamepadState | null;
294
+ /**
295
+ * Get all connected gamepads
296
+ */
297
+ getAllGamepads(): GamepadState[];
298
+ /**
299
+ * Check if a button is currently pressed
300
+ *
301
+ * @param gamepadIndex - Gamepad index (0-3)
302
+ * @param buttonIndex - Button index (0-16, standard mapping)
303
+ * @returns True if button is pressed, false otherwise
304
+ * @throws {Error} If gamepadIndex or buttonIndex is invalid
305
+ *
306
+ * @example
307
+ * ```typescript
308
+ * // Check A button (Xbox) / Cross (PlayStation)
309
+ * if (gamepad.isButtonPressed(0, 0)) {
310
+ * console.log('Action button pressed!');
311
+ * }
312
+ * ```
313
+ */
314
+ isButtonPressed(gamepadIndex: number, buttonIndex: number): boolean;
315
+ /**
316
+ * Check if a button was just pressed this frame
317
+ */
318
+ isButtonJustPressed(gamepadIndex: number, buttonIndex: number): boolean;
319
+ /**
320
+ * Check if a button was just released this frame
321
+ */
322
+ isButtonJustReleased(gamepadIndex: number, buttonIndex: number): boolean;
323
+ /**
324
+ * Get the analog value of a button (0.0 to 1.0)
325
+ */
326
+ getButtonValue(gamepadIndex: number, buttonIndex: number): number;
327
+ /**
328
+ * Get the value of an axis (-1.0 to 1.0, with deadzone applied)
329
+ *
330
+ * @param gamepadIndex - Gamepad index (0-3)
331
+ * @param axisIndex - Axis index (0: left X, 1: left Y, 2: right X, 3: right Y)
332
+ * @returns Axis value from -1.0 (left/up) to 1.0 (right/down)
333
+ * @throws {Error} If gamepadIndex or axisIndex is invalid
334
+ *
335
+ * @example
336
+ * ```typescript
337
+ * const leftStickX = gamepad.getAxis(0, 0);
338
+ * const leftStickY = gamepad.getAxis(0, 1);
339
+ *
340
+ * playerX += leftStickX * speed;
341
+ * playerY += leftStickY * speed;
342
+ * ```
343
+ */
344
+ getAxis(gamepadIndex: number, axisIndex: number): number;
345
+ /**
346
+ * Get the raw axis value without deadzone
347
+ */
348
+ getAxisRaw(gamepadIndex: number, axisIndex: number): number;
349
+ /**
350
+ * Get left stick as a 2D vector (x: axis 0, y: axis 1)
351
+ *
352
+ * @param gamepadIndex - Gamepad index (0-3)
353
+ * @returns Object with x and y properties (-1.0 to 1.0)
354
+ *
355
+ * @example
356
+ * ```typescript
357
+ * const leftStick = gamepad.getLeftStick(0);
358
+ *
359
+ * if (Math.abs(leftStick.x) > 0.5) {
360
+ * console.log('Strong horizontal input');
361
+ * }
362
+ *
363
+ * // Calculate magnitude for movement
364
+ * const magnitude = Math.sqrt(leftStick.x ** 2 + leftStick.y ** 2);
365
+ * ```
366
+ */
367
+ getLeftStick(gamepadIndex: number): {
368
+ x: number;
369
+ y: number;
370
+ };
371
+ /**
372
+ * Get right stick as a 2D vector (x: axis 2, y: axis 3)
373
+ */
374
+ getRightStick(gamepadIndex: number): {
375
+ x: number;
376
+ y: number;
377
+ };
378
+ /**
379
+ * Check if a gamepad supports vibration
380
+ *
381
+ * @param gamepadIndex - Gamepad index (0-3)
382
+ * @returns True if gamepad supports vibration
383
+ *
384
+ * @example
385
+ * ```typescript
386
+ * if (gamepad.supportsVibration(0)) {
387
+ * gamepad.vibrate(0, { duration: 200, strongMagnitude: 1.0 });
388
+ * }
389
+ * ```
390
+ */
391
+ supportsVibration(gamepadIndex: number): boolean;
392
+ /**
393
+ * Vibrate a gamepad with dual-motor control
394
+ *
395
+ * Modern gamepads have two motors:
396
+ * - Strong motor (low frequency) - heavy rumble
397
+ * - Weak motor (high frequency) - light vibration
398
+ *
399
+ * @param gamepadIndex - Gamepad index (0-3)
400
+ * @param options - Vibration options
401
+ * @returns Promise that resolves when vibration completes or fails
402
+ *
403
+ * @example
404
+ * ```typescript
405
+ * // Strong rumble for 500ms
406
+ * await gamepad.vibrate(0, {
407
+ * duration: 500,
408
+ * strongMagnitude: 1.0,
409
+ * weakMagnitude: 0.0,
410
+ * });
411
+ *
412
+ * // Light vibration
413
+ * await gamepad.vibrate(0, {
414
+ * duration: 200,
415
+ * strongMagnitude: 0.0,
416
+ * weakMagnitude: 0.5,
417
+ * });
418
+ *
419
+ * // Balanced rumble
420
+ * await gamepad.vibrate(0, {
421
+ * duration: 300,
422
+ * strongMagnitude: 0.5,
423
+ * weakMagnitude: 0.5,
424
+ * });
425
+ * ```
426
+ */
427
+ vibrate(gamepadIndex: number, options: GamepadVibrationOptions): Promise<boolean>;
428
+ /**
429
+ * Stop vibration on a gamepad
430
+ *
431
+ * @param gamepadIndex - Gamepad index (0-3)
432
+ * @returns Promise that resolves when vibration is stopped
433
+ */
434
+ stopVibration(gamepadIndex: number): Promise<boolean>;
435
+ /**
436
+ * Stop vibration on all connected gamepads
437
+ */
438
+ stopAllVibrations(): Promise<void>;
439
+ /**
440
+ * Vibrate all connected gamepads
441
+ *
442
+ * @param options - Vibration options (same as vibrate())
443
+ */
444
+ vibrateAll(options: {
445
+ duration: number;
446
+ strongMagnitude?: number;
447
+ weakMagnitude?: number;
448
+ startDelay?: number;
449
+ }): Promise<void>;
450
+ /**
451
+ * Preset vibration patterns for common use cases
452
+ */
453
+ readonly vibrationPresets: {
454
+ /** Light tap feedback */
455
+ tap: {
456
+ duration: number;
457
+ strongMagnitude: number;
458
+ weakMagnitude: number;
459
+ };
460
+ /** Medium impact */
461
+ impact: {
462
+ duration: number;
463
+ strongMagnitude: number;
464
+ weakMagnitude: number;
465
+ };
466
+ /** Heavy rumble */
467
+ heavy: {
468
+ duration: number;
469
+ strongMagnitude: number;
470
+ weakMagnitude: number;
471
+ };
472
+ /** Success feedback */
473
+ success: {
474
+ duration: number;
475
+ strongMagnitude: number;
476
+ weakMagnitude: number;
477
+ };
478
+ /** Error/failure feedback */
479
+ error: {
480
+ duration: number;
481
+ strongMagnitude: number;
482
+ weakMagnitude: number;
483
+ };
484
+ /** Explosion effect */
485
+ explosion: {
486
+ duration: number;
487
+ strongMagnitude: number;
488
+ weakMagnitude: number;
489
+ };
490
+ /** Engine/continuous rumble */
491
+ engine: {
492
+ duration: number;
493
+ strongMagnitude: number;
494
+ weakMagnitude: number;
495
+ };
496
+ /** Heartbeat (use twice with delay) */
497
+ heartbeat: {
498
+ duration: number;
499
+ strongMagnitude: number;
500
+ weakMagnitude: number;
501
+ };
502
+ };
503
+ /**
504
+ * Play a preset vibration pattern
505
+ *
506
+ * @param gamepadIndex - Gamepad index (0-3)
507
+ * @param preset - Preset name
508
+ *
509
+ * @example
510
+ * ```typescript
511
+ * // On hit
512
+ * gamepad.vibratePreset(0, 'impact');
513
+ *
514
+ * // On explosion
515
+ * gamepad.vibratePreset(0, 'explosion');
516
+ * ```
517
+ */
518
+ vibratePreset(gamepadIndex: number, preset: keyof typeof this.vibrationPresets): Promise<boolean>;
519
+ /**
520
+ * Get the native Gamepad object (for advanced usage)
521
+ */
522
+ private getNativeGamepad;
523
+ /**
524
+ * Set gamepad-specific callbacks
525
+ *
526
+ * @param callbacks - Object containing callback functions
527
+ *
528
+ * @example
529
+ * ```typescript
530
+ * gamepad.setGamepadCallbacks({
531
+ * onGamepadConnected: (state) => {
532
+ * console.log(`Gamepad connected: ${state.id}`);
533
+ * },
534
+ * onButtonDown: (gamepadIndex, buttonIndex, value) => {
535
+ * console.log(`Button ${buttonIndex} pressed with value ${value}`);
536
+ * },
537
+ * onAxisMove: (gamepadIndex, axisIndex, value) => {
538
+ * console.log(`Axis ${axisIndex} moved to ${value}`);
539
+ * },
540
+ * });
541
+ * ```
542
+ */
543
+ setGamepadCallbacks(callbacks: GamepadCallbacks): void;
544
+ /**
545
+ * Clear all gamepad callbacks
546
+ */
547
+ clearGamepadCallbacks(): void;
548
+ setCallbacks(callbacks: GamepadCallbacks | unknown): void;
549
+ private isGamepadCallbacks;
550
+ private updateGamepadState;
551
+ private triggerCallbacks;
552
+ private handleGamepadConnected;
553
+ private handleGamepadDisconnected;
554
+ private applyDeadzone;
555
+ private cloneGamepadState;
556
+ }
557
+ /**
558
+ * React hook for gamepad inputs
559
+ */
560
+ declare function useGamepadInputs(callbacks?: GamepadCallbacks, config?: GamepadInputsConfig): GamepadInputs;
561
+
562
+ export { BaseInputs, GamepadInputs, InputEventType, useGamepadInputs };
563
+ export type { ButtonState, GamepadButtonState, GamepadCallbacks, GamepadInputsConfig, GamepadState, IInputs, InputConfig };
@@ -0,0 +1 @@
1
+ var G=Object.defineProperty;var M=(o,n,e)=>n in o?G(o,n,{enumerable:!0,configurable:!0,writable:!0,value:e}):o[n]=e;var m=(o,n)=>G(o,"name",{value:n,configurable:!0});var s=(o,n,e)=>(M(o,typeof n!="symbol"?n+"":n,e),e);import{Vector2 as V}from"@utsp/types";import{InputDeviceType as B,GamepadInput as N}from"@utsp/types";var w=(r=>(r.KeyDown="keydown",r.KeyUp="keyup",r.MouseDown="mousedown",r.MouseUp="mouseup",r.MouseMove="mousemove",r.MouseWheel="mousewheel",r.TouchStart="touchstart",r.TouchEnd="touchend",r.TouchMove="touchmove",r.GamepadConnected="gamepadconnected",r.GamepadDisconnected="gamepaddisconnected",r.GamepadButton="gamepadbutton",r.GamepadAxis="gamepadaxis",r))(w||{}),f=class f{constructor(n=window,e={}){s(this,"isActive",!1);s(this,"targetElement");s(this,"config");s(this,"callbacks",{});this.targetElement=n,this.config={enabled:!0,preventDefault:!1,stopPropagation:!1,debug:!1,...e}}isListening(){return this.isActive}destroy(){this.stop(),this.clearCallbacks()}setCallbacks(n){typeof n=="object"&&n!==null&&Object.assign(this.callbacks,n)}clearCallbacks(){this.callbacks={}}getTargetElement(){return this.targetElement}enable(){this.config.enabled=!0,this.isActive||this.start()}disable(){this.config.enabled=!1,this.isActive&&this.stop()}isEnabled(){return this.config.enabled??!0}emit(n,...e){this.callbacks[n]&&this.callbacks[n].forEach(t=>{try{t(...e)}catch(a){this.config.debug&&console.error(`Error in ${n} callback:`,a)}})}log(...n){this.config.debug&&console.warn("[InputSystem]",...n)}};m(f,"BaseInputs");var b=f;var k=.01,v=class v extends b{constructor(e={}){super(window,e);s(this,"gamepads",new Map);s(this,"previousGamepads",new Map);s(this,"gamepadCallbacks",{});s(this,"gamepadConfig");s(this,"pollingIntervalId",null);s(this,"rafId",null);s(this,"boundHandlers");s(this,"vibrationPresets",{tap:{duration:50,strongMagnitude:.3,weakMagnitude:.5},impact:{duration:100,strongMagnitude:.7,weakMagnitude:.3},heavy:{duration:200,strongMagnitude:1,weakMagnitude:.2},success:{duration:150,strongMagnitude:.4,weakMagnitude:.6},error:{duration:300,strongMagnitude:.8,weakMagnitude:.4},explosion:{duration:400,strongMagnitude:1,weakMagnitude:.8},engine:{duration:1e3,strongMagnitude:.5,weakMagnitude:.2},heartbeat:{duration:100,strongMagnitude:.6,weakMagnitude:.1}});this.gamepadConfig={targetElement:window,maxGamepads:e.maxGamepads??4,axisDeadzone:e.axisDeadzone??.1,pollingInterval:e.pollingInterval??16,autoPolling:e.autoPolling??!0,enabled:e.enabled??!0,debug:e.debug??!1,preventDefault:e.preventDefault??!1,stopPropagation:e.stopPropagation??!1},this.boundHandlers={connected:this.handleGamepadConnected.bind(this),disconnected:this.handleGamepadDisconnected.bind(this)}}start(){this.isActive||(window.addEventListener("gamepadconnected",this.boundHandlers.connected),window.addEventListener("gamepaddisconnected",this.boundHandlers.disconnected),this.gamepadConfig.autoPolling&&this.startPolling(),this.isActive=!0,this.log("GamepadInputs started"))}stop(){this.isActive&&(window.removeEventListener("gamepadconnected",this.boundHandlers.connected),window.removeEventListener("gamepaddisconnected",this.boundHandlers.disconnected),this.stopPolling(),this.isActive=!1,this.log("GamepadInputs stopped"))}reset(){this.gamepads.clear(),this.previousGamepads.clear()}resetDelta(){this.previousGamepads.clear(),this.gamepads.forEach((e,t)=>{this.previousGamepads.set(t,this.cloneGamepadState(e))})}startPolling(){if(this.pollingIntervalId!==null||this.rafId!==null)return;let e=m(()=>{this.poll(),this.rafId=requestAnimationFrame(e)},"poll");this.rafId=requestAnimationFrame(e),this.log("Polling started")}stopPolling(){this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null),this.pollingIntervalId!==null&&(clearInterval(this.pollingIntervalId),this.pollingIntervalId=null),this.log("Polling stopped")}poll(){let e=navigator.getGamepads?navigator.getGamepads():[];for(let t=0;t<e.length&&t<this.gamepadConfig.maxGamepads;t++){let a=e[t];a&&this.updateGamepadState(a)}}getConnectedGamepadCount(){return this.gamepads.size}isGamepadConnected(e){return this.gamepads.has(e)&&this.gamepads.get(e).connected}getGamepadState(e){return this.gamepads.get(e)||null}getAllGamepads(){return Array.from(this.gamepads.values()).filter(e=>e.connected)}isButtonPressed(e,t){if(!Number.isInteger(e)||e<0)throw new Error(`Invalid gamepadIndex: ${e}. Must be a non-negative integer.`);if(!Number.isInteger(t)||t<0)throw new Error(`Invalid buttonIndex: ${t}. Must be a non-negative integer.`);let a=this.gamepads.get(e);return!a||!a.connected||t>=a.buttons.length?!1:a.buttons[t].pressed}isButtonJustPressed(e,t){if(!Number.isInteger(e)||e<0)throw new Error(`Invalid gamepadIndex: ${e}. Must be a non-negative integer.`);if(!Number.isInteger(t)||t<0)throw new Error(`Invalid buttonIndex: ${t}. Must be a non-negative integer.`);let a=this.gamepads.get(e);return!a||!a.connected||t>=a.buttons.length?!1:a.buttons[t].justPressed}isButtonJustReleased(e,t){if(!Number.isInteger(e)||e<0)throw new Error(`Invalid gamepadIndex: ${e}. Must be a non-negative integer.`);if(!Number.isInteger(t)||t<0)throw new Error(`Invalid buttonIndex: ${t}. Must be a non-negative integer.`);let a=this.gamepads.get(e);return!a||!a.connected||t>=a.buttons.length?!1:a.buttons[t].justReleased}getButtonValue(e,t){if(!Number.isInteger(e)||e<0)throw new Error(`Invalid gamepadIndex: ${e}. Must be a non-negative integer.`);if(!Number.isInteger(t)||t<0)throw new Error(`Invalid buttonIndex: ${t}. Must be a non-negative integer.`);let a=this.gamepads.get(e);return!a||!a.connected||t>=a.buttons.length?0:a.buttons[t].value}getAxis(e,t){if(!Number.isInteger(e)||e<0)throw new Error(`Invalid gamepadIndex: ${e}. Must be a non-negative integer.`);if(!Number.isInteger(t)||t<0)throw new Error(`Invalid axisIndex: ${t}. Must be a non-negative integer.`);let a=this.gamepads.get(e);if(!a||!a.connected||t>=a.axes.length)return 0;let i=a.axes[t];return this.applyDeadzone(i)}getAxisRaw(e,t){if(!Number.isInteger(e)||e<0)throw new Error(`Invalid gamepadIndex: ${e}. Must be a non-negative integer.`);if(!Number.isInteger(t)||t<0)throw new Error(`Invalid axisIndex: ${t}. Must be a non-negative integer.`);let a=this.gamepads.get(e);return!a||!a.connected||t>=a.axes.length?0:a.axes[t]}getLeftStick(e){return{x:this.getAxis(e,0),y:this.getAxis(e,1)}}getRightStick(e){return{x:this.getAxis(e,2),y:this.getAxis(e,3)}}supportsVibration(e){let t=this.getNativeGamepad(e);if(!t)return!1;let a=t.vibrationActuator;if(a&&typeof a.playEffect=="function")return!0;let i=t.hapticActuators;return!!(i&&i.length>0)}async vibrate(e,t){let a=this.getNativeGamepad(e);if(!a)return this.log(`Vibrate failed: gamepad ${e} not found`),!1;let{duration:i,strongMagnitude:u=1,weakMagnitude:d=.5,startDelay:g=0}=t;try{let l=a.vibrationActuator;if(l&&typeof l.playEffect=="function"){let c=await l.playEffect("dual-rumble",{startDelay:g,duration:i,strongMagnitude:Math.min(1,Math.max(0,u)),weakMagnitude:Math.min(1,Math.max(0,d))});return this.log(`Vibration effect result: ${c}`),c==="complete"}let p=a.hapticActuators;if(p&&p.length>0){let c=Math.max(u,d);return await p[0].pulse(c,i),!0}return this.log(`Vibrate failed: gamepad ${e} does not support vibration`),!1}catch(l){return this.log("Vibrate error:",l),!1}}async stopVibration(e){let t=this.getNativeGamepad(e);if(!t)return!1;try{let a=t.vibrationActuator;return a&&typeof a.reset=="function"?(await a.reset(),!0):a&&typeof a.playEffect=="function"?(await a.playEffect("dual-rumble",{duration:0,strongMagnitude:0,weakMagnitude:0}),!0):!1}catch(a){return this.log("Stop vibration error:",a),!1}}async stopAllVibrations(){let e=[];for(let[t]of this.gamepads)e.push(this.stopVibration(t));await Promise.all(e)}async vibrateAll(e){let t=[];for(let[a]of this.gamepads)t.push(this.vibrate(a,e));await Promise.all(t)}async vibratePreset(e,t){let a=this.vibrationPresets[t];return a?this.vibrate(e,a):(this.log(`Unknown vibration preset: ${t}`),!1)}getNativeGamepad(e){return(navigator.getGamepads?navigator.getGamepads():[])[e]||null}setGamepadCallbacks(e){this.gamepadCallbacks={...this.gamepadCallbacks,...e}}clearGamepadCallbacks(){this.gamepadCallbacks={}}setCallbacks(e){this.isGamepadCallbacks(e)&&this.setGamepadCallbacks(e)}isGamepadCallbacks(e){return typeof e=="object"&&e!==null&&("onGamepadConnected"in e||"onGamepadDisconnected"in e||"onButtonDown"in e||"onButtonUp"in e||"onAxisMove"in e)}updateGamepadState(e){let t=this.gamepads.get(e.index),a=Array.from(e.buttons).map((d,g)=>{let l=t?.buttons[g],p=d.pressed,c=l?.pressed??!1;return{pressed:p,justPressed:p&&!c,justReleased:!p&&c,value:d.value,touched:d.touched??!1}}),i=Array.from(e.axes),u={id:e.id,index:e.index,connected:e.connected,buttons:a,axes:i,timestamp:e.timestamp,mapping:e.mapping};this.gamepads.set(e.index,u),this.triggerCallbacks(u,t)}triggerCallbacks(e,t){try{e.buttons.forEach((a,i)=>{a.justPressed&&this.gamepadCallbacks.onButtonDown?.(e.index,i,a.value),a.justReleased&&this.gamepadCallbacks.onButtonUp?.(e.index,i)}),e.axes.forEach((a,i)=>{let u=t?.axes[i]??0,d=this.applyDeadzone(a),g=this.applyDeadzone(u);Math.abs(d-g)>k&&this.gamepadCallbacks.onAxisMove?.(e.index,i,d)})}catch(a){this.log("Error in gamepad callbacks:",a)}}handleGamepadConnected(e){try{this.log(`Gamepad connected: ${e.gamepad.id} at index ${e.gamepad.index}`);let t={id:e.gamepad.id,index:e.gamepad.index,connected:!0,buttons:Array.from(e.gamepad.buttons).map(a=>({pressed:a.pressed,justPressed:!1,justReleased:!1,value:a.value,touched:a.touched??!1})),axes:Array.from(e.gamepad.axes),timestamp:e.gamepad.timestamp,mapping:e.gamepad.mapping};this.gamepads.set(e.gamepad.index,t),this.gamepadCallbacks.onGamepadConnected?.(t)}catch(t){this.log("Error handling gamepad connected:",t)}}handleGamepadDisconnected(e){try{this.log(`Gamepad disconnected at index ${e.gamepad.index}`),this.gamepads.delete(e.gamepad.index),this.previousGamepads.delete(e.gamepad.index),this.gamepadCallbacks.onGamepadDisconnected?.(e.gamepad.index)}catch(t){this.log("Error handling gamepad disconnected:",t)}}applyDeadzone(e){let t=this.gamepadConfig.axisDeadzone;return Math.abs(e)<t?0:(e>0?1:-1)*((Math.abs(e)-t)/(1-t))}cloneGamepadState(e){return{...e,buttons:e.buttons.map(t=>({...t})),axes:[...e.axes]}}};m(v,"GamepadInputs");var h=v;function y(o,n){let e=new h(n);return o&&e.setGamepadCallbacks(o),e.start(),e}m(y,"useGamepadInputs");export{b as BaseInputs,N as GamepadInput,h as GamepadInputs,B as InputDeviceType,w as InputEventType,V as Vector2,y as useGamepadInputs};