Skip to main content

fantasy_craft/input/
manager.rs

1use std::collections::HashMap;
2use macroquad::prelude::*;
3
4#[derive(Debug, Clone, Copy, PartialEq)]
5pub enum InputVariant {
6    Key(KeyCode),
7    Mouse(MouseButton)
8}
9
10#[derive(Debug, Clone, Copy, PartialEq)]
11pub enum KeyboardLayout {
12    Qwerty,
13    Azerty,
14}
15
16pub struct InputManager {
17    bindings: HashMap<String, Vec<InputVariant>>,
18    /// Layout is used to adapt key interpretation on Native targets.
19    /// On Web, we ignore this because browsers send physical scancodes.
20    pub layout: KeyboardLayout,
21}
22
23impl Default for InputManager {
24    fn default() -> Self {
25        Self {
26            bindings: HashMap::new(),
27            layout: KeyboardLayout::Qwerty, // Default standard
28        }
29    }
30}
31
32impl InputManager {
33    pub fn new() -> Self {
34        Self::default()
35    }
36
37    pub fn set_layout(&mut self, layout: KeyboardLayout) {
38        self.layout = layout;
39        // Ideally, we should reload bindings here if they were already loaded,
40        // but for simplicity, we assume set_layout is called before loading config.
41        info!("InputManager: Keyboard layout set to {:?}", layout);
42    }
43
44    pub fn bind(&mut self, action: &str, input: InputVariant) {
45        self.bindings
46            .entry(action.to_string())
47            .or_insert_with(Vec::new)
48            .push(input);
49    }
50
51    /// Loads bindings from a JSON string content.
52    pub fn load_from_string(&mut self, json_content: &str) {
53        let json_bindings: HashMap<String, Vec<String>> = match serde_json::from_str(json_content) {
54            Ok(data) => data,
55            Err(e) => {
56                error!("InputManager: Failed to parse bindings JSON. Error: {}", e);
57                return;
58            }
59        };
60
61        // Clear existing bindings to avoid duplicates if reloading
62        self.bindings.clear();
63
64        for (action, inputs) in json_bindings {
65            for input_str in inputs {
66                let variants = self.parse_input_string(&input_str);
67                
68                if variants.is_empty() {
69                    warn!("InputManager: Unknown input key '{}' for action '{}'", input_str, action);
70                }
71
72                for variant in variants {
73                    self.bind(&action, variant);
74                }
75            }
76        }
77        info!("InputManager: Bindings loaded.");
78    }
79
80    pub fn is_action_down(&self, action: &str) -> bool {
81        if let Some(inputs) = self.bindings.get(action) {
82            for input in inputs {
83                match input {
84                    InputVariant::Key(k) => if is_key_down(*k) { return true; },
85                    InputVariant::Mouse(b) => if is_mouse_button_down(*b) { return true; }
86                }
87            }
88        }
89        false
90    }
91
92    pub fn is_action_just_pressed(&self, action: &str) -> bool {
93        if let Some(inputs) = self.bindings.get(action) {
94            for input in inputs {
95                match input {
96                    InputVariant::Key(k) => if is_key_pressed(*k) { return true; },
97                    InputVariant::Mouse(b) => if is_mouse_button_pressed(*b) { return true; }
98                }
99            }
100        }
101        false
102    }
103
104    /// Converts a string representation of a key to a LIST of Macroquad InputVariants.
105    fn parse_input_string(&self, s: &str) -> Vec<InputVariant> {
106        let mut variants = Vec::new();
107
108        // Helper to check if we are in a context where remapping is needed
109        // On WASM, we trust physical keys (Browser KeyCodes).
110        // On Native, we respect the user's Layout setting.
111        let use_azerty_mapping = {
112            #[cfg(target_arch = "wasm32")]
113            { false }
114            #[cfg(not(target_arch = "wasm32"))]
115            { self.layout == KeyboardLayout::Azerty }
116        };
117
118        match s {
119            // Mouse
120            "MouseLeft" => variants.push(InputVariant::Mouse(MouseButton::Left)),
121            "MouseRight" => variants.push(InputVariant::Mouse(MouseButton::Right)),
122            "MouseMiddle" => variants.push(InputVariant::Mouse(MouseButton::Middle)),
123            
124            // --- MOVEMENT KEYS REMAPPING ---
125            // The JSON config should use standard QWERTY names ("W", "A", "S", "D")
126            
127            "W" | "ScanCodeW" => {
128                if use_azerty_mapping {
129                    variants.push(InputVariant::Key(KeyCode::Z)); // Native AZERTY
130                } else {
131                    variants.push(InputVariant::Key(KeyCode::W)); // Native QWERTY or Web
132                }
133            },
134            
135            "A" | "ScanCodeA" => {
136                if use_azerty_mapping {
137                    variants.push(InputVariant::Key(KeyCode::Q));
138                } else {
139                    variants.push(InputVariant::Key(KeyCode::A));
140                }
141            },
142
143            "Z" | "ScanCodeZ" => {
144                if use_azerty_mapping {
145                    variants.push(InputVariant::Key(KeyCode::W));
146                } else {
147                    variants.push(InputVariant::Key(KeyCode::Z));
148                }
149            },
150
151            "Q" | "ScanCodeQ" => {
152                if use_azerty_mapping {
153                    variants.push(InputVariant::Key(KeyCode::A));
154                } else {
155                    variants.push(InputVariant::Key(KeyCode::Q));
156                }
157            },
158            
159            "M" => {
160                if use_azerty_mapping {
161                     // M is tricky on AZERTY (it's to the right of L, often semicolon on US)
162                     // But usually M on AZERTY maps to KeyCode::Semicolon in some raw modes
163                     // or KeyCode::M. For simplicity, we assume M is M logic here or comma.
164                     // This often needs specific tweaking per OS.
165                     variants.push(InputVariant::Key(KeyCode::M));
166                } else {
167                     variants.push(InputVariant::Key(KeyCode::M));
168                }
169            },
170
171            // --- INVARIANTS ---
172            "S" | "ScanCodeS" => variants.push(InputVariant::Key(KeyCode::S)),
173            "D" | "ScanCodeD" => variants.push(InputVariant::Key(KeyCode::D)),
174            
175            "Space" => variants.push(InputVariant::Key(KeyCode::Space)),
176            "Escape" => variants.push(InputVariant::Key(KeyCode::Escape)),
177            "Enter" => variants.push(InputVariant::Key(KeyCode::Enter)),
178            "Tab" => variants.push(InputVariant::Key(KeyCode::Tab)),
179            "LeftShift" => variants.push(InputVariant::Key(KeyCode::LeftShift)),
180            "RightShift" => variants.push(InputVariant::Key(KeyCode::RightShift)),
181            "LeftControl" => variants.push(InputVariant::Key(KeyCode::LeftControl)),
182            "RightControl" => variants.push(InputVariant::Key(KeyCode::RightControl)),
183            
184            "Up" => variants.push(InputVariant::Key(KeyCode::Up)),
185            "Down" => variants.push(InputVariant::Key(KeyCode::Down)),
186            "Left" => variants.push(InputVariant::Key(KeyCode::Left)),
187            "Right" => variants.push(InputVariant::Key(KeyCode::Right)),
188            
189            // Letters (simplified)
190            "B" => variants.push(InputVariant::Key(KeyCode::B)),
191            "C" => variants.push(InputVariant::Key(KeyCode::C)),
192            "E" => variants.push(InputVariant::Key(KeyCode::E)),
193            "F" => variants.push(InputVariant::Key(KeyCode::F)),
194            "G" => variants.push(InputVariant::Key(KeyCode::G)),
195            "H" => variants.push(InputVariant::Key(KeyCode::H)),
196            "I" => variants.push(InputVariant::Key(KeyCode::I)),
197            "J" => variants.push(InputVariant::Key(KeyCode::J)),
198            "K" => variants.push(InputVariant::Key(KeyCode::K)),
199            "L" => variants.push(InputVariant::Key(KeyCode::L)),
200            "N" => variants.push(InputVariant::Key(KeyCode::N)),
201            "O" => variants.push(InputVariant::Key(KeyCode::O)),
202            "P" => variants.push(InputVariant::Key(KeyCode::P)),
203            "R" => variants.push(InputVariant::Key(KeyCode::R)),
204            "T" => variants.push(InputVariant::Key(KeyCode::T)),
205            "U" => variants.push(InputVariant::Key(KeyCode::U)),
206            "V" => variants.push(InputVariant::Key(KeyCode::V)),
207            "X" => variants.push(InputVariant::Key(KeyCode::X)),
208            "Y" => variants.push(InputVariant::Key(KeyCode::Y)),
209
210            "0" => variants.push(InputVariant::Key(KeyCode::Key0)),
211            "1" => variants.push(InputVariant::Key(KeyCode::Key1)),
212            "2" => variants.push(InputVariant::Key(KeyCode::Key2)),
213            "3" => variants.push(InputVariant::Key(KeyCode::Key3)),
214            "4" => variants.push(InputVariant::Key(KeyCode::Key4)),
215            "5" => variants.push(InputVariant::Key(KeyCode::Key5)),
216            "6" => variants.push(InputVariant::Key(KeyCode::Key6)),
217            "7" => variants.push(InputVariant::Key(KeyCode::Key7)),
218            "8" => variants.push(InputVariant::Key(KeyCode::Key8)),
219            "9" => variants.push(InputVariant::Key(KeyCode::Key9)),
220
221            _ => {},
222        };
223        
224        variants
225    }
226}