Răsfoiți Sursa

feat(desktop-pet): 宠物形象配置解析(真实 JSON 测试)

liaoxg 1 zi în urmă
părinte
comite
149e8e52bf

+ 123 - 0
cfc-desktop-pet/src-tauri/src/config.rs

@@ -0,0 +1,123 @@
+use serde::{Deserialize, Serialize};
+use std::collections::HashMap;
+
+#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub enum PetType {
+    #[default]
+    Animal,
+    Human,
+    Custom,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "snake_case")]
+pub struct PetConfig {
+    #[serde(default = "default_version")]
+    pub version: String,
+    #[serde(rename = "type", default)]
+    pub pet_type: PetType,
+    #[serde(default = "default_species")]
+    pub species: String,
+    #[serde(default = "default_gender")]
+    pub gender: String,
+    #[serde(default)]
+    pub image_base_url: String,
+    #[serde(default = "default_image")]
+    pub default_image: String,
+    #[serde(default)]
+    pub actions: HashMap<String, String>,
+    #[serde(default)]
+    pub sounds: HashMap<String, String>,
+    #[serde(default)]
+    pub parameters: PetParameters,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "snake_case")]
+pub struct PetParameters {
+    #[serde(default = "default_move_distance")]
+    pub move_distance: i32,
+    #[serde(default = "default_animation_speed")]
+    pub animation_speed: f64,
+    #[serde(default = "default_interaction_timeout")]
+    pub interaction_timeout: i32,
+}
+
+impl Default for PetParameters {
+    fn default() -> Self {
+        Self {
+            move_distance: default_move_distance(),
+            animation_speed: default_animation_speed(),
+            interaction_timeout: default_interaction_timeout(),
+        }
+    }
+}
+
+fn default_version() -> String { "1.0".into() }
+fn default_species() -> String { "other".into() }
+fn default_gender() -> String { "neutral".into() }
+fn default_image() -> String { "standing.png".into() }
+fn default_move_distance() -> i32 { 15 }
+fn default_animation_speed() -> f64 { 1.0 }
+fn default_interaction_timeout() -> i32 { 300 }
+
+impl PetConfig {
+    /// 解析 JSON;actions 为空时补默认映射(idle/happy/busy/sleeping)
+    pub fn parse(json: &str) -> Result<Self, serde_json::Error> {
+        let mut cfg: PetConfig = serde_json::from_str(json)?;
+        if cfg.actions.is_empty() {
+            cfg.actions = [
+                ("idle".to_string(), "idle".to_string()),
+                ("happy".to_string(), "happy".to_string()),
+                ("busy".to_string(), "busy".to_string()),
+                ("sleeping".to_string(), "sleeping".to_string()),
+            ]
+            .into_iter()
+            .collect();
+        }
+        Ok(cfg)
+    }
+
+    pub fn get_action_for_state(&self, state_name: &str) -> String {
+        self.actions.get(state_name).cloned().unwrap_or_else(|| state_name.to_string())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    const XIBAO_JSON: &str = include_str!("../../src/DefaultPets/xibao/pet-config.json");
+
+    #[test]
+    fn parses_real_xibao_config() {
+        let cfg = PetConfig::parse(XIBAO_JSON).unwrap();
+        assert_eq!(cfg.version, "1.0");
+        assert!(matches!(cfg.pet_type, PetType::Animal));
+        assert_eq!(cfg.image_base_url, "DefaultPets/xibao/");
+        assert_eq!(cfg.default_image, "standing.png");
+        assert!(cfg.actions.contains_key("idle"));
+        assert_eq!(cfg.get_action_for_state("happy"), "wings_spread");
+    }
+
+    #[test]
+    fn fills_default_actions_when_empty() {
+        let json = r#"{"version":"1.0","type":"custom","actions":{}}"#;
+        let cfg = PetConfig::parse(json).unwrap();
+        assert_eq!(cfg.actions.len(), 4);
+        assert_eq!(cfg.actions["idle"], "idle");
+    }
+
+    #[test]
+    fn action_falls_back_to_state_name() {
+        let json = r#"{"type":"custom","actions":{"idle":"idle"}}"#;
+        let cfg = PetConfig::parse(json).unwrap();
+        assert_eq!(cfg.get_action_for_state("no_such_state"), "no_such_state");
+    }
+
+    #[test]
+    fn parse_error_on_bad_json() {
+        assert!(PetConfig::parse("not json").is_err());
+    }
+}

+ 1 - 0
cfc-desktop-pet/src-tauri/src/main.rs

@@ -1,6 +1,7 @@
 #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
 
 mod api;
+mod config;
 mod state;
 
 fn main() {