Split out card stuff to separate module, more docs
Getty Ritter
6 years ago
1 | use failure; | |
2 | use rrecutils; | |
3 | ||
4 | /// A `Card` always has a name, an icon, and some miscellaneous other information | |
5 | #[derive(Debug)] | |
6 | pub struct Card<'a> { | |
7 | pub name: &'a str, | |
8 | pub icon: String, | |
9 | pub card_type: CardType<'a>, | |
10 | } | |
11 | ||
12 | /// Different card types have different miscellaneous other pieces of | |
13 | /// information, but most of them are kept in a catch-all "Generic" | |
14 | /// case | |
15 | #[derive(Debug)] | |
16 | pub enum CardType<'a> { | |
17 | Monster { | |
18 | hp: &'a str, | |
19 | melee: Option<&'a str>, | |
20 | ranged: Option<&'a str>, | |
21 | arcane: Option<&'a str>, | |
22 | defense: Option<&'a str>, | |
23 | action: &'a str, | |
24 | effect: &'a str, | |
25 | }, | |
26 | Weapon { | |
27 | weapon_type: WeaponType, | |
28 | ability: &'a str, | |
29 | effect: &'a str, | |
30 | }, | |
31 | Generic { | |
32 | kind: Kind, | |
33 | effect: &'a str, | |
34 | }, | |
35 | } | |
36 | ||
37 | #[derive(Debug, Clone, Copy)] | |
38 | pub enum Kind { Ability, Equipment, Item, Plant, Treasure } | |
39 | ||
40 | #[derive(Debug, Clone, Copy)] | |
41 | pub enum WeaponType { Melee, Ranged, Arcane } | |
42 | ||
43 | impl<'a> Card<'a> { | |
44 | /// Here we parse out our in-program `Card` type from the recutils | |
45 | /// storage format of the card | |
46 | pub fn from_record(rec: &'a rrecutils::Record) | |
47 | -> Result<Card<'a>, failure::Error> | |
48 | { | |
49 | let name = rec.get("Name")?; | |
50 | // If this doesn't have a Type set by the Recutils parser, | |
51 | // then that's a problem! | |
52 | let typ = rec.get_type()?; | |
53 | ||
54 | let card_type = match typ { | |
55 | "Weapon" => CardType::Weapon { | |
56 | weapon_type: match rec.get("Type")? { | |
57 | "melee" => WeaponType::Melee, | |
58 | "ranged" => WeaponType::Ranged, | |
59 | // XXX FIXME IN THE FILE (but for ease of writing | |
60 | // this assumes that we might keep using the old | |
61 | // "magic" terminology instead of "arcane") | |
62 | "magic" | "arcane" => WeaponType::Arcane, | |
63 | t => bail!("Unknown weapon type: {}", t), | |
64 | }, | |
65 | ability: rec.get("Ability")?, | |
66 | effect: rec.get("Effect")?, | |
67 | }, | |
68 | ||
69 | "Monster" => CardType::Monster { | |
70 | hp: rec.get("HP")?, | |
71 | melee: rec.get("Melee").ok(), | |
72 | ranged: rec.get("Ranged").ok(), | |
73 | arcane: rec.get("Arcane").ok(), | |
74 | defense: rec.get("Defense").ok(), | |
75 | action: rec.get("Action")?, | |
76 | effect: rec.get("Effect")?, | |
77 | }, | |
78 | ||
79 | t => CardType::Generic { | |
80 | kind: match t { | |
81 | "Ability" => Kind::Ability, | |
82 | "Item" => Kind::Item, | |
83 | "Equipment" => Kind::Equipment, | |
84 | "Plant" => Kind::Plant, | |
85 | "Treasure" => Kind::Treasure, | |
86 | _ => bail!("Unknown card type: {}", t), | |
87 | }, | |
88 | effect: rec.get("Effect")?, | |
89 | } | |
90 | }; | |
91 | ||
92 | ||
93 | let lower_type = typ.to_lowercase(); | |
94 | let icon = format!( | |
95 | "../icons/{}-icon.svg", | |
96 | rec.get("Icon").unwrap_or( | |
97 | match typ { | |
98 | "Weapon" => rec.get("Type")?, | |
99 | _ => &lower_type, | |
100 | } | |
101 | ), | |
102 | ); | |
103 | ||
104 | Ok(Card { name, icon, card_type }) | |
105 | } | |
106 | ||
107 | pub fn type_name(&self) -> &'static str { | |
108 | match self.card_type { | |
109 | CardType::Weapon { .. } => "weapon", | |
110 | CardType::Monster { .. } => "monster", | |
111 | CardType::Generic { kind, .. } => match kind { | |
112 | Kind::Ability => "ability", | |
113 | Kind::Item => "item", | |
114 | Kind::Equipment => "equipment", | |
115 | Kind::Plant => "plant", | |
116 | Kind::Treasure => "treasure", | |
117 | }, | |
118 | } | |
119 | } | |
120 | ||
121 | pub fn card_name(&self) -> String { | |
122 | self.name.chars().filter_map(|c| { | |
123 | if c == ' '{ | |
124 | Some('_') | |
125 | } else if c.is_alphabetic() { | |
126 | Some(c.to_ascii_lowercase()) | |
127 | } else { | |
128 | None | |
129 | } | |
130 | }).collect() | |
131 | } | |
132 | } |
6 | 6 | extern crate rsvg; |
7 | 7 | |
8 | 8 | mod markup; |
9 | mod data; | |
9 | 10 | |
10 | 11 | use markup::Weight; |
11 | 12 | |
14 | 15 | |
15 | 16 | const SCALE: i32 = 3; |
16 | 17 | |
17 | #[derive(Debug)] | |
18 | struct Card<'a> { | |
19 | name: &'a str, | |
20 | icon: String, | |
21 | card_type: CardType<'a>, | |
22 | } | |
23 | ||
24 | #[derive(Debug)] | |
25 | enum CardType<'a> { | |
26 | Monster { | |
27 | hp: &'a str, | |
28 | melee: Option<&'a str>, | |
29 | ranged: Option<&'a str>, | |
30 | arcane: Option<&'a str>, | |
31 | defense: Option<&'a str>, | |
32 | action: &'a str, | |
33 | effect: &'a str, | |
34 | }, | |
35 | Weapon { | |
36 | weapon_type: WeaponType, | |
37 | ability: &'a str, | |
38 | effect: &'a str, | |
39 | }, | |
40 | Generic { | |
41 | kind: Kind, | |
42 | effect: &'a str, | |
43 | }, | |
44 | } | |
45 | ||
46 | #[derive(Debug, Clone, Copy)] | |
47 | enum Kind { Ability, Equipment, Item, Plant, Treasure } | |
48 | ||
49 | #[derive(Debug, Clone, Copy)] | |
50 | enum WeaponType { Melee, Ranged, Arcane } | |
51 | ||
52 | impl<'a> Card<'a> { | |
53 | fn from_record(rec: &'a rrecutils::Record) | |
54 | -> Result<Card<'a>, failure::Error> | |
55 | { | |
56 | let name = rec.get("Name")?; | |
57 | let typ = rec.get_type()?; | |
58 | ||
59 | let card_type = match typ { | |
60 | "Weapon" => CardType::Weapon { | |
61 | weapon_type: match rec.get("Type")? { | |
62 | "melee" => WeaponType::Melee, | |
63 | "ranged" => WeaponType::Ranged, | |
64 | "magic" | "arcane" => WeaponType::Arcane, | |
65 | t => bail!("Unknown weapon type: {}", t), | |
66 | }, | |
67 | ability: rec.get("Ability")?, | |
68 | effect: rec.get("Effect")?, | |
69 | }, | |
70 | "Monster" => CardType::Monster { | |
71 | hp: rec.get("HP")?, | |
72 | melee: rec.get("Melee").ok(), | |
73 | ranged: rec.get("Ranged").ok(), | |
74 | arcane: rec.get("Arcane").ok(), | |
75 | defense: rec.get("Defense").ok(), | |
76 | action: rec.get("Action")?, | |
77 | effect: rec.get("Effect")?, | |
78 | }, | |
79 | t => CardType::Generic { | |
80 | kind: match t { | |
81 | "Ability" => Kind::Ability, | |
82 | "Item" => Kind::Item, | |
83 | "Equipment" => Kind::Equipment, | |
84 | "Plant" => Kind::Plant, | |
85 | "Treasure" => Kind::Treasure, | |
86 | _ => bail!("Unknown card type: {}", t), | |
87 | }, | |
88 | effect: rec.get("Effect")?, | |
89 | } | |
90 | }; | |
91 | ||
92 | ||
93 | let lower_type = typ.to_lowercase(); | |
94 | let icon = format!( | |
95 | "../icons/{}-icon.svg", | |
96 | rec.get("Icon").unwrap_or( | |
97 | match typ { | |
98 | "Weapon" => rec.get("Type")?, | |
99 | _ => &lower_type, | |
100 | } | |
101 | ), | |
102 | ); | |
103 | ||
104 | Ok(Card { name, icon, card_type }) | |
105 | } | |
106 | ||
107 | fn type_name(&self) -> &'static str { | |
108 | match self.card_type { | |
109 | CardType::Weapon { .. } => "weapon", | |
110 | CardType::Monster { .. } => "monster", | |
111 | CardType::Generic { kind, .. } => match kind { | |
112 | Kind::Ability => "ability", | |
113 | Kind::Item => "item", | |
114 | Kind::Equipment => "equipment", | |
115 | Kind::Plant => "plant", | |
116 | Kind::Treasure => "treasure", | |
117 | }, | |
118 | } | |
119 | } | |
120 | ||
121 | fn card_name(&self) -> String { | |
122 | self.name.chars().filter_map(|c| { | |
123 | if c == ' '{ | |
124 | Some('_') | |
125 | } else if c.is_alphabetic() { | |
126 | Some(c.to_ascii_lowercase()) | |
127 | } else { | |
128 | None | |
129 | } | |
130 | }).collect() | |
131 | } | |
132 | } | |
133 | ||
134 | fn draw_card(card: &Card) -> Result<(), failure::Error> { | |
135 | use CardType::*; | |
18 | fn draw_card(card: &data::Card) -> Result<(), failure::Error> { | |
19 | use data::CardType::*; | |
136 | 20 | |
137 | 21 | let surf = cairo::ImageSurface::create( |
138 | 22 | cairo::Format::Rgb24, |
246 | 130 | |
247 | 131 | for w in recfile.iter() { |
248 | 132 | if w.rec_type.is_some() { |
249 |
let card = |
|
133 | let card = data::Card::from_record(w)?; | |
250 | 134 | draw_card(&card)?; |
251 | 135 | } |
252 | 136 | } |