1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
//! # The XML `<Behavior>` format
//!
//! This is used in:
//! - The `ObjectBehaviors` table

use serde::{Deserialize, Serialize};

mod element {
    use std::{fmt, str::FromStr};

    use serde::{
        de::Deserializer,
        ser::{SerializeMap, Serializer},
        Deserialize, Serialize,
    };

    #[derive(Serialize, Deserialize)]
    struct Element<T> {
        #[serde(rename = "$value")]
        value: T,
    }

    pub fn deserialize<'de, D: Deserializer<'de>, T: FromStr>(
        deserializer: D,
    ) -> Result<T, D::Error>
    where
        T::Err: fmt::Display,
    {
        Element::<String>::deserialize(deserializer).and_then(|e| {
            e.value
                .parse()
                .map_err(|e: T::Err| <D::Error as serde::de::Error>::custom(e.to_string()))
        })
    }

    pub fn serialize<S: Serializer, T: Serialize>(
        value: &T,
        serializer: S,
    ) -> Result<S::Ok, S::Error> {
        let mut map = serializer.serialize_map(Some(1))?;
        map.serialize_entry("$value", value)?;
        map.end()
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
/// One model behavior
pub struct Behavior {
    /// Version 1.0
    version: String,

    /// Name of this behavior
    #[serde(with = "element", rename = "Name")]
    name: String,

    /// Actions associated with the behavior
    #[serde(rename = "CompoundAction")]
    actions: Vec<CompoundAction>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
/// A group of actions
pub struct CompoundAction {
    /// The actions for this compound
    #[serde(rename = "Action")]
    pub actions: Vec<Action>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "Type", rename_all = "PascalCase")]
/// A single action
pub enum Action {
    /// When the object is attacked
    OnAttack,
    /// When a player comes close
    OnEnterProximity {
        /// Distance to trigger
        #[serde(with = "element", rename = "Distance")]
        distance: f32,
    },
    /// When receiving a chat message
    OnChat {
        /// Message to post to chat
        #[serde(with = "element", rename = "Message")]
        message: String,
    },
    /// Play a sound
    PlaySound {
        /// GUID (`{...}`) of the audio
        #[serde(with = "element")]
        sound: String,
    },
    /// Send a chat message
    Chat {
        /// Message to post to chat
        #[serde(with = "element", rename = "Message")]
        message: String,
    },
    /// Smash the object
    Smash {
        /// Unclear
        #[serde(with = "element", rename = "Opacity")]
        opacity: f32,
        /// Unclear
        #[serde(with = "element")]
        force: f32,
    },
    /// Wait for some time
    Wait {
        #[serde(with = "element", rename = "Delay")]
        /// The amount of time to wait
        delay: f32,
    },
    /// Revert a smashing
    UnSmash {
        #[serde(with = "element", rename = "Duration")]
        /// Unclear
        duration: f32,
    },
    /// Move to the interactor
    MoveToInteractor {
        #[serde(with = "element", rename = "Distance")]
        /// Unclear
        distance: f32,
    },
}

#[cfg(test)]
mod tests {
    use super::Behavior;

    #[test]
    fn test_deserialize() {
        let text = include_str!("../test/Droid.xml");
        let _data: Behavior = quick_xml::de::from_str(text).unwrap();
    }
}