| 1 | 1 |
extern crate cairo;
|
| 2 | 2 |
#[macro_use] extern crate failure;
|
| 3 | |
extern crate heck;
|
| 4 | 3 |
extern crate pango;
|
| 5 | 4 |
extern crate pangocairo;
|
| 6 | 5 |
extern crate rrecutils;
|
| 7 | 6 |
extern crate rsvg;
|
| 8 | 7 |
|
| 9 | 8 |
use std::{fs,io};
|
| 10 | |
use heck::TitleCase;
|
| 11 | 9 |
use pango::LayoutExt;
|
| 12 | 10 |
use rsvg::HandleExt;
|
| 13 | 11 |
|
|
| 42 | 40 |
},
|
| 43 | 41 |
}
|
| 44 | 42 |
|
| 45 | |
#[derive(Debug)]
|
| 43 |
#[derive(Debug, Clone, Copy)]
|
| 46 | 44 |
enum Kind { Ability, Equipment, Item, Plant, Treasure }
|
| 47 | 45 |
|
| 48 | |
#[derive(Debug)]
|
| 46 |
#[derive(Debug, Clone, Copy)]
|
| 49 | 47 |
enum WeaponType { Melee, Ranged, Arcane }
|
| 50 | 48 |
|
| 51 | 49 |
impl<'a> Card<'a> {
|
|
| 53 | 51 |
-> Result<Card<'a>, failure::Error>
|
| 54 | 52 |
{
|
| 55 | 53 |
let name = rec.get("Name")?;
|
| 56 | |
|
| 57 | |
let card_type = match rec.get_type()? {
|
| 54 |
let typ = rec.get_type()?;
|
| 55 |
|
| 56 |
let card_type = match typ {
|
| 58 | 57 |
"Weapon" => CardType::Weapon {
|
| 59 | 58 |
weapon_type: match rec.get("Type")? {
|
| 60 | 59 |
"melee" => WeaponType::Melee,
|
|
| 88 | 87 |
};
|
| 89 | 88 |
|
| 90 | 89 |
|
| 91 | |
let icon = format!("icon");
|
| 90 |
let lower_type = typ.to_lowercase();
|
| 91 |
let icon = format!(
|
| 92 |
"../icons/{}-icon.svg",
|
| 93 |
rec.get("Icon").unwrap_or(
|
| 94 |
match typ {
|
| 95 |
"Weapon" => rec.get("Type")?,
|
| 96 |
_ => &lower_type,
|
| 97 |
}
|
| 98 |
),
|
| 99 |
);
|
| 92 | 100 |
|
| 93 | 101 |
Ok(Card { name, icon, card_type })
|
| 94 | 102 |
}
|
| 95 | |
}
|
| 96 | |
|
| 97 | |
fn draw_card(rec: &rrecutils::Record) -> Result<(), failure::Error> {
|
| 98 | |
let typ = match rec.rec_type {
|
| 99 | |
Some(ref t) => t,
|
| 100 | |
None => return Ok(()),
|
| 101 | |
};
|
| 102 | |
let _ = Card::from_record(rec)?;
|
| 103 | |
let name = rec.get("Name")?;
|
| 103 |
|
| 104 |
fn type_name(&self) -> &'static str {
|
| 105 |
match self.card_type {
|
| 106 |
CardType::Weapon { .. } => "weapon",
|
| 107 |
CardType::Monster { .. } => "monster",
|
| 108 |
CardType::Generic { kind, .. } => match kind {
|
| 109 |
Kind::Ability => "ability",
|
| 110 |
Kind::Item => "item",
|
| 111 |
Kind::Equipment => "equipment",
|
| 112 |
Kind::Plant => "plant",
|
| 113 |
Kind::Treasure => "treasure",
|
| 114 |
},
|
| 115 |
}
|
| 116 |
}
|
| 117 |
|
| 118 |
fn card_name(&self) -> String {
|
| 119 |
self.name.chars().filter_map(|c| {
|
| 120 |
if c == ' '{
|
| 121 |
Some('_')
|
| 122 |
} else if c.is_alphabetic() {
|
| 123 |
Some(c.to_ascii_lowercase())
|
| 124 |
} else {
|
| 125 |
None
|
| 126 |
}
|
| 127 |
}).collect()
|
| 128 |
}
|
| 129 |
}
|
| 130 |
|
| 131 |
|
| 132 |
|
| 133 |
fn draw_card(card: &Card) -> Result<(), failure::Error> {
|
| 134 |
use CardType::*;
|
| 104 | 135 |
|
| 105 | 136 |
let surf = cairo::ImageSurface::create(
|
| 106 | 137 |
cairo::Format::Rgb24,
|
|
| 129 | 160 |
layout.set_font_description(&font);
|
| 130 | 161 |
|
| 131 | 162 |
ctx.move_to(100.0, 8.0);
|
| 132 | |
let title = match typ.as_ref() {
|
| 133 | |
"Weapon" => format!(
|
| 134 | |
"{} (Weapon, {})",
|
| 135 | |
name,
|
| 136 | |
rec.get("Type")?
|
| 137 | |
.to_title_case(),
|
| 138 | |
),
|
| 139 | |
"Ability" | "Equipment" | "Item" | "Plant" | "Treasure" =>
|
| 140 | |
format!("{} ({})", name, typ),
|
| 141 | |
"Monster" => {
|
| 163 |
let title = match card.card_type {
|
| 164 |
Weapon { ref weapon_type, .. } =>
|
| 165 |
format!("{} (Weapon, {:?})", card.name, weapon_type),
|
| 166 |
Monster { ref hp, ref melee, ref ranged, ref arcane, ref defense, .. } => {
|
| 142 | 167 |
let mut buf = String::new();
|
| 143 | |
let hp = rec.get("HP")?;
|
| 144 | |
buf.push_str(&format!("{} (Beast <span font='8' font_weight='heavy'>HP</span>{}", name, hp));
|
| 145 | |
if let Ok(m) = rec.get("Melee") {
|
| 168 |
buf.push_str(&format!(
|
| 169 |
"{} (Beast <span font='8' font_weight='heavy'>HP</span>{}",
|
| 170 |
card.name,
|
| 171 |
hp,
|
| 172 |
));
|
| 173 |
if let &Some(ref m) = melee {
|
| 146 | 174 |
buf.push_str(&format!(" <span font='8' font_weight='heavy'>M</span>{}", m));
|
| 147 | 175 |
}
|
| 148 | |
if let Ok(m) = rec.get("Ranged") {
|
| 176 |
if let &Some(ref m) = ranged {
|
| 149 | 177 |
buf.push_str(&format!(" <span font='8' font_weight='heavy'>R</span>{}", m));
|
| 150 | 178 |
}
|
| 151 | |
if let Ok(m) = rec.get("Arcane") {
|
| 179 |
if let &Some(ref m) = arcane {
|
| 152 | 180 |
buf.push_str(&format!(" <span font='8' font_weight='heavy'>A</span>{}", m));
|
| 153 | 181 |
}
|
| 154 | |
if let Ok(m) = rec.get("Defense") {
|
| 182 |
if let &Some(ref m) = defense {
|
| 155 | 183 |
buf.push_str(&format!(" <span font='8' font_weight='heavy'>D</span>{}", m));
|
| 156 | 184 |
}
|
| 157 | 185 |
buf.push_str(")");
|
| 158 | |
|
| 159 | 186 |
buf
|
| 160 | 187 |
}
|
| 161 | |
_ => bail!("Unknown type: {}", typ),
|
| 188 |
Generic { ref kind, .. } => format!("{} ({:?})", card.name, kind),
|
| 162 | 189 |
};
|
| 163 | 190 |
|
| 164 | 191 |
let (attrs, text, _) = pango::parse_markup(&title, '\0')?;
|
|
| 176 | 203 |
|
| 177 | 204 |
let (attrs, text, _) = {
|
| 178 | 205 |
let mut buf = String::new();
|
| 179 | |
match typ.as_ref() {
|
| 180 | |
"Weapon" => {
|
| 206 |
match card.card_type {
|
| 207 |
Weapon { ability, effect, .. } => {
|
| 181 | 208 |
buf.push_str("<span font_weight=\"bold\">");
|
| 182 | |
buf.push_str(rec.get("Ability")?);
|
| 209 |
buf.push_str(ability);
|
| 183 | 210 |
buf.push_str(":</span> ");
|
| 184 | |
buf.push_str(rec.get("Effect")?);
|
| 185 | |
}
|
| 186 | |
"Ability" | "Equipment" | "Item" | "Plant" | "Treasure" =>
|
| 187 | |
buf.push_str(rec.get("Effect")?),
|
| 188 | |
"Monster" => {
|
| 211 |
buf.push_str(effect);
|
| 212 |
},
|
| 213 |
Monster { action, effect, .. } => {
|
| 189 | 214 |
buf.push_str("<span font_weight=\"bold\">");
|
| 190 | |
buf.push_str(rec.get("Action")?);
|
| 215 |
buf.push_str(action);
|
| 191 | 216 |
buf.push_str(":</span> ");
|
| 192 | |
buf.push_str(rec.get("Effect")?);
|
| 193 | |
}
|
| 194 | |
_ => bail!("Unknown type: {}", typ),
|
| 217 |
buf.push_str(effect);
|
| 218 |
},
|
| 219 |
Generic { effect, .. } => {
|
| 220 |
buf.push_str(effect);
|
| 221 |
},
|
| 195 | 222 |
}
|
| 196 | 223 |
pango::parse_markup(&buf, '\0')?
|
| 197 | 224 |
};
|
|
| 200 | 227 |
layout.set_text(&text);
|
| 201 | 228 |
pangocairo::functions::show_layout(&ctx, &layout);
|
| 202 | 229 |
|
| 203 | |
let special_icon = rec.get("Icon").map(|i| format!("../icons/{}-icon.svg", i));
|
| 204 | |
let icon_path = special_icon.unwrap_or(format!(
|
| 205 | |
"../icons/{}-icon.svg",
|
| 206 | |
match typ.as_ref() {
|
| 207 | |
"Weapon" => rec.get("Type")?.to_owned(),
|
| 208 | |
_ => typ.to_lowercase(),
|
| 209 | |
},
|
| 210 | |
));
|
| 211 | |
let icon = rsvg::Handle::new_from_file(&icon_path)
|
| 212 | |
.map_err(|_| format_err!("Error loading image: {}", icon_path))?;
|
| 230 |
let icon = rsvg::Handle::new_from_file(&card.icon)
|
| 231 |
.map_err(|_| format_err!("Error loading image: {}", card.icon))?;
|
| 213 | 232 |
ctx.translate(10.0, 10.0);
|
| 214 | 233 |
ctx.scale(0.9, 0.9);
|
| 215 | 234 |
icon.render_cairo(&ctx);
|
| 216 | 235 |
|
| 217 | 236 |
|
| 218 | |
{
|
| 219 | |
let name = name.chars().filter_map(|c| {
|
| 220 | |
if c == ' ' {
|
| 221 | |
Some('_')
|
| 222 | |
} else if c.is_alphabetic() {
|
| 223 | |
Some(c.to_ascii_lowercase())
|
| 224 | |
} else {
|
| 225 | |
None
|
| 226 | |
}
|
| 227 | |
}).collect::<String>();
|
| 228 | |
let path = format!("cards/{}_{}.png", typ.to_lowercase(), name);
|
| 229 | |
let mut f = fs::File::create(&path)?;
|
| 230 | |
println!("Writing to {}", path);
|
| 231 | |
surf.write_to_png(&mut f)?;
|
| 232 | |
}
|
| 237 |
let path = format!("cards/{}_{}.png", card.type_name(), card.card_name());
|
| 238 |
let mut f = fs::File::create(&path)?;
|
| 239 |
println!("Writing to {}", path);
|
| 240 |
surf.write_to_png(&mut f)?;
|
| 233 | 241 |
|
| 234 | 242 |
Ok(())
|
| 235 | 243 |
}
|
|
| 239 | 247 |
let recfile = rrecutils::Recfile::parse(&mut f)?;
|
| 240 | 248 |
|
| 241 | 249 |
for w in recfile.iter() {
|
| 242 | |
draw_card(w)?;
|
| 250 |
if w.rec_type.is_some() {
|
| 251 |
let card = Card::from_record(w)?;
|
| 252 |
draw_card(&card)?;
|
| 253 |
}
|
| 243 | 254 |
}
|
| 244 | 255 |
|
| 245 | 256 |
Ok(())
|