1#![allow(non_snake_case)]
9
10use std::collections::HashMap;
11
12#[macro_use] extern crate error;
13pub mod monster;
14pub mod breed;
15pub mod xml;
16pub mod skill;
17
18use monster::{Monster, Family};
19use breed::Formula;
20use skill::Skill;
21use crate::error::Error;
22
23#[derive(Default, Clone)]
25pub struct GameData
26{
27 pub monster_data: monster::Info,
29 pub breed_formulae: Vec<Formula>,
31 pub skills: HashMap<String, Skill>,
33}
34
35impl GameData
36{
37 pub fn fromXML(x: &[u8]) -> Result<Self, Error>
39 {
40 let mut monster_data = monster::Info::default();
41 let mut breed_formulae = Vec::new();
42 let mut skills = HashMap::new();
43
44 let mut p = xml::Parser::new();
45 p.addTagHandler("families", |_, tag| {
46 monster_data = monster::Info::fromXML(tag)?;
47 Ok(())
48 });
49 p.addTagHandler("breed", |_, tag| {
50 breed_formulae.push(Formula::fromXML(tag)?);
51 Ok(())
52 });
53 p.addTagHandler("skill-data", |_, tag| {
54 let s = Skill::fromXML(tag)?;
55 skills.insert(s.name.clone(), s);
56 Ok(())
57 });
58 p.parse(x)?;
59 drop(p);
60
61 let keys: Vec<String> = skills.keys().map(|k| k.to_owned()).collect();
63 for s in keys
64 {
65 if let Some(precursor) = skills[&s].upgrade_from.clone()
66 {
67 skills.get_mut(&precursor).ok_or_else(
68 || rterr!("{} is upgraded from {}, which is not found.",
69 s, precursor))?
70 .upgrade_to = Some(s.to_owned());
71 }
72 }
73
74 Ok(Self { monster_data, breed_formulae, skills })
75 }
76
77 pub fn family(&self, name: &str) -> Option<&Family>
79 {
80 self.monster_data.families.iter()
81 .find(|f| f.name == name)
82 }
83
84 pub fn monster(&self, name: &str) -> Option<&Monster>
86 {
87 self.monster_data.monsters.iter()
88 .find(|m| m.name == name)
89 }
90
91 pub fn skill(&self, name: &str) -> Option<&Skill>
93 {
94 self.skills.get(name)
95 }
96
97 pub fn monstersWithSkill<'a>(&'a self, skill_name: &'a str) ->
99 impl Iterator<Item = &Monster> + 'a
100 {
101 self.monster_data.monsters.iter().filter(
102 move |m| m.skills.iter().any(|a| a == skill_name))
103 }
104
105 pub fn monstersInFamily<'a>(&'a self, family: &'a Family) ->
107 impl Iterator<Item = &Monster> + 'a
108 {
109 self.monster_data.monsters.iter().filter(
110 |m| family.members.contains(&m.name))
111 }
112
113 pub fn usedInFormulae<'a>(&'a self, parent: &'a breed::Parent) ->
115 impl Iterator<Item = &Formula> + 'a
116 {
117 self.breed_formulae.iter().filter(
118 move |form| form.base.iter().find(|p| &p.parent == parent).is_some()
119 || form.mate.iter().find(|p| &p.parent == parent).is_some())
120 }
121
122 pub fn breedFromFormulae<'a>(&'a self, offspring: &'a str) ->
124 impl Iterator<Item = &Formula> + 'a
125 {
126 self.breed_formulae.iter()
127 .filter(move |form| &form.offspring == offspring)
128 }
129
130 fn skillUpgrades<'a>(&'a self, skill: &'a Skill) -> Vec<&'a Skill>
133 {
134 let mut result: Vec<&Skill> = vec![skill];
135 if let Some(name) = &skill.upgrade_to
136 {
137 let upgrade = self.skill(name).unwrap();
138 result.append(&mut self.skillUpgrades(upgrade));
139 }
140 result
141 }
142
143 fn skillFirstPrecursor<'a>(&'a self, skill: &'a Skill) -> &'a Skill
144 {
145 if let Some(name) = &skill.upgrade_from
146 {
147 let precursor = self.skill(name).unwrap();
148 self.skillFirstPrecursor(precursor)
149 }
150 else
151 {
152 skill
153 }
154 }
155
156 pub fn skillUpgradePath<'a>(&'a self, skill: &'a Skill) -> Vec<&'a Skill>
169 {
170 if skill.upgrade_from.is_some() || skill.upgrade_to.is_some()
171 {
172 self.skillUpgrades(self.skillFirstPrecursor(skill))
173 }
174 else
175 {
176 vec![skill,]
177 }
178 }
179
180 pub fn skillCombinesInto<'a>(&'a self, skill: &'a Skill) ->
183 impl Iterator<Item = &Skill> + 'a
184 {
185 let mut upgrades = self.skillUpgrades(skill);
186 upgrades.push(skill);
187 self.skills.values().filter(move |s| {
188 for upgrade in &upgrades
189 {
190 if s.combine_from.contains(&upgrade.name)
191 {
192 return true;
193 }
194 }
195 false
196 })
197 }
198}
199
200#[cfg(test)]
203mod tests {
204 use super::*;
205
206 use anyhow::Result;
207
208 #[test]
209 fn readXML() -> Result<()>
210 {
211 let xml = r#"<monster-data>
212 <families>
213 <family name="slime">
214 <monsters>
215 <monster name="SpotSlime" in_story="true">
216 <spawn-locations>
217 <location>
218 <map>Oasis Key World</map>
219 <description>Near Door Shrine</description>
220 </location>
221 </spawn-locations>
222 <skills>
223 <skill>CallHelp</skill>
224 <skill>LushLicks</skill>
225 <skill>Imitate</skill>
226 </skills>
227 <growth agl="17" int="8" maxlvl="35" atk="17" mp="1" exp="10" hp="17" def="4"/>
228 </monster>
229 <monster name="BoxSlime" in_story="true">
230 <spawn-locations>
231 <location>
232 <map>Sky Key World</map>
233 <description>Heaven Helm Cave Floor 1</description>
234 </location>
235 </spawn-locations>
236 <skills>
237 <skill>Blaze</skill>
238 <skill>Upper</skill>
239 <skill>Ramming</skill>
240 </skills>
241 <growth agl="14" int="13" maxlvl="50" atk="14" mp="10" exp="11" hp="11" def="19"/>
242 </monster>
243 </monsters>
244 </family>
245 </families>
246 <breeds>
247 <breed target="Zoma">
248 <base>
249 <breed-requirement monster="DracoLord1"/>
250 <breed-requirement monster="DracoLord2"/>
251 </base>
252 <mate>
253 <breed-requirement monster="Sidoh"/>
254 </mate>
255 </breed>
256 </breeds>
257 <skills-data>
258 <skill-data name="VacuSlash">
259 <skill-requirements lvl="11" hp="77" mp="34" atk="66" def="0" agl="0" int="76"/>
260 <combine-from>
261 <skill>WindBeast</skill>
262 <skill>ChargeUp</skill>
263 </combine-from>
264 </skill-data>
265 <skill-data name="Vacuum">
266 <skill-requirements lvl="19" hp="112" mp="0" atk="114" def="0" agl="132" int="0"/>
267 <precursor>VacuSlash</precursor>
268 </skill-data>
269 </skills-data>
270</monster-data>
271"#;
272 let data = GameData::fromXML(xml.as_bytes())?;
273 assert_eq!(data.breed_formulae.len(), 1);
274 assert_eq!(data.monster_data.monsters.len(), 2);
275 assert_eq!(data.monster_data.families.len(), 1);
276 assert_eq!(data.monster_data.families[0].members.len(), 2);
277 assert_eq!(data.monster_data.monsters[0].skills.len(), 3);
278 assert_eq!(data.skills.len(), 2);
279 assert_eq!(data.skill("VacuSlash").unwrap().combine_from,
280 vec![String::from("WindBeast"), String::from("ChargeUp")]);
281 assert_eq!(data.skill("VacuSlash").unwrap().requirements.hp, 77);
282 assert_eq!(data.skill("VacuSlash").unwrap().upgrade_from, None);
283 assert_eq!(data.skill("Vacuum").unwrap().upgrade_from,
284 Some(String::from("VacuSlash")));
285 Ok(())
286 }
287
288 #[test]
289 fn gameDataOk() -> Result<()>
290 {
291 let content = include_bytes!("../../monster-data.xml");
292 GameData::fromXML(content)?;
293 Ok(())
294 }
295}