konnektoren_bevy/input/
components.rs

1use super::device::{InputDevice, KeyboardScheme};
2use bevy::prelude::*;
3use std::collections::HashMap;
4
5/// Main input controller component for entities that need input
6#[derive(Component, Reflect, Clone)]
7#[reflect(Component)]
8pub struct InputController {
9    pub player_id: u32,
10    pub movement: Vec2,
11    pub primary_action: bool,   // Confirm/Select/Activate
12    pub secondary_action: bool, // Back/Cancel/Abort
13    pub input_source: InputSource,
14    pub enabled: bool,
15}
16
17impl Default for InputController {
18    fn default() -> Self {
19        Self {
20            player_id: 0,
21            movement: Vec2::ZERO,
22            primary_action: false,
23            secondary_action: false,
24            input_source: InputSource::Keyboard(KeyboardScheme::WASD),
25            enabled: true,
26        }
27    }
28}
29
30impl InputController {
31    pub fn new(player_id: u32) -> Self {
32        Self {
33            player_id,
34            ..Default::default()
35        }
36    }
37
38    pub fn with_input_source(mut self, source: InputSource) -> Self {
39        self.input_source = source;
40        self
41    }
42
43    pub fn enabled(mut self, enabled: bool) -> Self {
44        self.enabled = enabled;
45        self
46    }
47
48    /// Clear all input states
49    pub fn clear(&mut self) {
50        self.movement = Vec2::ZERO;
51        self.primary_action = false;
52        self.secondary_action = false;
53    }
54
55    /// Check if any input is active
56    pub fn has_input(&self) -> bool {
57        self.movement != Vec2::ZERO || self.primary_action || self.secondary_action
58    }
59}
60
61/// Input source tracking
62#[derive(Reflect, Clone, Debug, PartialEq)]
63pub enum InputSource {
64    Keyboard(KeyboardScheme),
65    Gamepad(Entity),
66    Mouse,
67    Touch,
68}
69
70impl InputSource {
71    pub fn name(&self) -> String {
72        match self {
73            InputSource::Keyboard(scheme) => format!("Keyboard ({})", scheme.name()),
74            InputSource::Gamepad(_) => "Gamepad".to_string(),
75            InputSource::Mouse => "Mouse".to_string(),
76            InputSource::Touch => "Touch".to_string(),
77        }
78    }
79}
80
81/// Component to map specific inputs to a player
82#[derive(Component, Reflect, Clone)]
83#[reflect(Component)]
84pub struct PlayerInputMapping {
85    pub player_id: u32,
86    pub primary_device: Option<InputDevice>,
87    pub secondary_device: Option<InputDevice>, // For fallback/dual input
88    pub enabled: bool,
89}
90
91impl Default for PlayerInputMapping {
92    fn default() -> Self {
93        Self {
94            player_id: 0,
95            primary_device: Some(InputDevice::Keyboard(KeyboardScheme::WASD)),
96            secondary_device: None,
97            enabled: true,
98        }
99    }
100}
101
102impl PlayerInputMapping {
103    pub fn new(player_id: u32) -> Self {
104        Self {
105            player_id,
106            ..Default::default()
107        }
108    }
109
110    pub fn with_primary_device(mut self, device: InputDevice) -> Self {
111        self.primary_device = Some(device);
112        self
113    }
114
115    pub fn with_secondary_device(mut self, device: Option<InputDevice>) -> Self {
116        self.secondary_device = device;
117        self
118    }
119
120    pub fn enabled(mut self, enabled: bool) -> Self {
121        self.enabled = enabled;
122        self
123    }
124
125    /// Get the currently assigned device (primary takes precedence)
126    pub fn get_active_device(&self) -> Option<&InputDevice> {
127        self.primary_device
128            .as_ref()
129            .or(self.secondary_device.as_ref())
130    }
131}
132
133/// Resource for tracking device assignments
134#[derive(Resource, Reflect, Default)]
135#[reflect(Resource)]
136pub struct InputDeviceAssignment {
137    pub assignments: HashMap<u32, InputDevice>, // player_id -> device
138    pub max_players: u32,
139}
140
141impl InputDeviceAssignment {
142    pub fn new(max_players: u32) -> Self {
143        Self {
144            assignments: HashMap::new(),
145            max_players,
146        }
147    }
148
149    /// Assign a device to a player
150    pub fn assign_device(&mut self, player_id: u32, device: InputDevice) {
151        // Remove device from other players to prevent conflicts
152        self.assignments
153            .retain(|_, assigned_device| assigned_device != &device);
154
155        // Assign to the specified player
156        self.assignments.insert(player_id, device);
157
158        info!(
159            "Assigned device to player {}: {:?}",
160            player_id,
161            self.assignments.get(&player_id)
162        );
163    }
164
165    /// Get the device assigned to a player
166    pub fn get_device_for_player(&self, player_id: u32) -> Option<&InputDevice> {
167        self.assignments.get(&player_id)
168    }
169
170    /// Check if a device is assigned to any player
171    pub fn is_device_assigned(&self, device: &InputDevice) -> bool {
172        self.assignments
173            .values()
174            .any(|assigned_device| assigned_device == device)
175    }
176
177    /// Get the player ID that has the device assigned
178    pub fn get_player_for_device(&self, device: &InputDevice) -> Option<u32> {
179        self.assignments
180            .iter()
181            .find(|(_, assigned_device)| *assigned_device == device)
182            .map(|(player_id, _)| *player_id)
183    }
184
185    /// Remove device assignment from a player
186    pub fn unassign_player(&mut self, player_id: u32) {
187        self.assignments.remove(&player_id);
188    }
189
190    /// Clear all assignments
191    pub fn clear(&mut self) {
192        self.assignments.clear();
193    }
194
195    /// Get all assigned players
196    pub fn get_assigned_players(&self) -> Vec<u32> {
197        self.assignments.keys().copied().collect()
198    }
199}
200
201/// Input configuration settings
202#[derive(Resource, Reflect, Clone)]
203#[reflect(Resource)]
204pub struct InputSettings {
205    pub gamepad_deadzone: f32,
206    pub movement_threshold: f32,
207    pub auto_assign_devices: bool,
208    pub allow_keyboard_sharing: bool, // Allow multiple players to use different keyboard schemes
209}
210
211impl Default for InputSettings {
212    fn default() -> Self {
213        Self {
214            gamepad_deadzone: 0.2,
215            movement_threshold: 0.1,
216            auto_assign_devices: true,
217            allow_keyboard_sharing: true,
218        }
219    }
220}
221
222/// Events for input system
223#[derive(Event, Debug, Clone)]
224pub enum InputEvent {
225    /// Device assigned to player
226    DeviceAssigned { player_id: u32, device: InputDevice },
227    /// Device unassigned from player
228    DeviceUnassigned { player_id: u32 },
229    /// Primary action pressed
230    PrimaryAction { player_id: u32, source: InputSource },
231    /// Secondary action pressed
232    SecondaryAction { player_id: u32, source: InputSource },
233    /// Movement input
234    Movement {
235        player_id: u32,
236        direction: Vec2,
237        source: InputSource,
238    },
239}
240
241/// Component marker for input configuration UI
242#[derive(Component)]
243pub struct InputConfigurationMarker;