Skip to main content

goud_engine/ecs/
input_manager.rs

1//! Input management system for ECS integration.
2//!
3//! The `InputManager` resource provides a centralized interface for querying input state
4//! within the ECS. It tracks keyboard keys, mouse buttons, mouse position, and gamepad state
5//! across frames, enabling queries for:
6//! - Current state (is pressed)
7//! - Just pressed (pressed this frame, not last frame)
8//! - Just released (released this frame, was pressed last frame)
9//!
10//! # Architecture
11//!
12//! The InputManager sits between the platform layer (GLFW) and the game systems:
13//!
14//! ```text
15//! GLFW Events → InputHandler → InputManager → Game Systems
16//!                (platform)     (ECS resource)   (queries)
17//! ```
18//!
19//! # Usage
20//!
21//! ## Raw Input Queries
22//!
23//! ```ignore
24//! use goud_engine::ecs::{InputManager, Resource};
25//! use glfw::Key;
26//!
27//! // In your setup system:
28//! world.insert_resource(InputManager::new());
29//!
30//! // In a system:
31//! fn player_movement_system(input: Res<InputManager>) {
32//!     if input.key_pressed(Key::W) {
33//!         // Move forward continuously while held
34//!     }
35//!     if input.key_just_pressed(Key::Space) {
36//!         // Jump only once per press
37//!     }
38//! }
39//! ```
40//!
41//! ## Action Mapping
42//!
43//! Action mapping allows semantic names for input, supporting multiple bindings:
44//!
45//! ```ignore
46//! use goud_engine::ecs::{InputManager, InputBinding};
47//! use glfw::Key;
48//!
49//! let mut input = InputManager::new();
50//!
51//! // Map "Jump" to Space, W key, or gamepad button 0
52//! input.map_action("Jump", InputBinding::Key(Key::Space));
53//! input.map_action("Jump", InputBinding::Key(Key::W));
54//! input.map_action("Jump", InputBinding::GamepadButton { gamepad_id: 0, button: 0 });
55//!
56//! // Query action state (returns true if ANY binding is pressed)
57//! if input.action_pressed("Jump") {
58//!     player.jump();
59//! }
60//!
61//! if input.action_just_pressed("Attack") {
62//!     player.attack();
63//! }
64//! ```
65//!
66//! # Frame Management
67//!
68//! Call `update()` at the start of each frame to advance the input state:
69//!
70//! ```ignore
71//! fn input_update_system(mut input: ResMut<InputManager>) {
72//!     input.update();
73//! }
74//! ```
75
76#[cfg(feature = "native")]
77use glfw::{GamepadAxis, Key, MouseButton};
78use std::collections::{HashMap, HashSet, VecDeque};
79use std::time::{Duration, Instant};
80
81use crate::core::math::Vec2;
82
83/// Gamepad state for a single controller.
84///
85/// Tracks buttons, axes (analog sticks, triggers), connection status, and vibration.
86#[derive(Debug, Clone)]
87struct GamepadState {
88    /// Currently pressed buttons (using GLFW's button indices)
89    buttons: HashSet<u32>,
90    /// Analog axis values (-1.0 to 1.0)
91    axes: HashMap<GamepadAxis, f32>,
92    /// Whether this gamepad is currently connected
93    connected: bool,
94    /// Rumble/vibration intensity (0.0-1.0)
95    vibration: f32,
96}
97
98impl GamepadState {
99    fn new() -> Self {
100        Self {
101            buttons: HashSet::new(),
102            axes: HashMap::new(),
103            connected: false,
104            vibration: 0.0,
105        }
106    }
107
108    #[allow(dead_code)]
109    fn is_empty(&self) -> bool {
110        self.buttons.is_empty() && self.axes.is_empty()
111    }
112}
113
114/// Represents a single input binding that can be mapped to an action.
115///
116/// An action can have multiple bindings, allowing for keyboard, mouse, and gamepad
117/// inputs to all trigger the same action.
118#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
119pub enum InputBinding {
120    /// A keyboard key.
121    Key(Key),
122    /// A mouse button.
123    MouseButton(MouseButton),
124    /// A gamepad button for a specific gamepad.
125    GamepadButton {
126        /// The ID of the gamepad (0-indexed).
127        gamepad_id: usize,
128        /// The button index, following platform conventions.
129        button: u32,
130    },
131}
132
133impl InputBinding {
134    /// Returns true if this binding is currently pressed.
135    pub fn is_pressed(&self, input: &InputManager) -> bool {
136        match self {
137            InputBinding::Key(key) => input.key_pressed(*key),
138            InputBinding::MouseButton(button) => input.mouse_button_pressed(*button),
139            InputBinding::GamepadButton { gamepad_id, button } => {
140                input.gamepad_button_pressed(*gamepad_id, *button)
141            }
142        }
143    }
144
145    /// Returns true if this binding was just pressed this frame.
146    pub fn is_just_pressed(&self, input: &InputManager) -> bool {
147        match self {
148            InputBinding::Key(key) => input.key_just_pressed(*key),
149            InputBinding::MouseButton(button) => input.mouse_button_just_pressed(*button),
150            InputBinding::GamepadButton { gamepad_id, button } => {
151                input.gamepad_button_just_pressed(*gamepad_id, *button)
152            }
153        }
154    }
155
156    /// Returns true if this binding was just released this frame.
157    pub fn is_just_released(&self, input: &InputManager) -> bool {
158        match self {
159            InputBinding::Key(key) => input.key_just_released(*key),
160            InputBinding::MouseButton(button) => input.mouse_button_just_released(*button),
161            InputBinding::GamepadButton { gamepad_id, button } => {
162                input.gamepad_button_just_released(*gamepad_id, *button)
163            }
164        }
165    }
166}
167
168impl std::fmt::Display for InputBinding {
169    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
170        match self {
171            InputBinding::Key(key) => write!(f, "Key({:?})", key),
172            InputBinding::MouseButton(button) => write!(f, "MouseButton({:?})", button),
173            InputBinding::GamepadButton { gamepad_id, button } => {
174                write!(
175                    f,
176                    "GamepadButton(gamepad={}, button={})",
177                    gamepad_id, button
178                )
179            }
180        }
181    }
182}
183
184/// Represents a buffered input event with a timestamp.
185///
186/// Used for detecting input sequences and combos within a time window.
187#[derive(Debug, Clone)]
188struct BufferedInput {
189    /// The input binding that was pressed
190    binding: InputBinding,
191    /// When the input was pressed
192    timestamp: Instant,
193}
194
195impl BufferedInput {
196    /// Creates a new buffered input.
197    fn new(binding: InputBinding, timestamp: Instant) -> Self {
198        Self { binding, timestamp }
199    }
200
201    /// Returns the age of this input in seconds.
202    fn age(&self, now: Instant) -> f32 {
203        now.duration_since(self.timestamp).as_secs_f32()
204    }
205
206    /// Returns true if this input has expired given the buffer duration.
207    fn is_expired(&self, now: Instant, buffer_duration: Duration) -> bool {
208        now.duration_since(self.timestamp) > buffer_duration
209    }
210}
211
212/// Input management resource for ECS integration.
213///
214/// Tracks keyboard, mouse, and gamepad input state across frames,
215/// enabling queries for current state, just pressed, and just released.
216/// Also supports action mapping for semantic input handling.
217#[derive(Debug, Clone)]
218pub struct InputManager {
219    // Current frame state
220    keys_current: HashSet<Key>,
221    mouse_buttons_current: HashSet<MouseButton>,
222    gamepad_buttons_current: Vec<HashSet<u32>>, // gamepad_id -> buttons (deprecated, use gamepads)
223    mouse_position: Vec2,
224    mouse_delta: Vec2,
225
226    // Previous frame state (for just_pressed/just_released detection)
227    keys_previous: HashSet<Key>,
228    mouse_buttons_previous: HashSet<MouseButton>,
229    gamepad_buttons_previous: Vec<HashSet<u32>>, // deprecated, use gamepads_previous
230
231    // Gamepad state (current and previous)
232    gamepads: Vec<GamepadState>,
233    gamepads_previous: Vec<GamepadState>,
234
235    // Mouse scroll
236    scroll_delta: Vec2,
237
238    // Action mappings (action_name -> list of bindings)
239    action_mappings: HashMap<String, Vec<InputBinding>>,
240
241    // Input buffering for sequences and combos
242    input_buffer: VecDeque<BufferedInput>,
243    buffer_duration: Duration,
244    last_update: Instant,
245
246    // Analog deadzone threshold (default 0.1)
247    analog_deadzone: f32,
248}
249
250impl InputManager {
251    /// Creates a new InputManager with no inputs pressed.
252    ///
253    /// Default buffer duration is 200ms, suitable for most combo detection.
254    pub fn new() -> Self {
255        Self::with_buffer_duration(Duration::from_millis(200))
256    }
257
258    /// Creates a new InputManager with custom buffer duration.
259    ///
260    /// The buffer duration determines how long inputs are remembered for sequence detection.
261    /// - Short durations (50-100ms): Strict, requires fast input
262    /// - Medium durations (200-300ms): Balanced, works for most games
263    /// - Long durations (500ms+): Lenient, easier for casual players
264    pub fn with_buffer_duration(buffer_duration: Duration) -> Self {
265        Self {
266            keys_current: HashSet::new(),
267            mouse_buttons_current: HashSet::new(),
268            gamepad_buttons_current: vec![HashSet::new(); 4], // Deprecated, for backward compat
269            mouse_position: Vec2::zero(),
270            mouse_delta: Vec2::zero(),
271            keys_previous: HashSet::new(),
272            mouse_buttons_previous: HashSet::new(),
273            gamepad_buttons_previous: vec![HashSet::new(); 4], // Deprecated
274            gamepads: vec![GamepadState::new(); 4], // Support up to 4 gamepads by default
275            gamepads_previous: vec![GamepadState::new(); 4],
276            scroll_delta: Vec2::zero(),
277            action_mappings: HashMap::new(),
278            input_buffer: VecDeque::with_capacity(32),
279            buffer_duration,
280            last_update: Instant::now(),
281            analog_deadzone: 0.1, // 10% deadzone by default
282        }
283    }
284
285    /// Updates the input state for the next frame.
286    ///
287    /// This should be called at the start of each frame, before any input queries.
288    /// It copies current state to previous state and resets deltas.
289    pub fn update(&mut self) {
290        let now = Instant::now();
291
292        // Copy current to previous
293        self.keys_previous = self.keys_current.clone();
294        self.mouse_buttons_previous = self.mouse_buttons_current.clone();
295        self.gamepad_buttons_previous = self.gamepad_buttons_current.clone();
296        self.gamepads_previous = self.gamepads.clone();
297
298        // Reset deltas
299        self.mouse_delta = Vec2::zero();
300        self.scroll_delta = Vec2::zero();
301
302        // Clean up expired inputs from buffer
303        self.input_buffer
304            .retain(|input| !input.is_expired(now, self.buffer_duration));
305
306        self.last_update = now;
307    }
308
309    // === Keyboard Input ===
310
311    /// Sets a key as pressed.
312    pub fn press_key(&mut self, key: Key) {
313        // Only buffer if this is a new press (not already held)
314        if !self.keys_current.contains(&key) {
315            self.buffer_input(InputBinding::Key(key));
316        }
317        self.keys_current.insert(key);
318    }
319
320    /// Sets a key as released.
321    pub fn release_key(&mut self, key: Key) {
322        self.keys_current.remove(&key);
323    }
324
325    /// Returns true if the key is currently pressed.
326    pub fn key_pressed(&self, key: Key) -> bool {
327        self.keys_current.contains(&key)
328    }
329
330    /// Returns true if the key was just pressed this frame.
331    ///
332    /// True only on the first frame the key is pressed.
333    pub fn key_just_pressed(&self, key: Key) -> bool {
334        self.keys_current.contains(&key) && !self.keys_previous.contains(&key)
335    }
336
337    /// Returns true if the key was just released this frame.
338    ///
339    /// True only on the first frame the key is released.
340    pub fn key_just_released(&self, key: Key) -> bool {
341        !self.keys_current.contains(&key) && self.keys_previous.contains(&key)
342    }
343
344    /// Returns an iterator over all currently pressed keys.
345    pub fn keys_pressed(&self) -> impl Iterator<Item = &Key> {
346        self.keys_current.iter()
347    }
348
349    // === Mouse Input ===
350
351    /// Sets a mouse button as pressed.
352    pub fn press_mouse_button(&mut self, button: MouseButton) {
353        // Only buffer if this is a new press
354        if !self.mouse_buttons_current.contains(&button) {
355            self.buffer_input(InputBinding::MouseButton(button));
356        }
357        self.mouse_buttons_current.insert(button);
358    }
359
360    /// Sets a mouse button as released.
361    pub fn release_mouse_button(&mut self, button: MouseButton) {
362        self.mouse_buttons_current.remove(&button);
363    }
364
365    /// Returns true if the mouse button is currently pressed.
366    pub fn mouse_button_pressed(&self, button: MouseButton) -> bool {
367        self.mouse_buttons_current.contains(&button)
368    }
369
370    /// Returns true if the mouse button was just pressed this frame.
371    pub fn mouse_button_just_pressed(&self, button: MouseButton) -> bool {
372        self.mouse_buttons_current.contains(&button)
373            && !self.mouse_buttons_previous.contains(&button)
374    }
375
376    /// Returns true if the mouse button was just released this frame.
377    pub fn mouse_button_just_released(&self, button: MouseButton) -> bool {
378        !self.mouse_buttons_current.contains(&button)
379            && self.mouse_buttons_previous.contains(&button)
380    }
381
382    /// Returns an iterator over all currently pressed mouse buttons.
383    pub fn mouse_buttons_pressed(&self) -> impl Iterator<Item = &MouseButton> {
384        self.mouse_buttons_current.iter()
385    }
386
387    // === Mouse Position ===
388
389    /// Updates the mouse position.
390    pub fn set_mouse_position(&mut self, position: Vec2) {
391        self.mouse_delta = position - self.mouse_position;
392        self.mouse_position = position;
393    }
394
395    /// Returns the current mouse position in screen coordinates.
396    pub fn mouse_position(&self) -> Vec2 {
397        self.mouse_position
398    }
399
400    /// Returns the mouse movement delta since last frame.
401    pub fn mouse_delta(&self) -> Vec2 {
402        self.mouse_delta
403    }
404
405    // === Mouse Scroll ===
406
407    /// Updates the mouse scroll delta.
408    pub fn add_scroll_delta(&mut self, delta: Vec2) {
409        self.scroll_delta = self.scroll_delta + delta;
410    }
411
412    /// Returns the mouse scroll delta for this frame.
413    pub fn scroll_delta(&self) -> Vec2 {
414        self.scroll_delta
415    }
416
417    // === Gamepad Input ===
418
419    /// Sets a gamepad button as pressed.
420    ///
421    /// # Panics
422    /// Panics if gamepad_id >= 4.
423    pub fn press_gamepad_button(&mut self, gamepad_id: usize, button: u32) {
424        self.ensure_gamepad_capacity(gamepad_id);
425
426        // Only buffer if this is a new press
427        let is_new = !self.gamepad_buttons_current[gamepad_id].contains(&button);
428        if is_new {
429            self.buffer_input(InputBinding::GamepadButton { gamepad_id, button });
430        }
431
432        self.gamepad_buttons_current[gamepad_id].insert(button);
433    }
434
435    /// Sets a gamepad button as released.
436    ///
437    /// # Panics
438    /// Panics if gamepad_id >= 4.
439    pub fn release_gamepad_button(&mut self, gamepad_id: usize, button: u32) {
440        self.ensure_gamepad_capacity(gamepad_id);
441        self.gamepad_buttons_current[gamepad_id].remove(&button);
442    }
443
444    /// Returns true if the gamepad button is currently pressed.
445    ///
446    /// Returns false if gamepad_id is invalid.
447    pub fn gamepad_button_pressed(&self, gamepad_id: usize, button: u32) -> bool {
448        self.gamepad_buttons_current
449            .get(gamepad_id)
450            .is_some_and(|buttons| buttons.contains(&button))
451    }
452
453    /// Returns true if the gamepad button was just pressed this frame.
454    pub fn gamepad_button_just_pressed(&self, gamepad_id: usize, button: u32) -> bool {
455        let current = self
456            .gamepad_buttons_current
457            .get(gamepad_id)
458            .is_some_and(|buttons| buttons.contains(&button));
459        let previous = self
460            .gamepad_buttons_previous
461            .get(gamepad_id)
462            .is_some_and(|buttons| buttons.contains(&button));
463        current && !previous
464    }
465
466    /// Returns true if the gamepad button was just released this frame.
467    pub fn gamepad_button_just_released(&self, gamepad_id: usize, button: u32) -> bool {
468        let current = self
469            .gamepad_buttons_current
470            .get(gamepad_id)
471            .is_some_and(|buttons| buttons.contains(&button));
472        let previous = self
473            .gamepad_buttons_previous
474            .get(gamepad_id)
475            .is_some_and(|buttons| buttons.contains(&button));
476        !current && previous
477    }
478
479    /// Ensures gamepad capacity for the given ID.
480    fn ensure_gamepad_capacity(&mut self, gamepad_id: usize) {
481        while self.gamepad_buttons_current.len() <= gamepad_id {
482            self.gamepad_buttons_current.push(HashSet::new());
483        }
484        while self.gamepad_buttons_previous.len() <= gamepad_id {
485            self.gamepad_buttons_previous.push(HashSet::new());
486        }
487        while self.gamepads.len() <= gamepad_id {
488            self.gamepads.push(GamepadState::new());
489        }
490        while self.gamepads_previous.len() <= gamepad_id {
491            self.gamepads_previous.push(GamepadState::new());
492        }
493    }
494
495    // === Gamepad Analog Axes ===
496
497    /// Sets a gamepad analog axis value (-1.0 to 1.0).
498    ///
499    /// Values within the deadzone threshold are clamped to 0.0.
500    pub fn set_gamepad_axis(&mut self, gamepad_id: usize, axis: GamepadAxis, value: f32) {
501        self.ensure_gamepad_capacity(gamepad_id);
502
503        // Apply deadzone
504        let deadzone_value = if value.abs() < self.analog_deadzone {
505            0.0
506        } else {
507            value
508        };
509
510        self.gamepads[gamepad_id].axes.insert(axis, deadzone_value);
511    }
512
513    /// Returns the current value of a gamepad analog axis (-1.0 to 1.0).
514    ///
515    /// Returns 0.0 if the gamepad or axis doesn't exist.
516    pub fn gamepad_axis(&self, gamepad_id: usize, axis: GamepadAxis) -> f32 {
517        self.gamepads
518            .get(gamepad_id)
519            .and_then(|gamepad| gamepad.axes.get(&axis).copied())
520            .unwrap_or(0.0)
521    }
522
523    /// Returns the left stick as a Vec2 (x, y) where -1.0 is left/down and 1.0 is right/up.
524    ///
525    /// Returns Vec2::zero() if the gamepad doesn't exist.
526    pub fn gamepad_left_stick(&self, gamepad_id: usize) -> Vec2 {
527        Vec2::new(
528            self.gamepad_axis(gamepad_id, GamepadAxis::AxisLeftX),
529            self.gamepad_axis(gamepad_id, GamepadAxis::AxisLeftY),
530        )
531    }
532
533    /// Returns the right stick as a Vec2 (x, y) where -1.0 is left/down and 1.0 is right/up.
534    ///
535    /// Returns Vec2::zero() if the gamepad doesn't exist.
536    pub fn gamepad_right_stick(&self, gamepad_id: usize) -> Vec2 {
537        Vec2::new(
538            self.gamepad_axis(gamepad_id, GamepadAxis::AxisRightX),
539            self.gamepad_axis(gamepad_id, GamepadAxis::AxisRightY),
540        )
541    }
542
543    /// Returns the left trigger value (0.0 to 1.0).
544    ///
545    /// Returns 0.0 if the gamepad doesn't exist.
546    pub fn gamepad_left_trigger(&self, gamepad_id: usize) -> f32 {
547        // Left trigger is mapped to axis 2, normalized from -1.0..1.0 to 0.0..1.0
548        (self.gamepad_axis(gamepad_id, GamepadAxis::AxisLeftTrigger) + 1.0) * 0.5
549    }
550
551    /// Returns the right trigger value (0.0 to 1.0).
552    ///
553    /// Returns 0.0 if the gamepad doesn't exist.
554    pub fn gamepad_right_trigger(&self, gamepad_id: usize) -> f32 {
555        // Right trigger is mapped to axis 3, normalized from -1.0..1.0 to 0.0..1.0
556        (self.gamepad_axis(gamepad_id, GamepadAxis::AxisRightTrigger) + 1.0) * 0.5
557    }
558
559    // === Gamepad Connection ===
560
561    /// Sets the connection status of a gamepad.
562    pub fn set_gamepad_connected(&mut self, gamepad_id: usize, connected: bool) {
563        self.ensure_gamepad_capacity(gamepad_id);
564        self.gamepads[gamepad_id].connected = connected;
565    }
566
567    /// Returns true if the gamepad is currently connected.
568    pub fn is_gamepad_connected(&self, gamepad_id: usize) -> bool {
569        self.gamepads
570            .get(gamepad_id)
571            .map(|gamepad| gamepad.connected)
572            .unwrap_or(false)
573    }
574
575    /// Returns the number of connected gamepads.
576    pub fn connected_gamepad_count(&self) -> usize {
577        self.gamepads
578            .iter()
579            .filter(|gamepad| gamepad.connected)
580            .count()
581    }
582
583    /// Returns an iterator over all connected gamepad IDs.
584    pub fn connected_gamepads(&self) -> impl Iterator<Item = usize> + '_ {
585        self.gamepads
586            .iter()
587            .enumerate()
588            .filter_map(|(id, gamepad)| if gamepad.connected { Some(id) } else { None })
589    }
590
591    // === Gamepad Vibration ===
592
593    /// Sets the vibration intensity for a gamepad (0.0-1.0).
594    ///
595    /// Note: Actual vibration must be implemented by the platform layer.
596    /// This only tracks the requested vibration intensity.
597    pub fn set_gamepad_vibration(&mut self, gamepad_id: usize, intensity: f32) {
598        self.ensure_gamepad_capacity(gamepad_id);
599        self.gamepads[gamepad_id].vibration = intensity.clamp(0.0, 1.0);
600    }
601
602    /// Returns the current vibration intensity for a gamepad (0.0-1.0).
603    pub fn gamepad_vibration(&self, gamepad_id: usize) -> f32 {
604        self.gamepads
605            .get(gamepad_id)
606            .map(|gamepad| gamepad.vibration)
607            .unwrap_or(0.0)
608    }
609
610    /// Stops vibration for a gamepad (sets intensity to 0.0).
611    pub fn stop_gamepad_vibration(&mut self, gamepad_id: usize) {
612        self.set_gamepad_vibration(gamepad_id, 0.0);
613    }
614
615    /// Stops vibration for all gamepads.
616    pub fn stop_all_vibration(&mut self) {
617        for gamepad in &mut self.gamepads {
618            gamepad.vibration = 0.0;
619        }
620    }
621
622    // === Analog Deadzone ===
623
624    /// Returns the current analog deadzone threshold (0.0-1.0).
625    ///
626    /// Default is 0.1 (10%).
627    pub fn analog_deadzone(&self) -> f32 {
628        self.analog_deadzone
629    }
630
631    /// Sets the analog deadzone threshold (0.0-1.0).
632    ///
633    /// Analog axis values within this threshold are clamped to 0.0.
634    /// This prevents stick drift and accidental input.
635    pub fn set_analog_deadzone(&mut self, deadzone: f32) {
636        self.analog_deadzone = deadzone.clamp(0.0, 1.0);
637    }
638
639    // === Input Buffering ===
640
641    /// Adds an input to the buffer for sequence detection.
642    fn buffer_input(&mut self, binding: InputBinding) {
643        let now = Instant::now();
644        self.input_buffer
645            .push_back(BufferedInput::new(binding, now));
646
647        // Keep buffer size reasonable (prevent memory growth from rapid inputs)
648        while self.input_buffer.len() > 32 {
649            self.input_buffer.pop_front();
650        }
651    }
652
653    /// Returns the current buffer duration.
654    pub fn buffer_duration(&self) -> Duration {
655        self.buffer_duration
656    }
657
658    /// Sets the buffer duration for input sequences.
659    ///
660    /// This determines how long inputs are remembered for combo detection.
661    pub fn set_buffer_duration(&mut self, duration: Duration) {
662        self.buffer_duration = duration;
663    }
664
665    /// Returns the number of inputs currently in the buffer.
666    pub fn buffer_size(&self) -> usize {
667        self.input_buffer.len()
668    }
669
670    /// Clears the input buffer.
671    ///
672    /// Useful when resetting combos or canceling sequences.
673    pub fn clear_buffer(&mut self) {
674        self.input_buffer.clear();
675    }
676
677    /// Checks if a sequence of inputs was pressed within the buffer duration.
678    ///
679    /// Returns true if all bindings in the sequence were pressed in order,
680    /// with each subsequent input occurring within the buffer window.
681    ///
682    /// # Example
683    ///
684    /// ```ignore
685    /// use goud_engine::ecs::{InputManager, InputBinding};
686    /// use glfw::Key;
687    ///
688    /// let mut input = InputManager::new();
689    ///
690    /// // Detect "Down, Down, Forward, Punch" combo (fighting game)
691    /// let combo = vec![
692    ///     InputBinding::Key(Key::Down),
693    ///     InputBinding::Key(Key::Down),
694    ///     InputBinding::Key(Key::Right),
695    ///     InputBinding::Key(Key::Space),
696    /// ];
697    ///
698    /// if input.sequence_detected(&combo) {
699    ///     player.perform_special_move();
700    /// }
701    /// ```
702    pub fn sequence_detected(&self, sequence: &[InputBinding]) -> bool {
703        if sequence.is_empty() || self.input_buffer.is_empty() {
704            return false;
705        }
706
707        let now = Instant::now();
708        let mut seq_index = 0;
709
710        // Scan buffer from oldest to newest
711        for buffered in &self.input_buffer {
712            // Skip expired inputs
713            if buffered.is_expired(now, self.buffer_duration) {
714                continue;
715            }
716
717            // Check if this matches the next input in sequence
718            if buffered.binding == sequence[seq_index] {
719                seq_index += 1;
720
721                // Entire sequence matched
722                if seq_index == sequence.len() {
723                    return true;
724                }
725            }
726        }
727
728        false
729    }
730
731    /// Checks if a sequence was pressed and clears the buffer if detected.
732    ///
733    /// This is useful for consuming combos so they don't trigger multiple times.
734    ///
735    /// Returns true if the sequence was detected and consumed.
736    pub fn consume_sequence(&mut self, sequence: &[InputBinding]) -> bool {
737        if self.sequence_detected(sequence) {
738            self.clear_buffer();
739            true
740        } else {
741            false
742        }
743    }
744
745    /// Returns the time since the last buffered input in seconds.
746    ///
747    /// Returns None if the buffer is empty.
748    pub fn time_since_last_input(&self) -> Option<f32> {
749        self.input_buffer
750            .back()
751            .map(|input| input.age(Instant::now()))
752    }
753
754    /// Returns all inputs in the buffer (oldest to newest).
755    ///
756    /// Useful for debugging or visualizing input history.
757    pub fn buffered_inputs(&self) -> impl Iterator<Item = (InputBinding, f32)> + '_ {
758        let now = Instant::now();
759        self.input_buffer
760            .iter()
761            .map(move |input| (input.binding, input.age(now)))
762    }
763
764    /// Clears all input state (useful for focus loss or pausing).
765    pub fn clear(&mut self) {
766        self.keys_current.clear();
767        self.keys_previous.clear();
768        self.mouse_buttons_current.clear();
769        self.mouse_buttons_previous.clear();
770        for buttons in &mut self.gamepad_buttons_current {
771            buttons.clear();
772        }
773        for buttons in &mut self.gamepad_buttons_previous {
774            buttons.clear();
775        }
776        // Clear new gamepad state (buttons and axes)
777        for gamepad in &mut self.gamepads {
778            gamepad.buttons.clear();
779            gamepad.axes.clear();
780            // Don't clear connection status or vibration
781        }
782        for gamepad in &mut self.gamepads_previous {
783            gamepad.buttons.clear();
784            gamepad.axes.clear();
785        }
786        self.mouse_delta = Vec2::zero();
787        self.scroll_delta = Vec2::zero();
788    }
789
790    // === Action Mapping ===
791
792    /// Maps an input binding to an action.
793    ///
794    /// An action can have multiple bindings. If the action already exists,
795    /// the binding is added to its list. Duplicate bindings are allowed.
796    ///
797    /// # Example
798    ///
799    /// ```ignore
800    /// use goud_engine::ecs::{InputManager, InputBinding};
801    /// use glfw::Key;
802    ///
803    /// let mut input = InputManager::new();
804    /// input.map_action("Jump", InputBinding::Key(Key::Space));
805    /// input.map_action("Jump", InputBinding::Key(Key::W)); // Alternative binding
806    /// ```
807    pub fn map_action(&mut self, action: impl Into<String>, binding: InputBinding) {
808        self.action_mappings
809            .entry(action.into())
810            .or_default()
811            .push(binding);
812    }
813
814    /// Unmaps a specific input binding from an action.
815    ///
816    /// Returns true if the binding was removed, false if it wasn't found.
817    pub fn unmap_action(&mut self, action: &str, binding: InputBinding) -> bool {
818        if let Some(bindings) = self.action_mappings.get_mut(action) {
819            if let Some(pos) = bindings.iter().position(|b| *b == binding) {
820                bindings.remove(pos);
821                return true;
822            }
823        }
824        false
825    }
826
827    /// Removes all bindings for an action.
828    ///
829    /// Returns true if the action existed and was removed.
830    pub fn clear_action(&mut self, action: &str) -> bool {
831        self.action_mappings.remove(action).is_some()
832    }
833
834    /// Removes all action mappings.
835    pub fn clear_all_actions(&mut self) {
836        self.action_mappings.clear();
837    }
838
839    /// Returns all bindings for an action.
840    ///
841    /// Returns an empty slice if the action doesn't exist.
842    pub fn get_action_bindings(&self, action: &str) -> &[InputBinding] {
843        self.action_mappings
844            .get(action)
845            .map(|v| v.as_slice())
846            .unwrap_or(&[])
847    }
848
849    /// Returns true if the action has any bindings.
850    pub fn has_action(&self, action: &str) -> bool {
851        self.action_mappings.contains_key(action)
852    }
853
854    /// Returns an iterator over all action names.
855    pub fn action_names(&self) -> impl Iterator<Item = &str> {
856        self.action_mappings.keys().map(|s| s.as_str())
857    }
858
859    /// Returns the number of registered actions.
860    pub fn action_count(&self) -> usize {
861        self.action_mappings.len()
862    }
863
864    /// Returns true if ANY binding for the action is currently pressed.
865    ///
866    /// Returns false if the action doesn't exist or has no bindings.
867    pub fn action_pressed(&self, action: &str) -> bool {
868        self.action_mappings
869            .get(action)
870            .is_some_and(|bindings| bindings.iter().any(|b| b.is_pressed(self)))
871    }
872
873    /// Returns true if ANY binding for the action was just pressed this frame.
874    ///
875    /// Returns false if the action doesn't exist or has no bindings.
876    pub fn action_just_pressed(&self, action: &str) -> bool {
877        self.action_mappings
878            .get(action)
879            .is_some_and(|bindings| bindings.iter().any(|b| b.is_just_pressed(self)))
880    }
881
882    /// Returns true if ANY binding for the action was just released this frame.
883    ///
884    /// Returns false if the action doesn't exist or has no bindings.
885    pub fn action_just_released(&self, action: &str) -> bool {
886        self.action_mappings
887            .get(action)
888            .is_some_and(|bindings| bindings.iter().any(|b| b.is_just_released(self)))
889    }
890
891    /// Returns the strength of the action (0.0-1.0).
892    ///
893    /// For digital inputs (keys, buttons), this returns 1.0 if pressed, 0.0 otherwise.
894    /// This method exists for future analog input support (triggers, analog sticks).
895    pub fn action_strength(&self, action: &str) -> f32 {
896        if self.action_pressed(action) {
897            1.0
898        } else {
899            0.0
900        }
901    }
902}
903
904impl Default for InputManager {
905    fn default() -> Self {
906        Self::new()
907    }
908}
909
910#[cfg(test)]
911mod tests {
912    use super::*;
913
914    #[test]
915    fn test_new_input_manager() {
916        let input = InputManager::new();
917        assert!(!input.key_pressed(Key::A));
918        assert!(!input.mouse_button_pressed(MouseButton::Button1));
919        assert_eq!(input.mouse_position(), Vec2::zero());
920        assert_eq!(input.mouse_delta(), Vec2::zero());
921    }
922
923    #[test]
924    fn test_default() {
925        let input = InputManager::default();
926        assert!(!input.key_pressed(Key::W));
927    }
928
929    // === Keyboard Tests ===
930
931    #[test]
932    fn test_key_pressed() {
933        let mut input = InputManager::new();
934        assert!(!input.key_pressed(Key::A));
935
936        input.press_key(Key::A);
937        assert!(input.key_pressed(Key::A));
938
939        input.release_key(Key::A);
940        assert!(!input.key_pressed(Key::A));
941    }
942
943    #[test]
944    fn test_key_just_pressed() {
945        let mut input = InputManager::new();
946
947        // Press key
948        input.press_key(Key::Space);
949        assert!(input.key_just_pressed(Key::Space)); // First frame
950
951        // Update to next frame
952        input.update();
953        assert!(!input.key_just_pressed(Key::Space)); // Still held, but not "just" pressed
954
955        // Release and press again
956        input.release_key(Key::Space);
957        input.update();
958        input.press_key(Key::Space);
959        assert!(input.key_just_pressed(Key::Space)); // Just pressed again
960    }
961
962    #[test]
963    fn test_key_just_released() {
964        let mut input = InputManager::new();
965
966        // Press key
967        input.press_key(Key::W);
968        input.update(); // Make it "previous"
969
970        // Release key
971        input.release_key(Key::W);
972        assert!(input.key_just_released(Key::W));
973
974        // Update to next frame
975        input.update();
976        assert!(!input.key_just_released(Key::W)); // No longer "just" released
977    }
978
979    #[test]
980    fn test_keys_pressed_iterator() {
981        let mut input = InputManager::new();
982        input.press_key(Key::A);
983        input.press_key(Key::B);
984        input.press_key(Key::C);
985
986        let pressed_keys: Vec<_> = input.keys_pressed().collect();
987        assert_eq!(pressed_keys.len(), 3);
988        assert!(pressed_keys.contains(&&Key::A));
989        assert!(pressed_keys.contains(&&Key::B));
990        assert!(pressed_keys.contains(&&Key::C));
991    }
992
993    // === Mouse Button Tests ===
994
995    #[test]
996    fn test_mouse_button_pressed() {
997        let mut input = InputManager::new();
998        assert!(!input.mouse_button_pressed(MouseButton::Button1));
999
1000        input.press_mouse_button(MouseButton::Button1);
1001        assert!(input.mouse_button_pressed(MouseButton::Button1));
1002
1003        input.release_mouse_button(MouseButton::Button1);
1004        assert!(!input.mouse_button_pressed(MouseButton::Button1));
1005    }
1006
1007    #[test]
1008    fn test_mouse_button_just_pressed() {
1009        let mut input = InputManager::new();
1010
1011        input.press_mouse_button(MouseButton::Button2);
1012        assert!(input.mouse_button_just_pressed(MouseButton::Button2));
1013
1014        input.update();
1015        assert!(!input.mouse_button_just_pressed(MouseButton::Button2));
1016    }
1017
1018    #[test]
1019    fn test_mouse_button_just_released() {
1020        let mut input = InputManager::new();
1021
1022        input.press_mouse_button(MouseButton::Button1);
1023        input.update();
1024        input.release_mouse_button(MouseButton::Button1);
1025        assert!(input.mouse_button_just_released(MouseButton::Button1));
1026
1027        input.update();
1028        assert!(!input.mouse_button_just_released(MouseButton::Button1));
1029    }
1030
1031    #[test]
1032    fn test_mouse_buttons_pressed_iterator() {
1033        let mut input = InputManager::new();
1034        input.press_mouse_button(MouseButton::Button1);
1035        input.press_mouse_button(MouseButton::Button2);
1036
1037        let pressed_buttons: Vec<_> = input.mouse_buttons_pressed().collect();
1038        assert_eq!(pressed_buttons.len(), 2);
1039    }
1040
1041    // === Mouse Position Tests ===
1042
1043    #[test]
1044    fn test_mouse_position() {
1045        let mut input = InputManager::new();
1046        assert_eq!(input.mouse_position(), Vec2::zero());
1047
1048        let pos = Vec2::new(100.0, 200.0);
1049        input.set_mouse_position(pos);
1050        assert_eq!(input.mouse_position(), pos);
1051    }
1052
1053    #[test]
1054    fn test_mouse_delta() {
1055        let mut input = InputManager::new();
1056
1057        // First position
1058        input.set_mouse_position(Vec2::new(100.0, 100.0));
1059        assert_eq!(input.mouse_delta(), Vec2::new(100.0, 100.0)); // Delta from (0,0)
1060
1061        // Second position
1062        input.set_mouse_position(Vec2::new(150.0, 120.0));
1063        assert_eq!(input.mouse_delta(), Vec2::new(50.0, 20.0)); // Delta from previous
1064    }
1065
1066    #[test]
1067    fn test_mouse_delta_reset_on_update() {
1068        let mut input = InputManager::new();
1069
1070        input.set_mouse_position(Vec2::new(100.0, 100.0));
1071        assert_ne!(input.mouse_delta(), Vec2::zero());
1072
1073        input.update(); // Reset delta
1074        assert_eq!(input.mouse_delta(), Vec2::zero());
1075    }
1076
1077    // === Scroll Tests ===
1078
1079    #[test]
1080    fn test_scroll_delta() {
1081        let mut input = InputManager::new();
1082        assert_eq!(input.scroll_delta(), Vec2::zero());
1083
1084        input.add_scroll_delta(Vec2::new(0.0, 1.0));
1085        assert_eq!(input.scroll_delta(), Vec2::new(0.0, 1.0));
1086
1087        input.add_scroll_delta(Vec2::new(0.0, 2.0));
1088        assert_eq!(input.scroll_delta(), Vec2::new(0.0, 3.0)); // Accumulates
1089    }
1090
1091    #[test]
1092    fn test_scroll_delta_reset_on_update() {
1093        let mut input = InputManager::new();
1094
1095        input.add_scroll_delta(Vec2::new(0.0, 5.0));
1096        input.update();
1097        assert_eq!(input.scroll_delta(), Vec2::zero());
1098    }
1099
1100    // === Gamepad Tests ===
1101
1102    #[test]
1103    fn test_gamepad_button_pressed() {
1104        let mut input = InputManager::new();
1105
1106        input.press_gamepad_button(0, 1);
1107        assert!(input.gamepad_button_pressed(0, 1));
1108        assert!(!input.gamepad_button_pressed(0, 2));
1109        assert!(!input.gamepad_button_pressed(1, 1)); // Different gamepad
1110
1111        input.release_gamepad_button(0, 1);
1112        assert!(!input.gamepad_button_pressed(0, 1));
1113    }
1114
1115    #[test]
1116    fn test_gamepad_button_just_pressed() {
1117        let mut input = InputManager::new();
1118
1119        input.press_gamepad_button(0, 5);
1120        assert!(input.gamepad_button_just_pressed(0, 5));
1121
1122        input.update();
1123        assert!(!input.gamepad_button_just_pressed(0, 5));
1124    }
1125
1126    #[test]
1127    fn test_gamepad_button_just_released() {
1128        let mut input = InputManager::new();
1129
1130        input.press_gamepad_button(1, 3);
1131        input.update();
1132        input.release_gamepad_button(1, 3);
1133        assert!(input.gamepad_button_just_released(1, 3));
1134
1135        input.update();
1136        assert!(!input.gamepad_button_just_released(1, 3));
1137    }
1138
1139    #[test]
1140    fn test_gamepad_multiple_gamepads() {
1141        let mut input = InputManager::new();
1142
1143        input.press_gamepad_button(0, 1);
1144        input.press_gamepad_button(1, 1);
1145        input.press_gamepad_button(2, 2);
1146
1147        assert!(input.gamepad_button_pressed(0, 1));
1148        assert!(input.gamepad_button_pressed(1, 1));
1149        assert!(input.gamepad_button_pressed(2, 2));
1150        assert!(!input.gamepad_button_pressed(2, 1));
1151    }
1152
1153    #[test]
1154    fn test_gamepad_capacity_expansion() {
1155        let mut input = InputManager::new();
1156
1157        // Should expand to support gamepad 5
1158        input.press_gamepad_button(5, 1);
1159        assert!(input.gamepad_button_pressed(5, 1));
1160    }
1161
1162    // === Clear Tests ===
1163
1164    #[test]
1165    fn test_clear() {
1166        let mut input = InputManager::new();
1167
1168        // Set various inputs
1169        input.press_key(Key::A);
1170        input.press_mouse_button(MouseButton::Button1);
1171        input.set_mouse_position(Vec2::new(100.0, 100.0));
1172        input.add_scroll_delta(Vec2::new(0.0, 1.0));
1173        input.press_gamepad_button(0, 5);
1174
1175        // Clear all
1176        input.clear();
1177
1178        // Verify all cleared
1179        assert!(!input.key_pressed(Key::A));
1180        assert!(!input.mouse_button_pressed(MouseButton::Button1));
1181        assert_eq!(input.mouse_delta(), Vec2::zero());
1182        assert_eq!(input.scroll_delta(), Vec2::zero());
1183        assert!(!input.gamepad_button_pressed(0, 5));
1184    }
1185
1186    // === Update Tests ===
1187
1188    #[test]
1189    fn test_update_copies_state() {
1190        let mut input = InputManager::new();
1191
1192        input.press_key(Key::Space);
1193        assert!(input.key_just_pressed(Key::Space));
1194
1195        input.update();
1196        assert!(!input.key_just_pressed(Key::Space)); // No longer "just" pressed
1197        assert!(input.key_pressed(Key::Space)); // Still pressed
1198    }
1199
1200    #[test]
1201    fn test_clone() {
1202        let mut input = InputManager::new();
1203        input.press_key(Key::A);
1204
1205        let cloned = input.clone();
1206        assert!(cloned.key_pressed(Key::A));
1207    }
1208
1209    #[test]
1210    fn test_debug() {
1211        let input = InputManager::new();
1212        let debug_str = format!("{:?}", input);
1213        assert!(debug_str.contains("InputManager"));
1214    }
1215
1216    // === InputBinding Tests ===
1217
1218    #[test]
1219    fn test_input_binding_key() {
1220        let mut input = InputManager::new();
1221        let binding = InputBinding::Key(Key::A);
1222
1223        assert!(!binding.is_pressed(&input));
1224        assert!(!binding.is_just_pressed(&input));
1225
1226        input.press_key(Key::A);
1227        assert!(binding.is_pressed(&input));
1228        assert!(binding.is_just_pressed(&input));
1229
1230        input.update();
1231        assert!(binding.is_pressed(&input));
1232        assert!(!binding.is_just_pressed(&input));
1233
1234        input.release_key(Key::A);
1235        assert!(!binding.is_pressed(&input));
1236        assert!(binding.is_just_released(&input));
1237    }
1238
1239    #[test]
1240    fn test_input_binding_mouse_button() {
1241        let mut input = InputManager::new();
1242        let binding = InputBinding::MouseButton(MouseButton::Button1);
1243
1244        assert!(!binding.is_pressed(&input));
1245
1246        input.press_mouse_button(MouseButton::Button1);
1247        assert!(binding.is_pressed(&input));
1248        assert!(binding.is_just_pressed(&input));
1249    }
1250
1251    #[test]
1252    fn test_input_binding_gamepad_button() {
1253        let mut input = InputManager::new();
1254        let binding = InputBinding::GamepadButton {
1255            gamepad_id: 0,
1256            button: 5,
1257        };
1258
1259        assert!(!binding.is_pressed(&input));
1260
1261        input.press_gamepad_button(0, 5);
1262        assert!(binding.is_pressed(&input));
1263        assert!(binding.is_just_pressed(&input));
1264    }
1265
1266    #[test]
1267    fn test_input_binding_display() {
1268        let key_binding = InputBinding::Key(Key::Space);
1269        let mouse_binding = InputBinding::MouseButton(MouseButton::Button1);
1270        let gamepad_binding = InputBinding::GamepadButton {
1271            gamepad_id: 2,
1272            button: 10,
1273        };
1274
1275        let key_str = format!("{}", key_binding);
1276        let mouse_str = format!("{}", mouse_binding);
1277        let gamepad_str = format!("{}", gamepad_binding);
1278
1279        assert!(key_str.contains("Key"));
1280        assert!(mouse_str.contains("MouseButton"));
1281        assert!(gamepad_str.contains("GamepadButton"));
1282        assert!(gamepad_str.contains("gamepad=2"));
1283        assert!(gamepad_str.contains("button=10"));
1284    }
1285
1286    #[test]
1287    fn test_input_binding_eq() {
1288        let binding1 = InputBinding::Key(Key::A);
1289        let binding2 = InputBinding::Key(Key::A);
1290        let binding3 = InputBinding::Key(Key::B);
1291
1292        assert_eq!(binding1, binding2);
1293        assert_ne!(binding1, binding3);
1294    }
1295
1296    #[test]
1297    fn test_input_binding_hash() {
1298        use std::collections::HashSet;
1299
1300        let mut set = HashSet::new();
1301        set.insert(InputBinding::Key(Key::A));
1302        set.insert(InputBinding::Key(Key::A)); // Duplicate
1303        set.insert(InputBinding::Key(Key::B));
1304
1305        assert_eq!(set.len(), 2); // Duplicate not added
1306    }
1307
1308    // === Action Mapping Tests ===
1309
1310    #[test]
1311    fn test_map_action_basic() {
1312        let mut input = InputManager::new();
1313
1314        input.map_action("Jump", InputBinding::Key(Key::Space));
1315        assert!(input.has_action("Jump"));
1316        assert_eq!(input.get_action_bindings("Jump").len(), 1);
1317    }
1318
1319    #[test]
1320    fn test_map_action_multiple_bindings() {
1321        let mut input = InputManager::new();
1322
1323        input.map_action("Jump", InputBinding::Key(Key::Space));
1324        input.map_action("Jump", InputBinding::Key(Key::W));
1325        input.map_action(
1326            "Jump",
1327            InputBinding::GamepadButton {
1328                gamepad_id: 0,
1329                button: 0,
1330            },
1331        );
1332
1333        let bindings = input.get_action_bindings("Jump");
1334        assert_eq!(bindings.len(), 3);
1335    }
1336
1337    #[test]
1338    fn test_unmap_action() {
1339        let mut input = InputManager::new();
1340        let binding = InputBinding::Key(Key::Space);
1341
1342        input.map_action("Jump", binding);
1343        assert_eq!(input.get_action_bindings("Jump").len(), 1);
1344
1345        assert!(input.unmap_action("Jump", binding));
1346        assert_eq!(input.get_action_bindings("Jump").len(), 0);
1347
1348        // Unmapping non-existent binding returns false
1349        assert!(!input.unmap_action("Jump", binding));
1350    }
1351
1352    #[test]
1353    fn test_clear_action() {
1354        let mut input = InputManager::new();
1355
1356        input.map_action("Jump", InputBinding::Key(Key::Space));
1357        input.map_action("Jump", InputBinding::Key(Key::W));
1358
1359        assert!(input.clear_action("Jump"));
1360        assert!(!input.has_action("Jump"));
1361
1362        // Clearing non-existent action returns false
1363        assert!(!input.clear_action("Jump"));
1364    }
1365
1366    #[test]
1367    fn test_clear_all_actions() {
1368        let mut input = InputManager::new();
1369
1370        input.map_action("Jump", InputBinding::Key(Key::Space));
1371        input.map_action("Attack", InputBinding::Key(Key::E));
1372        input.map_action("Defend", InputBinding::Key(Key::Q));
1373
1374        assert_eq!(input.action_count(), 3);
1375
1376        input.clear_all_actions();
1377        assert_eq!(input.action_count(), 0);
1378        assert!(!input.has_action("Jump"));
1379        assert!(!input.has_action("Attack"));
1380        assert!(!input.has_action("Defend"));
1381    }
1382
1383    #[test]
1384    fn test_get_action_bindings_nonexistent() {
1385        let input = InputManager::new();
1386        let bindings = input.get_action_bindings("NonExistent");
1387        assert_eq!(bindings.len(), 0);
1388    }
1389
1390    #[test]
1391    fn test_has_action() {
1392        let mut input = InputManager::new();
1393
1394        assert!(!input.has_action("Jump"));
1395
1396        input.map_action("Jump", InputBinding::Key(Key::Space));
1397        assert!(input.has_action("Jump"));
1398    }
1399
1400    #[test]
1401    fn test_action_names() {
1402        let mut input = InputManager::new();
1403
1404        input.map_action("Jump", InputBinding::Key(Key::Space));
1405        input.map_action("Attack", InputBinding::Key(Key::E));
1406
1407        let names: Vec<_> = input.action_names().collect();
1408        assert_eq!(names.len(), 2);
1409        assert!(names.contains(&"Jump"));
1410        assert!(names.contains(&"Attack"));
1411    }
1412
1413    #[test]
1414    fn test_action_count() {
1415        let mut input = InputManager::new();
1416
1417        assert_eq!(input.action_count(), 0);
1418
1419        input.map_action("Jump", InputBinding::Key(Key::Space));
1420        assert_eq!(input.action_count(), 1);
1421
1422        input.map_action("Attack", InputBinding::Key(Key::E));
1423        assert_eq!(input.action_count(), 2);
1424    }
1425
1426    #[test]
1427    fn test_action_pressed() {
1428        let mut input = InputManager::new();
1429
1430        input.map_action("Jump", InputBinding::Key(Key::Space));
1431        input.map_action("Jump", InputBinding::Key(Key::W));
1432
1433        // No keys pressed
1434        assert!(!input.action_pressed("Jump"));
1435
1436        // One key pressed
1437        input.press_key(Key::Space);
1438        assert!(input.action_pressed("Jump"));
1439
1440        // Other key pressed
1441        input.release_key(Key::Space);
1442        input.press_key(Key::W);
1443        assert!(input.action_pressed("Jump"));
1444
1445        // Both keys pressed
1446        input.press_key(Key::Space);
1447        assert!(input.action_pressed("Jump"));
1448
1449        // No keys pressed again
1450        input.release_key(Key::Space);
1451        input.release_key(Key::W);
1452        assert!(!input.action_pressed("Jump"));
1453    }
1454
1455    #[test]
1456    fn test_action_pressed_nonexistent() {
1457        let input = InputManager::new();
1458        assert!(!input.action_pressed("NonExistent"));
1459    }
1460
1461    #[test]
1462    fn test_action_just_pressed() {
1463        let mut input = InputManager::new();
1464
1465        input.map_action("Jump", InputBinding::Key(Key::Space));
1466
1467        // Press key
1468        input.press_key(Key::Space);
1469        assert!(input.action_just_pressed("Jump"));
1470
1471        // Update to next frame
1472        input.update();
1473        assert!(!input.action_just_pressed("Jump")); // Still held, not "just" pressed
1474    }
1475
1476    #[test]
1477    fn test_action_just_released() {
1478        let mut input = InputManager::new();
1479
1480        input.map_action("Jump", InputBinding::Key(Key::Space));
1481
1482        // Press key
1483        input.press_key(Key::Space);
1484        input.update();
1485
1486        // Release key
1487        input.release_key(Key::Space);
1488        assert!(input.action_just_released("Jump"));
1489
1490        // Update to next frame
1491        input.update();
1492        assert!(!input.action_just_released("Jump"));
1493    }
1494
1495    #[test]
1496    fn test_action_strength() {
1497        let mut input = InputManager::new();
1498
1499        input.map_action("Jump", InputBinding::Key(Key::Space));
1500
1501        // Not pressed
1502        assert_eq!(input.action_strength("Jump"), 0.0);
1503
1504        // Pressed
1505        input.press_key(Key::Space);
1506        assert_eq!(input.action_strength("Jump"), 1.0);
1507    }
1508
1509    #[test]
1510    fn test_action_strength_nonexistent() {
1511        let input = InputManager::new();
1512        assert_eq!(input.action_strength("NonExistent"), 0.0);
1513    }
1514
1515    #[test]
1516    fn test_action_multiple_input_types() {
1517        let mut input = InputManager::new();
1518
1519        // Map action to key, mouse button, and gamepad button
1520        input.map_action("Fire", InputBinding::Key(Key::Space));
1521        input.map_action("Fire", InputBinding::MouseButton(MouseButton::Button1));
1522        input.map_action(
1523            "Fire",
1524            InputBinding::GamepadButton {
1525                gamepad_id: 0,
1526                button: 0,
1527            },
1528        );
1529
1530        // Test keyboard
1531        input.press_key(Key::Space);
1532        assert!(input.action_pressed("Fire"));
1533        input.release_key(Key::Space);
1534        assert!(!input.action_pressed("Fire"));
1535
1536        // Test mouse
1537        input.press_mouse_button(MouseButton::Button1);
1538        assert!(input.action_pressed("Fire"));
1539        input.release_mouse_button(MouseButton::Button1);
1540        assert!(!input.action_pressed("Fire"));
1541
1542        // Test gamepad
1543        input.press_gamepad_button(0, 0);
1544        assert!(input.action_pressed("Fire"));
1545    }
1546
1547    #[test]
1548    fn test_action_mapping_string_ownership() {
1549        let mut input = InputManager::new();
1550
1551        // Test with &str
1552        input.map_action("Jump", InputBinding::Key(Key::Space));
1553        assert!(input.has_action("Jump"));
1554
1555        // Test with String
1556        let action_name = String::from("Attack");
1557        input.map_action(action_name.clone(), InputBinding::Key(Key::E));
1558        assert!(input.has_action(&action_name));
1559    }
1560
1561    #[test]
1562    fn test_action_mapping_persistence_across_update() {
1563        let mut input = InputManager::new();
1564
1565        input.map_action("Jump", InputBinding::Key(Key::Space));
1566
1567        // Action mappings should persist across frame updates
1568        input.update();
1569        assert!(input.has_action("Jump"));
1570
1571        input.update();
1572        assert!(input.has_action("Jump"));
1573    }
1574
1575    #[test]
1576    fn test_action_mapping_persistence_across_clear() {
1577        let mut input = InputManager::new();
1578
1579        input.map_action("Jump", InputBinding::Key(Key::Space));
1580
1581        // Action mappings should persist across input state clear
1582        input.clear();
1583        assert!(input.has_action("Jump"));
1584    }
1585
1586    // === Input Buffering Tests ===
1587
1588    #[test]
1589    fn test_with_buffer_duration() {
1590        let duration = Duration::from_millis(500);
1591        let input = InputManager::with_buffer_duration(duration);
1592        assert_eq!(input.buffer_duration(), duration);
1593    }
1594
1595    #[test]
1596    fn test_set_buffer_duration() {
1597        let mut input = InputManager::new();
1598        let new_duration = Duration::from_millis(300);
1599
1600        input.set_buffer_duration(new_duration);
1601        assert_eq!(input.buffer_duration(), new_duration);
1602    }
1603
1604    #[test]
1605    fn test_buffer_size() {
1606        let mut input = InputManager::new();
1607        assert_eq!(input.buffer_size(), 0);
1608
1609        input.press_key(Key::A);
1610        assert_eq!(input.buffer_size(), 1);
1611
1612        input.press_key(Key::B);
1613        assert_eq!(input.buffer_size(), 2);
1614    }
1615
1616    #[test]
1617    fn test_clear_buffer() {
1618        let mut input = InputManager::new();
1619
1620        input.press_key(Key::A);
1621        input.press_key(Key::B);
1622        assert_eq!(input.buffer_size(), 2);
1623
1624        input.clear_buffer();
1625        assert_eq!(input.buffer_size(), 0);
1626    }
1627
1628    #[test]
1629    fn test_buffer_only_new_presses() {
1630        let mut input = InputManager::new();
1631
1632        // First press should buffer
1633        input.press_key(Key::A);
1634        assert_eq!(input.buffer_size(), 1);
1635
1636        // Pressing again while held should not buffer
1637        input.press_key(Key::A);
1638        assert_eq!(input.buffer_size(), 1);
1639
1640        // Release and press again should buffer
1641        input.release_key(Key::A);
1642        input.press_key(Key::A);
1643        assert_eq!(input.buffer_size(), 2);
1644    }
1645
1646    #[test]
1647    fn test_sequence_detected_basic() {
1648        let mut input = InputManager::new();
1649
1650        // Input sequence: A -> B -> C
1651        input.press_key(Key::A);
1652        input.press_key(Key::B);
1653        input.press_key(Key::C);
1654
1655        let sequence = vec![
1656            InputBinding::Key(Key::A),
1657            InputBinding::Key(Key::B),
1658            InputBinding::Key(Key::C),
1659        ];
1660
1661        assert!(input.sequence_detected(&sequence));
1662    }
1663
1664    #[test]
1665    fn test_sequence_detected_wrong_order() {
1666        let mut input = InputManager::new();
1667
1668        // Input sequence: A -> C -> B (wrong order)
1669        input.press_key(Key::A);
1670        input.press_key(Key::C);
1671        input.press_key(Key::B);
1672
1673        let sequence = vec![
1674            InputBinding::Key(Key::A),
1675            InputBinding::Key(Key::B),
1676            InputBinding::Key(Key::C),
1677        ];
1678
1679        assert!(!input.sequence_detected(&sequence));
1680    }
1681
1682    #[test]
1683    fn test_sequence_detected_partial() {
1684        let mut input = InputManager::new();
1685
1686        // Input only part of sequence
1687        input.press_key(Key::A);
1688        input.press_key(Key::B);
1689
1690        let sequence = vec![
1691            InputBinding::Key(Key::A),
1692            InputBinding::Key(Key::B),
1693            InputBinding::Key(Key::C),
1694        ];
1695
1696        assert!(!input.sequence_detected(&sequence));
1697    }
1698
1699    #[test]
1700    fn test_sequence_detected_empty() {
1701        let input = InputManager::new();
1702
1703        // Empty sequence should return false
1704        assert!(!input.sequence_detected(&[]));
1705    }
1706
1707    #[test]
1708    fn test_sequence_detected_with_extra_inputs() {
1709        let mut input = InputManager::new();
1710
1711        // Input sequence with extra inputs in between
1712        input.press_key(Key::A);
1713        input.press_key(Key::X); // Extra input
1714        input.press_key(Key::B);
1715        input.press_key(Key::Y); // Extra input
1716        input.press_key(Key::C);
1717
1718        let sequence = vec![
1719            InputBinding::Key(Key::A),
1720            InputBinding::Key(Key::B),
1721            InputBinding::Key(Key::C),
1722        ];
1723
1724        // Should still detect sequence (allows for extra inputs)
1725        assert!(input.sequence_detected(&sequence));
1726    }
1727
1728    #[test]
1729    fn test_consume_sequence() {
1730        let mut input = InputManager::new();
1731
1732        input.press_key(Key::A);
1733        input.press_key(Key::B);
1734
1735        let sequence = vec![InputBinding::Key(Key::A), InputBinding::Key(Key::B)];
1736
1737        // First consume should succeed
1738        assert!(input.consume_sequence(&sequence));
1739        assert_eq!(input.buffer_size(), 0); // Buffer cleared
1740
1741        // Second consume should fail (buffer cleared)
1742        assert!(!input.consume_sequence(&sequence));
1743    }
1744
1745    #[test]
1746    fn test_consume_sequence_not_detected() {
1747        let mut input = InputManager::new();
1748
1749        input.press_key(Key::A);
1750
1751        let sequence = vec![InputBinding::Key(Key::A), InputBinding::Key(Key::B)];
1752
1753        // Sequence not complete, should not consume
1754        assert!(!input.consume_sequence(&sequence));
1755        assert_eq!(input.buffer_size(), 1); // Buffer not cleared
1756    }
1757
1758    #[test]
1759    fn test_time_since_last_input() {
1760        let mut input = InputManager::new();
1761
1762        // No inputs, should return None
1763        assert!(input.time_since_last_input().is_none());
1764
1765        input.press_key(Key::A);
1766
1767        // Should return Some(small value)
1768        let time = input.time_since_last_input();
1769        assert!(time.is_some());
1770        assert!(time.unwrap() < 0.1); // Should be very recent
1771    }
1772
1773    #[test]
1774    fn test_buffered_inputs_iterator() {
1775        let mut input = InputManager::new();
1776
1777        input.press_key(Key::A);
1778        input.press_key(Key::B);
1779        input.press_key(Key::C);
1780
1781        let buffered: Vec<_> = input.buffered_inputs().collect();
1782        assert_eq!(buffered.len(), 3);
1783
1784        // Check bindings (ages will be very small)
1785        assert_eq!(buffered[0].0, InputBinding::Key(Key::A));
1786        assert_eq!(buffered[1].0, InputBinding::Key(Key::B));
1787        assert_eq!(buffered[2].0, InputBinding::Key(Key::C));
1788
1789        // Ages should be very small (recent)
1790        assert!(buffered[0].1 < 0.1);
1791        assert!(buffered[1].1 < 0.1);
1792        assert!(buffered[2].1 < 0.1);
1793    }
1794
1795    #[test]
1796    fn test_buffer_expiration() {
1797        use std::thread;
1798
1799        let mut input = InputManager::with_buffer_duration(Duration::from_millis(50));
1800
1801        input.press_key(Key::A);
1802        assert_eq!(input.buffer_size(), 1);
1803
1804        // Wait for buffer to expire
1805        thread::sleep(Duration::from_millis(60));
1806
1807        // Update should clean expired inputs
1808        input.update();
1809        assert_eq!(input.buffer_size(), 0);
1810    }
1811
1812    #[test]
1813    fn test_buffer_max_size() {
1814        let mut input = InputManager::new();
1815
1816        // Fill buffer beyond max size (32)
1817        for i in 0..40 {
1818            input.press_key(Key::A);
1819            input.release_key(Key::A);
1820        }
1821
1822        // Should cap at 32
1823        assert!(input.buffer_size() <= 32);
1824    }
1825
1826    #[test]
1827    fn test_sequence_mixed_input_types() {
1828        let mut input = InputManager::new();
1829
1830        // Sequence with keyboard, mouse, and gamepad
1831        input.press_key(Key::A);
1832        input.press_mouse_button(MouseButton::Button1);
1833        input.press_gamepad_button(0, 5);
1834
1835        let sequence = vec![
1836            InputBinding::Key(Key::A),
1837            InputBinding::MouseButton(MouseButton::Button1),
1838            InputBinding::GamepadButton {
1839                gamepad_id: 0,
1840                button: 5,
1841            },
1842        ];
1843
1844        assert!(input.sequence_detected(&sequence));
1845    }
1846
1847    #[test]
1848    fn test_fighting_game_combo() {
1849        let mut input = InputManager::new();
1850
1851        // Classic "hadouken" combo: Down -> Down (double tap) -> Forward -> Punch
1852        input.press_key(Key::Down);
1853        input.release_key(Key::Down); // Release for double tap
1854        input.press_key(Key::Down); // Second Down press
1855        input.press_key(Key::Right);
1856        input.press_key(Key::Space);
1857
1858        let hadouken = vec![
1859            InputBinding::Key(Key::Down),
1860            InputBinding::Key(Key::Down),
1861            InputBinding::Key(Key::Right),
1862            InputBinding::Key(Key::Space),
1863        ];
1864
1865        assert!(input.sequence_detected(&hadouken));
1866    }
1867
1868    #[test]
1869    fn test_sequence_persistence_across_update() {
1870        let mut input = InputManager::new();
1871
1872        input.press_key(Key::A);
1873        input.press_key(Key::B);
1874
1875        // Update shouldn't clear recent buffer
1876        input.update();
1877
1878        let sequence = vec![InputBinding::Key(Key::A), InputBinding::Key(Key::B)];
1879        assert!(input.sequence_detected(&sequence));
1880    }
1881
1882    #[test]
1883    fn test_buffer_not_cleared_by_state_clear() {
1884        let mut input = InputManager::new();
1885
1886        input.press_key(Key::A);
1887        assert_eq!(input.buffer_size(), 1);
1888
1889        // clear() only clears input state, not buffer
1890        input.clear();
1891        assert_eq!(input.buffer_size(), 1);
1892    }
1893
1894    #[test]
1895    fn test_double_tap_detection() {
1896        let mut input = InputManager::new();
1897
1898        // Double tap: A -> A
1899        input.press_key(Key::W);
1900        input.release_key(Key::W);
1901        input.press_key(Key::W);
1902
1903        let double_tap = vec![InputBinding::Key(Key::W), InputBinding::Key(Key::W)];
1904
1905        assert!(input.sequence_detected(&double_tap));
1906    }
1907
1908    // === Gamepad Analog Axes Tests ===
1909
1910    #[test]
1911    fn test_gamepad_axis_basic() {
1912        let mut input = InputManager::new();
1913
1914        // Initially zero
1915        assert_eq!(input.gamepad_axis(0, GamepadAxis::AxisLeftX), 0.0);
1916
1917        // Set axis value
1918        input.set_gamepad_axis(0, GamepadAxis::AxisLeftX, 0.5);
1919        assert_eq!(input.gamepad_axis(0, GamepadAxis::AxisLeftX), 0.5);
1920
1921        // Negative value
1922        input.set_gamepad_axis(0, GamepadAxis::AxisLeftY, -0.75);
1923        assert_eq!(input.gamepad_axis(0, GamepadAxis::AxisLeftY), -0.75);
1924    }
1925
1926    #[test]
1927    fn test_gamepad_axis_deadzone() {
1928        let mut input = InputManager::new();
1929
1930        // Default deadzone is 0.1
1931        assert_eq!(input.analog_deadzone(), 0.1);
1932
1933        // Values within deadzone should be zeroed
1934        input.set_gamepad_axis(0, GamepadAxis::AxisLeftX, 0.05);
1935        assert_eq!(input.gamepad_axis(0, GamepadAxis::AxisLeftX), 0.0);
1936
1937        // Values outside deadzone should pass through
1938        input.set_gamepad_axis(0, GamepadAxis::AxisLeftX, 0.15);
1939        assert_eq!(input.gamepad_axis(0, GamepadAxis::AxisLeftX), 0.15);
1940    }
1941
1942    #[test]
1943    fn test_set_analog_deadzone() {
1944        let mut input = InputManager::new();
1945
1946        // Set custom deadzone
1947        input.set_analog_deadzone(0.2);
1948        assert_eq!(input.analog_deadzone(), 0.2);
1949
1950        // Value within new deadzone is zeroed
1951        input.set_gamepad_axis(0, GamepadAxis::AxisLeftX, 0.15);
1952        assert_eq!(input.gamepad_axis(0, GamepadAxis::AxisLeftX), 0.0);
1953
1954        // Value outside new deadzone passes through
1955        input.set_gamepad_axis(0, GamepadAxis::AxisLeftX, 0.25);
1956        assert_eq!(input.gamepad_axis(0, GamepadAxis::AxisLeftX), 0.25);
1957    }
1958
1959    #[test]
1960    fn test_gamepad_left_stick() {
1961        let mut input = InputManager::new();
1962
1963        // Initially zero
1964        assert_eq!(input.gamepad_left_stick(0), Vec2::zero());
1965
1966        // Set stick values
1967        input.set_gamepad_axis(0, GamepadAxis::AxisLeftX, 0.8);
1968        input.set_gamepad_axis(0, GamepadAxis::AxisLeftY, -0.6);
1969
1970        let stick = input.gamepad_left_stick(0);
1971        assert_eq!(stick.x, 0.8);
1972        assert_eq!(stick.y, -0.6);
1973    }
1974
1975    #[test]
1976    fn test_gamepad_right_stick() {
1977        let mut input = InputManager::new();
1978
1979        // Set right stick values
1980        input.set_gamepad_axis(0, GamepadAxis::AxisRightX, -0.5);
1981        input.set_gamepad_axis(0, GamepadAxis::AxisRightY, 0.3);
1982
1983        let stick = input.gamepad_right_stick(0);
1984        assert_eq!(stick.x, -0.5);
1985        assert_eq!(stick.y, 0.3);
1986    }
1987
1988    #[test]
1989    fn test_gamepad_triggers() {
1990        let mut input = InputManager::new();
1991
1992        // Triggers are normalized from -1.0..1.0 to 0.0..1.0
1993        // Set left trigger (axis value -1.0 = 0.0, axis value 1.0 = 1.0)
1994        input.set_gamepad_axis(0, GamepadAxis::AxisLeftTrigger, -1.0);
1995        assert_eq!(input.gamepad_left_trigger(0), 0.0);
1996
1997        input.set_gamepad_axis(0, GamepadAxis::AxisLeftTrigger, 1.0);
1998        assert_eq!(input.gamepad_left_trigger(0), 1.0);
1999
2000        // Mid-press (axis 0.0 = trigger 0.5)
2001        input.set_gamepad_axis(0, GamepadAxis::AxisLeftTrigger, 0.0);
2002        assert_eq!(input.gamepad_left_trigger(0), 0.5);
2003
2004        // Right trigger
2005        input.set_gamepad_axis(0, GamepadAxis::AxisRightTrigger, 0.5);
2006        assert_eq!(input.gamepad_right_trigger(0), 0.75);
2007    }
2008
2009    #[test]
2010    fn test_gamepad_axis_nonexistent_gamepad() {
2011        let input = InputManager::new();
2012
2013        // Querying nonexistent gamepad returns 0.0 for axes
2014        assert_eq!(input.gamepad_axis(10, GamepadAxis::AxisLeftX), 0.0);
2015        assert_eq!(input.gamepad_left_stick(10), Vec2::zero());
2016
2017        // Triggers normalize from -1.0..1.0 to 0.0..1.0
2018        // So axis value 0.0 (default) becomes trigger value 0.5
2019        assert_eq!(input.gamepad_left_trigger(10), 0.5);
2020        assert_eq!(input.gamepad_right_trigger(10), 0.5);
2021    }
2022
2023    // === Gamepad Connection Tests ===
2024
2025    #[test]
2026    fn test_gamepad_connection() {
2027        let mut input = InputManager::new();
2028
2029        // Initially not connected
2030        assert!(!input.is_gamepad_connected(0));
2031        assert_eq!(input.connected_gamepad_count(), 0);
2032
2033        // Connect gamepad 0
2034        input.set_gamepad_connected(0, true);
2035        assert!(input.is_gamepad_connected(0));
2036        assert_eq!(input.connected_gamepad_count(), 1);
2037
2038        // Connect gamepad 1
2039        input.set_gamepad_connected(1, true);
2040        assert!(input.is_gamepad_connected(1));
2041        assert_eq!(input.connected_gamepad_count(), 2);
2042
2043        // Disconnect gamepad 0
2044        input.set_gamepad_connected(0, false);
2045        assert!(!input.is_gamepad_connected(0));
2046        assert!(input.is_gamepad_connected(1));
2047        assert_eq!(input.connected_gamepad_count(), 1);
2048    }
2049
2050    #[test]
2051    fn test_connected_gamepads_iterator() {
2052        let mut input = InputManager::new();
2053
2054        input.set_gamepad_connected(0, true);
2055        input.set_gamepad_connected(2, true);
2056        input.set_gamepad_connected(4, true);
2057
2058        let connected: Vec<_> = input.connected_gamepads().collect();
2059        assert_eq!(connected.len(), 3);
2060        assert!(connected.contains(&0));
2061        assert!(connected.contains(&2));
2062        assert!(connected.contains(&4));
2063    }
2064
2065    #[test]
2066    fn test_gamepad_connection_nonexistent() {
2067        let input = InputManager::new();
2068
2069        // Querying nonexistent gamepad returns false
2070        assert!(!input.is_gamepad_connected(10));
2071    }
2072
2073    // === Gamepad Vibration Tests ===
2074
2075    #[test]
2076    fn test_gamepad_vibration() {
2077        let mut input = InputManager::new();
2078
2079        // Initially no vibration
2080        assert_eq!(input.gamepad_vibration(0), 0.0);
2081
2082        // Set vibration
2083        input.set_gamepad_vibration(0, 0.75);
2084        assert_eq!(input.gamepad_vibration(0), 0.75);
2085
2086        // Clamping to 0.0-1.0
2087        input.set_gamepad_vibration(0, 1.5);
2088        assert_eq!(input.gamepad_vibration(0), 1.0);
2089
2090        input.set_gamepad_vibration(0, -0.5);
2091        assert_eq!(input.gamepad_vibration(0), 0.0);
2092    }
2093
2094    #[test]
2095    fn test_stop_gamepad_vibration() {
2096        let mut input = InputManager::new();
2097
2098        input.set_gamepad_vibration(0, 0.8);
2099        assert_eq!(input.gamepad_vibration(0), 0.8);
2100
2101        input.stop_gamepad_vibration(0);
2102        assert_eq!(input.gamepad_vibration(0), 0.0);
2103    }
2104
2105    #[test]
2106    fn test_stop_all_vibration() {
2107        let mut input = InputManager::new();
2108
2109        input.set_gamepad_vibration(0, 0.5);
2110        input.set_gamepad_vibration(1, 0.7);
2111        input.set_gamepad_vibration(2, 0.9);
2112
2113        input.stop_all_vibration();
2114
2115        assert_eq!(input.gamepad_vibration(0), 0.0);
2116        assert_eq!(input.gamepad_vibration(1), 0.0);
2117        assert_eq!(input.gamepad_vibration(2), 0.0);
2118    }
2119
2120    // === Gamepad Integration Tests ===
2121
2122    #[test]
2123    fn test_gamepad_state_clear_preserves_connection() {
2124        let mut input = InputManager::new();
2125
2126        // Set up gamepad state
2127        input.set_gamepad_connected(0, true);
2128        input.set_gamepad_axis(0, GamepadAxis::AxisLeftX, 0.5);
2129        input.press_gamepad_button(0, 1);
2130        input.set_gamepad_vibration(0, 0.8);
2131
2132        // Clear should remove input state but preserve connection and vibration
2133        input.clear();
2134
2135        assert!(input.is_gamepad_connected(0)); // Connection preserved
2136        assert_eq!(input.gamepad_vibration(0), 0.8); // Vibration preserved
2137        assert_eq!(input.gamepad_axis(0, GamepadAxis::AxisLeftX), 0.0); // Axes cleared
2138        assert!(!input.gamepad_button_pressed(0, 1)); // Buttons cleared
2139    }
2140
2141    #[test]
2142    fn test_gamepad_multiple_gamepads_axes() {
2143        let mut input = InputManager::new();
2144
2145        // Set different values for different gamepads
2146        input.set_gamepad_axis(0, GamepadAxis::AxisLeftX, 0.5);
2147        input.set_gamepad_axis(1, GamepadAxis::AxisLeftX, -0.5);
2148        input.press_gamepad_button(0, 1);
2149        input.press_gamepad_button(1, 2);
2150
2151        // Verify isolation
2152        assert_eq!(input.gamepad_axis(0, GamepadAxis::AxisLeftX), 0.5);
2153        assert_eq!(input.gamepad_axis(1, GamepadAxis::AxisLeftX), -0.5);
2154        assert!(input.gamepad_button_pressed(0, 1));
2155        assert!(!input.gamepad_button_pressed(0, 2));
2156        assert!(input.gamepad_button_pressed(1, 2));
2157        assert!(!input.gamepad_button_pressed(1, 1));
2158    }
2159
2160    #[test]
2161    fn test_gamepad_axes_update_persistence() {
2162        let mut input = InputManager::new();
2163
2164        input.set_gamepad_axis(0, GamepadAxis::AxisLeftX, 0.8);
2165
2166        // Axes should persist across update (unlike deltas)
2167        input.update();
2168        assert_eq!(input.gamepad_axis(0, GamepadAxis::AxisLeftX), 0.8);
2169
2170        input.update();
2171        assert_eq!(input.gamepad_axis(0, GamepadAxis::AxisLeftX), 0.8);
2172    }
2173
2174    #[test]
2175    fn test_gamepad_stick_magnitude() {
2176        let mut input = InputManager::new();
2177
2178        // Set stick to diagonal (0.6, 0.8) - should have magnitude ~1.0
2179        input.set_gamepad_axis(0, GamepadAxis::AxisLeftX, 0.6);
2180        input.set_gamepad_axis(0, GamepadAxis::AxisLeftY, 0.8);
2181
2182        let stick = input.gamepad_left_stick(0);
2183        let magnitude = (stick.x * stick.x + stick.y * stick.y).sqrt();
2184
2185        // Magnitude should be close to 1.0 (floating point precision)
2186        assert!((magnitude - 1.0).abs() < 0.01);
2187    }
2188
2189    #[test]
2190    fn test_gamepad_expansion() {
2191        let mut input = InputManager::new();
2192
2193        // Should automatically expand to support gamepad 10
2194        input.set_gamepad_axis(10, GamepadAxis::AxisLeftX, 0.5);
2195        assert_eq!(input.gamepad_axis(10, GamepadAxis::AxisLeftX), 0.5);
2196
2197        input.set_gamepad_connected(10, true);
2198        assert!(input.is_gamepad_connected(10));
2199    }
2200}