Skip to main content

flatland_client_lib/
settings.rs

1//! Client keybind settings (`assets/config/client-settings.yaml`).
2
3use std::path::PathBuf;
4
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
8pub struct ClientSettings {
9    #[serde(default = "default_version")]
10    pub version: String,
11    #[serde(default)]
12    pub keys: ClientKeyBindings,
13}
14
15fn default_version() -> String {
16    "1".into()
17}
18
19impl Default for ClientSettings {
20    fn default() -> Self {
21        Self {
22            version: default_version(),
23            keys: ClientKeyBindings::default(),
24        }
25    }
26}
27
28#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
29pub struct ClientKeyBindings {
30    #[serde(default = "default_target_t1")]
31    pub target_t1: String,
32    #[serde(default = "default_target_t2")]
33    pub target_t2: String,
34    #[serde(default = "default_auto_t1")]
35    pub auto_t1: String,
36    #[serde(default = "default_auto_t2")]
37    pub auto_t2: String,
38    #[serde(default = "default_loadout")]
39    pub loadout_menu: String,
40    #[serde(default = "default_rotation_editor")]
41    pub rotation_editor: String,
42    #[serde(default = "default_dodge")]
43    pub dodge: String,
44    #[serde(default = "default_lunge")]
45    pub lunge: String,
46    #[serde(default = "default_block")]
47    pub block: String,
48    #[serde(default = "default_clear_t1")]
49    pub clear_target_t1: String,
50    #[serde(default = "default_clear_t2")]
51    pub clear_target_t2: String,
52    /// Ability ids for keys `1`–`9` (index 0 = key 1). Empty / null = unbound.
53    #[serde(default = "default_hotbar")]
54    pub hotbar: Vec<Option<String>>,
55}
56
57fn default_target_t1() -> String {
58    "Tab".into()
59}
60fn default_target_t2() -> String {
61    "Shift+Tab".into()
62}
63fn default_auto_t1() -> String {
64    "r".into()
65}
66fn default_auto_t2() -> String {
67    "Shift+r".into()
68}
69fn default_loadout() -> String {
70    "l".into()
71}
72fn default_rotation_editor() -> String {
73    "o".into()
74}
75fn default_dodge() -> String {
76    "c".into()
77}
78fn default_lunge() -> String {
79    "Shift+Space".into()
80}
81fn default_block() -> String {
82    "q".into()
83}
84fn default_clear_t1() -> String {
85    "0".into()
86}
87fn default_clear_t2() -> String {
88    "Shift+0".into()
89}
90fn default_hotbar() -> Vec<Option<String>> {
91    vec![
92        Some("heal_touch".into()),
93        Some("poison_dart".into()),
94        Some("fireball".into()),
95        Some("arrow_shot".into()),
96        None,
97        None,
98        None,
99        None,
100        None,
101    ]
102}
103
104impl Default for ClientKeyBindings {
105    fn default() -> Self {
106        Self {
107            target_t1: default_target_t1(),
108            target_t2: default_target_t2(),
109            auto_t1: default_auto_t1(),
110            auto_t2: default_auto_t2(),
111            loadout_menu: default_loadout(),
112            rotation_editor: default_rotation_editor(),
113            dodge: default_dodge(),
114            lunge: default_lunge(),
115            block: default_block(),
116            clear_target_t1: default_clear_t1(),
117            clear_target_t2: default_clear_t2(),
118            hotbar: default_hotbar(),
119        }
120    }
121}
122
123impl ClientKeyBindings {
124    /// Ability bound to hotbar key `1`–`9`, if any.
125    pub fn hotbar_ability(&self, slot_1_to_9: u8) -> Option<&str> {
126        if !(1..=9).contains(&slot_1_to_9) {
127            return None;
128        }
129        self.hotbar
130            .get((slot_1_to_9 - 1) as usize)
131            .and_then(|a| a.as_deref())
132            .filter(|id| !id.is_empty())
133    }
134}
135
136fn find_settings_path() -> PathBuf {
137    let manifest = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
138    for ancestor in manifest.ancestors() {
139        let candidate = ancestor.join("assets/config/client-settings.yaml");
140        if candidate.is_file() {
141            return candidate;
142        }
143    }
144    PathBuf::from("assets/config/client-settings.yaml")
145}
146
147pub fn load_client_settings() -> ClientSettings {
148    let path = find_settings_path();
149    let Ok(raw) = std::fs::read_to_string(&path) else {
150        return ClientSettings::default();
151    };
152    serde_yaml::from_str(&raw).unwrap_or_default()
153}