Split out card stuff to separate module, more docs
    
    
      
        Getty Ritter
        7 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 | } |