Skip to main content

flatland_presentation/
player_config.rs

1//! Designer-authored player gfx presentation (`assets/gfx/player-presentation.yaml`).
2
3use std::path::Path;
4
5use serde::Deserialize;
6
7use crate::SpriteModeMap;
8
9#[derive(Debug, Clone, Default, Deserialize)]
10pub struct PlayerPresentationDoc {
11    #[serde(default)]
12    pub player_presentation: PlayerPresentationConfig,
13}
14
15#[derive(Debug, Clone, Default, Deserialize)]
16pub struct PlayerPresentationConfig {
17    #[serde(default)]
18    pub tile_id: Option<String>,
19    /// Paperdoll skin id under `assets/paperdoll/skins/`.
20    #[serde(default)]
21    pub paperdoll_ref: Option<String>,
22    #[serde(default)]
23    pub sprite_modes: SpriteModeMap,
24}
25
26impl PlayerPresentationConfig {
27    pub fn empty() -> Self {
28        Self::default()
29    }
30}
31
32pub fn default_player_presentation_path() -> Option<std::path::PathBuf> {
33    if let Ok(p) = std::env::var("FLATLAND_PLAYER_PRESENTATION") {
34        let path = std::path::PathBuf::from(p);
35        if path.is_file() {
36            return Some(path);
37        }
38    }
39    if let Ok(sprites) = std::env::var("FLATLAND_SPRITES_DIR") {
40        let path = std::path::PathBuf::from(sprites).join("player-presentation.yaml");
41        if path.is_file() {
42            return Some(path);
43        }
44    }
45    // Installed-client sync places the file at the rev root next to manifest.yaml.
46    if let Ok(home) = std::env::var("HOME") {
47        let current = std::path::PathBuf::from(home)
48            .join(".flatland3/assets/current/player-presentation.yaml");
49        if current.is_file() {
50            return Some(current);
51        }
52    }
53    let mut dir = std::env::current_dir().ok()?;
54    for _ in 0..8 {
55        let candidate = dir.join("assets/gfx/player-presentation.yaml");
56        if candidate.is_file() {
57            return Some(candidate);
58        }
59        if !dir.pop() {
60            break;
61        }
62    }
63    None
64}
65
66pub fn load_player_presentation(
67    path: impl AsRef<Path>,
68) -> anyhow::Result<PlayerPresentationConfig> {
69    let raw = std::fs::read_to_string(path.as_ref())?;
70    let doc: PlayerPresentationDoc = serde_yaml::from_str(&raw)?;
71    Ok(doc.player_presentation)
72}
73
74pub fn load_player_presentation_default() -> PlayerPresentationConfig {
75    default_player_presentation_path()
76        .and_then(|p| load_player_presentation(&p).ok())
77        .unwrap_or_default()
78}
79
80#[cfg(test)]
81mod tests {
82    use super::*;
83
84    #[test]
85    fn loads_repo_default() {
86        let path = default_player_presentation_path().expect("player-presentation.yaml");
87        let cfg = load_player_presentation(&path).expect("parse");
88        assert!(cfg.tile_id.is_some() || !cfg.sprite_modes.is_empty());
89    }
90}