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    /// Legacy client-local hotbar ability ids (unused for play casting — server persists hotbar).
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::new()
92}
93
94impl Default for ClientKeyBindings {
95    fn default() -> Self {
96        Self {
97            target_t1: default_target_t1(),
98            target_t2: default_target_t2(),
99            auto_t1: default_auto_t1(),
100            auto_t2: default_auto_t2(),
101            loadout_menu: default_loadout(),
102            rotation_editor: default_rotation_editor(),
103            dodge: default_dodge(),
104            lunge: default_lunge(),
105            block: default_block(),
106            clear_target_t1: default_clear_t1(),
107            clear_target_t2: default_clear_t2(),
108            hotbar: default_hotbar(),
109        }
110    }
111}
112
113impl ClientKeyBindings {
114    /// Legacy lookup for `keys.hotbar` — unused for play (server-persisted hotbar on GameState).
115    pub fn hotbar_ability(&self, slot_1_to_9: u8) -> Option<&str> {
116        if !(1..=9).contains(&slot_1_to_9) {
117            return None;
118        }
119        self.hotbar
120            .get((slot_1_to_9 - 1) as usize)
121            .and_then(|a| a.as_deref())
122            .filter(|id| !id.is_empty())
123    }
124}
125
126fn find_settings_path() -> PathBuf {
127    let manifest = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
128    for ancestor in manifest.ancestors() {
129        let candidate = ancestor.join("assets/config/client-settings.yaml");
130        if candidate.is_file() {
131            return candidate;
132        }
133    }
134    PathBuf::from("assets/config/client-settings.yaml")
135}
136
137pub fn load_client_settings() -> ClientSettings {
138    let path = find_settings_path();
139    let Ok(raw) = std::fs::read_to_string(&path) else {
140        return ClientSettings::default();
141    };
142    serde_yaml::from_str(&raw).unwrap_or_default()
143}