1use bevy::{
5 asset::{io::Reader, AssetLoader, LoadContext},
6 prelude::*,
7};
8use serde::de::Error;
9use thiserror::Error;
10
11use crate::runes::{RuneDeserializeError, RuneRegistry, Rune};
12
13#[derive(Asset, TypePath)]
37pub struct Spell {
38 pub name: String,
40 pub description: String,
42 pub runes: Vec<Box<dyn Rune>>,
44}
45
46impl Spell {
47 pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
49 Self {
50 name: name.into(),
51 description: description.into(),
52 runes: Vec::new(),
53 }
54 }
55
56 pub fn with_rune(mut self, rune: impl Rune + 'static) -> Self {
58 self.runes.push(Box::new(rune));
59 self
60 }
61}
62
63#[derive(Error, Debug)]
69pub enum SpellLoadError {
70 #[error("I/O error reading spell asset: {0}")]
71 Io(#[from] std::io::Error),
72 #[error("RON parse error in spell asset: {0}")]
73 Ron(#[from] ron::Error),
74 #[error("rune deserialization error: {0}")]
75 Rune(#[from] RuneDeserializeError),
76}
77
78#[derive(TypePath)]
83pub struct SpellAssetLoader {
84 pub(crate) registry: RuneRegistry,
85}
86
87impl AssetLoader for SpellAssetLoader {
88 type Asset = Spell;
89 type Settings = ();
90 type Error = SpellLoadError;
91
92 async fn load(
93 &self,
94 reader: &mut dyn Reader,
95 _settings: &Self::Settings,
96 _load_context: &mut LoadContext<'_>,
97 ) -> Result<Self::Asset, Self::Error> {
98 let mut bytes = Vec::new();
99 reader.read_to_end(&mut bytes).await?;
100 spell_from_ron(&bytes, &self.registry)
101 }
102
103 fn extensions(&self) -> &[&str] {
104 &["spell"]
105 }
106}
107
108fn spell_from_ron(bytes: &[u8], registry: &RuneRegistry) -> Result<Spell, SpellLoadError> {
109 let value: ron::value::Value = ron::de::from_bytes(bytes).map_err(|e| SpellLoadError::Ron(e.into()))?;
111 let obj = if let ron::value::Value::Map(m) = value {
112 m
113 } else {
114 return Err(SpellLoadError::Ron(ron::Error::custom(
115 "expected top-level RON map",
116 )));
117 };
118 let get_str = |key: &str| {
120 obj.get(&ron::value::Value::String(key.to_string()))
121 .and_then(|v| if let ron::value::Value::String(s) = v { Some(s) } else { None })
122 };
123 let name = get_str("name").ok_or_else(|| {
124 SpellLoadError::Ron(ron::Error::custom("missing or invalid 'name' field"))
125 })?;
126 let description = get_str("description").ok_or_else(|| {
127 SpellLoadError::Ron(ron::Error::custom("missing or invalid 'description' field"))
128 })?;
129 let runes_value = obj
130 .get(&ron::value::Value::String("runes".to_string()))
131 .ok_or_else(|| SpellLoadError::Ron(ron::Error::custom("missing 'runes' field")))?;
132 let runes_array = if let ron::value::Value::Seq(s) = runes_value {
133 s
134 } else {
135 return Err(SpellLoadError::Ron(ron::Error::custom(
136 "invalid 'runes' field (expected sequence)",
137 )));
138 };
139 let mut runes = Vec::new();
140 for rune_value in runes_array {
141 let rune = registry.deserialize_rune(rune_value.clone())?;
142 runes.push(rune);
143 }
144 Ok(Spell {
145 name: name.to_string(),
146 description: description.to_string(),
147 runes,
148 })
149}