use crate::crafting::recipe_kinds;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Cooking {
Smelting,
Blasting,
Smoking,
Campfire,
}
impl Cooking {
pub const ALL: [Cooking; 4] = [
Cooking::Smelting,
Cooking::Blasting,
Cooking::Smoking,
Cooking::Campfire,
];
pub fn word(self) -> &'static str {
recipe_kinds::ids::COOKING[self as usize]
}
pub fn of_kind(id: &str) -> Option<Cooking> {
Cooking::ALL.into_iter().find(|c| c.word() == id)
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum RecipeKind {
Shaped {
pattern: Vec<String>,
key: Vec<(char, Vec<String>)>,
},
Shapeless {
ingredients: Vec<Vec<String>>,
},
Cooking {
method: Cooking,
ingredient: Vec<String>,
experience: f32,
cook_time: u32,
},
Stonecutting {
ingredient: Vec<String>,
},
Smithing {
template: Vec<String>,
base: Vec<String>,
addition: Vec<String>,
},
}
#[derive(Debug, Clone, PartialEq)]
pub struct Recipe {
pub id: String,
pub kind: RecipeKind,
pub result: String,
pub count: u32,
pub group: Option<String>,
pub category: Option<String>,
}
const SMELT_TICKS: u32 = 200;
const FAST_COOK_TICKS: u32 = 100;
const CAMPFIRE_TICKS: u32 = 600;
pub const COOK_EXPERIENCE: f64 = 0.1;
impl Recipe {
fn of(id: impl Into<String>, kind: RecipeKind, result: impl Into<String>, count: u32) -> Self {
Recipe {
id: id.into(),
kind,
result: result.into(),
count,
group: None,
category: None,
}
}
pub fn shaped(id: impl Into<String>, result: impl Into<String>, count: u32) -> Self {
Self::of(
id,
RecipeKind::Shaped {
pattern: Vec::new(),
key: Vec::new(),
},
result,
count,
)
}
pub fn shapeless(id: impl Into<String>, result: impl Into<String>, count: u32) -> Self {
Self::of(
id,
RecipeKind::Shapeless {
ingredients: Vec::new(),
},
result,
count,
)
}
pub fn cooking(
id: impl Into<String>,
method: Cooking,
ingredient: impl Into<String>,
result: impl Into<String>,
count: u32,
) -> Self {
let cook_time = match method {
Cooking::Smelting => SMELT_TICKS,
Cooking::Blasting | Cooking::Smoking => FAST_COOK_TICKS,
Cooking::Campfire => CAMPFIRE_TICKS,
};
let kind = RecipeKind::Cooking {
method,
ingredient: vec![ingredient.into()],
experience: COOK_EXPERIENCE as f32,
cook_time,
};
Self::of(id, kind, result, count)
}
pub fn stonecutting(
id: impl Into<String>,
ingredient: impl Into<String>,
result: impl Into<String>,
count: u32,
) -> Self {
Self::of(
id,
RecipeKind::Stonecutting {
ingredient: vec![ingredient.into()],
},
result,
count,
)
}
pub fn smithing(
id: impl Into<String>,
template: impl Into<String>,
base: impl Into<String>,
addition: impl Into<String>,
result: impl Into<String>,
) -> Self {
let kind = RecipeKind::Smithing {
template: vec![template.into()],
base: vec![base.into()],
addition: vec![addition.into()],
};
Self::of(id, kind, result, 1)
}
pub fn row(mut self, pattern: impl Into<String>) -> Self {
if let RecipeKind::Shaped { pattern: p, .. } = &mut self.kind {
p.push(pattern.into());
}
self
}
pub fn key(self, symbol: char, item: impl Into<String>) -> Self {
self.key_any(symbol, [item.into()])
}
pub fn key_any(
mut self,
symbol: char,
items: impl IntoIterator<Item = impl Into<String>>,
) -> Self {
if let RecipeKind::Shaped { key, .. } = &mut self.kind {
key.push((symbol, items.into_iter().map(Into::into).collect()));
}
self
}
pub fn ingredient(self, item: impl Into<String>) -> Self {
self.ingredient_any([item.into()])
}
pub fn ingredient_any(mut self, items: impl IntoIterator<Item = impl Into<String>>) -> Self {
if let RecipeKind::Shapeless { ingredients } = &mut self.kind {
ingredients.push(items.into_iter().map(Into::into).collect());
}
self
}
pub fn experience(mut self, xp: f32) -> Self {
if let RecipeKind::Cooking { experience, .. } = &mut self.kind {
*experience = xp;
}
self
}
pub fn cook_time(mut self, ticks: u32) -> Self {
if let RecipeKind::Cooking { cook_time, .. } = &mut self.kind {
*cook_time = ticks;
}
self
}
pub fn group(mut self, group: impl Into<String>) -> Self {
self.group = Some(group.into());
self
}
pub fn category(mut self, category: impl Into<String>) -> Self {
self.category = Some(category.into());
self
}
pub fn to_data(&self) -> yog_data::Value {
use yog_data::{Map, Value};
let ing = |items: &[String]| match items {
[one] => Value::from(one.as_str()),
many => Value::List(many.iter().map(|i| Value::from(i.as_str())).collect()),
};
let mut m = Map::with_capacity(8);
let ty = |m: &mut Map, t: &str| m.insert("type", Value::from(t));
match &self.kind {
RecipeKind::Shaped { pattern, key } => {
ty(&mut m, "shaped");
m.insert(
"pattern",
Value::List(pattern.iter().map(|r| Value::from(r.as_str())).collect()),
);
let mut keys = Map::with_capacity(key.len());
for (c, items) in key {
keys.insert(c.to_string(), ing(items));
}
m.insert("key", Value::Map(keys));
}
RecipeKind::Shapeless { ingredients } => {
ty(&mut m, "shapeless");
m.insert(
"ingredients",
Value::List(ingredients.iter().map(|i| ing(i)).collect()),
);
}
RecipeKind::Cooking {
method,
ingredient,
experience,
cook_time,
} => {
ty(&mut m, method.word());
m.insert("ingredient", ing(ingredient));
m.insert(
"experience",
Value::Float(experience.to_string().parse().unwrap_or(0.0)),
);
m.insert("cook_time", Value::Int(*cook_time as i64));
}
RecipeKind::Stonecutting { ingredient } => {
ty(&mut m, "stonecutting");
m.insert("ingredient", ing(ingredient));
}
RecipeKind::Smithing {
template,
base,
addition,
} => {
ty(&mut m, "smithing");
m.insert("template", ing(template));
m.insert("base", ing(base));
m.insert("addition", ing(addition));
}
}
m.insert("result", Value::from(self.result.as_str()));
if self.count != 1 {
m.insert("count", Value::Int(self.count as i64));
}
if let Some(g) = &self.group {
m.insert("group", Value::from(g.as_str()));
}
if let Some(c) = &self.category {
m.insert("category", Value::from(c.as_str()));
}
Value::Map(m)
}
pub fn ns_name(&self) -> (&str, &str) {
yog_ids::split(&self.id)
}
}
#[derive(yog_data::Schema, Debug, Clone)]
pub struct StartupGrant {
pub id: String,
pub items: Vec<String>,
pub book: Option<String>,
pub command: Option<String>,
}
impl StartupGrant {
pub fn new(id: impl Into<String>) -> Self {
Self {
id: id.into(),
items: Vec::new(),
book: None,
command: None,
}
}
pub fn item(mut self, item_id: impl Into<String>) -> Self {
self.items.push(item_id.into());
self
}
pub fn book(mut self, book: impl Into<String>) -> Self {
self.book = Some(book.into());
self
}
pub fn command(mut self, cmd: impl Into<String>) -> Self {
self.command = Some(cmd.into());
self
}
}