konnektoren_bevy/input/
systems.rs

1use super::{
2    components::*,
3    device::{AvailableInputDevices, InputDevice},
4};
5use bevy::prelude::*;
6
7/// System to handle keyboard input
8pub fn handle_keyboard_input(
9    keyboard: Res<ButtonInput<KeyCode>>,
10    mut controller_query: Query<(&mut InputController, &PlayerInputMapping)>,
11    settings: Res<InputSettings>,
12    mut input_events: EventWriter<InputEvent>,
13) {
14    for (mut controller, mapping) in &mut controller_query {
15        if !controller.enabled || !mapping.enabled {
16            continue;
17        }
18
19        // Check if this player has a keyboard device assigned
20        let keyboard_scheme = match &mapping.primary_device {
21            Some(InputDevice::Keyboard(scheme)) => Some(scheme.clone()),
22            _ => match &mapping.secondary_device {
23                Some(InputDevice::Keyboard(scheme)) => Some(scheme.clone()),
24                _ => None,
25            },
26        };
27
28        let Some(scheme) = keyboard_scheme else {
29            continue;
30        };
31
32        let (up, down, left, right) = scheme.get_keys();
33
34        // Handle continuous movement input
35        let mut movement = Vec2::ZERO;
36        if keyboard.pressed(up) {
37            movement.y += 1.0;
38        }
39        if keyboard.pressed(down) {
40            movement.y -= 1.0;
41        }
42        if keyboard.pressed(left) {
43            movement.x -= 1.0;
44        }
45        if keyboard.pressed(right) {
46            movement.x += 1.0;
47        }
48
49        // Normalize diagonal movement
50        if movement != Vec2::ZERO {
51            movement = movement.normalize();
52        }
53
54        // Apply movement threshold
55        if movement.length() > settings.movement_threshold {
56            controller.movement = movement;
57            controller.input_source = InputSource::Keyboard(scheme.clone());
58
59            input_events.write(InputEvent::Movement {
60                player_id: controller.player_id,
61                direction: movement,
62                source: controller.input_source.clone(),
63            });
64        } else if matches!(controller.input_source, InputSource::Keyboard(_)) {
65            controller.movement = Vec2::ZERO;
66        }
67
68        // Handle action input
69        let primary_pressed =
70            keyboard.just_pressed(KeyCode::Space) || keyboard.just_pressed(KeyCode::Enter);
71        let secondary_pressed =
72            keyboard.just_pressed(KeyCode::Escape) || keyboard.just_pressed(KeyCode::Backspace);
73
74        if primary_pressed {
75            controller.primary_action = true;
76            controller.input_source = InputSource::Keyboard(scheme.clone());
77
78            input_events.write(InputEvent::PrimaryAction {
79                player_id: controller.player_id,
80                source: controller.input_source.clone(),
81            });
82        }
83
84        if secondary_pressed {
85            controller.secondary_action = true;
86            controller.input_source = InputSource::Keyboard(scheme.clone());
87
88            input_events.write(InputEvent::SecondaryAction {
89                player_id: controller.player_id,
90                source: controller.input_source.clone(),
91            });
92        }
93    }
94}
95
96/// System to handle gamepad input
97pub fn handle_gamepad_input(
98    gamepads: Query<(Entity, &Gamepad)>,
99    mut controller_query: Query<(&mut InputController, &PlayerInputMapping)>,
100    settings: Res<InputSettings>,
101    mut input_events: EventWriter<InputEvent>,
102) {
103    for (mut controller, mapping) in &mut controller_query {
104        if !controller.enabled || !mapping.enabled {
105            continue;
106        }
107
108        // Check if this player has a gamepad device assigned
109        let gamepad_id = match &mapping.primary_device {
110            Some(InputDevice::Gamepad(id)) => Some(*id),
111            _ => match &mapping.secondary_device {
112                Some(InputDevice::Gamepad(id)) => Some(*id),
113                _ => None,
114            },
115        };
116
117        let Some(target_gamepad_id) = gamepad_id else {
118            continue;
119        };
120
121        // Find the gamepad entity - Fixed the gamepad matching logic
122        let gamepad_entities: Vec<(Entity, &Gamepad)> = gamepads.iter().collect();
123
124        let Some((gamepad_entity, gamepad)) = gamepad_entities.get(target_gamepad_id as usize)
125        else {
126            continue;
127        };
128
129        let mut movement = Vec2::ZERO;
130
131        // D-Pad input
132        if gamepad.pressed(GamepadButton::DPadUp) {
133            movement.y += 1.0;
134        }
135        if gamepad.pressed(GamepadButton::DPadDown) {
136            movement.y -= 1.0;
137        }
138        if gamepad.pressed(GamepadButton::DPadLeft) {
139            movement.x -= 1.0;
140        }
141        if gamepad.pressed(GamepadButton::DPadRight) {
142            movement.x += 1.0;
143        }
144
145        // Analog stick input (with deadzone)
146        let left_stick = gamepad.left_stick();
147        if left_stick.length() > settings.gamepad_deadzone {
148            movement += left_stick;
149        }
150
151        // Normalize and clamp movement
152        if movement.length() > 1.0 {
153            movement = movement.normalize();
154        }
155
156        if movement.length() > settings.movement_threshold {
157            controller.movement = movement;
158            controller.input_source = InputSource::Gamepad(*gamepad_entity);
159
160            input_events.write(InputEvent::Movement {
161                player_id: controller.player_id,
162                direction: movement,
163                source: controller.input_source.clone(),
164            });
165        } else if matches!(controller.input_source, InputSource::Gamepad(_)) {
166            controller.movement = Vec2::ZERO;
167        }
168
169        // Handle action input
170        if gamepad.just_pressed(GamepadButton::South) || gamepad.just_pressed(GamepadButton::Start)
171        {
172            controller.primary_action = true;
173            controller.input_source = InputSource::Gamepad(*gamepad_entity);
174
175            input_events.write(InputEvent::PrimaryAction {
176                player_id: controller.player_id,
177                source: controller.input_source.clone(),
178            });
179        }
180
181        if gamepad.just_pressed(GamepadButton::East) || gamepad.just_pressed(GamepadButton::Select)
182        {
183            controller.secondary_action = true;
184            controller.input_source = InputSource::Gamepad(*gamepad_entity);
185
186            input_events.write(InputEvent::SecondaryAction {
187                player_id: controller.player_id,
188                source: controller.input_source.clone(),
189            });
190        }
191    }
192}
193
194/// System to detect and track connected gamepads
195pub fn detect_gamepads(
196    mut available_devices: ResMut<AvailableInputDevices>,
197    gamepads: Query<Entity, With<Gamepad>>,
198) {
199    let old_count = available_devices.gamepads.len();
200
201    // Update connected gamepads list
202    available_devices.gamepads.clear();
203    for gamepad_entity in gamepads.iter() {
204        available_devices.gamepads.push(gamepad_entity);
205    }
206
207    let new_count = available_devices.gamepads.len();
208
209    if old_count != new_count {
210        info!("Gamepad count changed: {} -> {}", old_count, new_count);
211    }
212
213    // Update general device availability
214    available_devices.update_availability();
215}
216
217/// System to assign devices to players automatically if enabled
218pub fn auto_assign_devices(
219    mut assignment: ResMut<InputDeviceAssignment>,
220    available_devices: Res<AvailableInputDevices>,
221    settings: Res<InputSettings>,
222    controllers: Query<&InputController>,
223) {
224    if !settings.auto_assign_devices || !assignment.assignments.is_empty() {
225        return;
226    }
227
228    let player_count = controllers.iter().map(|c| c.player_id).max().unwrap_or(0) + 1;
229    let available = available_devices.get_available_devices();
230
231    for player_id in 0..player_count.min(assignment.max_players) {
232        if assignment.get_device_for_player(player_id).is_some() {
233            continue; // Already assigned
234        }
235
236        // Find an unassigned device
237        if let Some(device) = available
238            .iter()
239            .find(|device| !assignment.is_device_assigned(device))
240        {
241            assignment.assign_device(player_id, device.clone());
242        }
243    }
244}
245
246/// System to update player input mappings from assignments
247pub fn update_player_mappings(
248    assignment: Res<InputDeviceAssignment>,
249    mut mappings: Query<&mut PlayerInputMapping>,
250) {
251    if !assignment.is_changed() {
252        return;
253    }
254
255    for mut mapping in mappings.iter_mut() {
256        if let Some(device) = assignment.get_device_for_player(mapping.player_id) {
257            mapping.primary_device = Some(device.clone());
258        } else {
259            mapping.primary_device = None;
260        }
261    }
262}
263
264/// System to clear input states at the end of each frame
265pub fn clear_input_states(mut controllers: Query<&mut InputController>) {
266    for mut controller in controllers.iter_mut() {
267        controller.primary_action = false;
268        controller.secondary_action = false;
269    }
270}