use yog_data::{Map, Value};
use crate::content::defs::data::definition_path;
use crate::content::source::{float, included};
#[derive(Debug, Clone)]
pub(in crate::content) enum RecipeKind {
Shaped {
pattern: Vec<String>,
key: Vec<(char, Vec<String>)>,
},
Shapeless(Vec<Vec<String>>),
Cooking {
method: yog_registry::Cooking,
input: Vec<String>,
experience: f64,
cook_time: u32,
},
Stonecutting(Vec<String>),
Smithing {
template: Vec<String>,
base: Vec<String>,
addition: Vec<String>,
},
Custom {
kind: String,
slots: Vec<(String, Vec<Vec<String>>)>,
values: Vec<(String, Value)>,
},
}
#[derive(Debug, Clone)]
pub(in crate::content) struct Recipe {
pub(in crate::content) id: String,
pub(in crate::content) ty: String,
pub(in crate::content) kind: RecipeKind,
pub(in crate::content) result: String,
pub(in crate::content) count: u32,
pub(in crate::content) group: Option<String>,
pub(in crate::content) category: Option<String>,
pub(in crate::content) requires: Vec<String>,
}
impl Recipe {
pub(in crate::content) fn data_file(&self) -> Option<(String, Vec<u8>)> {
let RecipeKind::Custom {
kind,
slots,
values,
} = &self.kind
else {
return None;
};
let recipe = self.custom_data(kind, slots, values);
Some((self.data_path(), yog_data::binary::encode(&recipe)))
}
fn data_path(&self) -> String {
definition_path(yog_ids::kind::RECIPE, &self.id)
}
fn custom_data(
&self,
kind: &str,
slots: &[(String, Vec<Vec<String>>)],
values: &[(String, Value)],
) -> Value {
use yog_data::{Schema, field};
use yog_formats::recipe::{Common, Custom, TYPE_FIELD};
let mut o = Map::new();
o.insert(TYPE_FIELD, kind.into());
let slots: Map = slots.iter().map(|(k, v)| (k.as_str(), v.write())).collect();
o.insert(field!(Custom, slots), slots.into());
if !values.is_empty() {
let values: Map = values
.iter()
.map(|(k, v)| (k.as_str(), v.clone()))
.collect();
o.insert(field!(Custom, values), values.into());
}
o.insert(field!(Common, result), self.result.as_str().into());
if self.count != 1 {
o.insert(field!(Common, count), i64::from(self.count).into());
}
if let Some(g) = &self.group {
o.insert(field!(Common, group), g.as_str().into());
}
o.into()
}
pub(in crate::content) fn registration(&self) -> String {
let list = |items: &[String]| {
let quoted: Vec<String> = items.iter().map(|i| format!("{i:?}.into()")).collect();
format!("vec![{}]", quoted.join(", "))
};
let kind = match &self.kind {
RecipeKind::Shaped { pattern, key } => {
let rows: Vec<String> = pattern.iter().map(|r| format!("{r:?}.into()")).collect();
let keys: Vec<String> = key
.iter()
.map(|(c, items)| format!("({c:?}, {})", list(items)))
.collect();
format!(
"Shaped {{ pattern: vec![{}], key: vec![{}] }}",
rows.join(", "),
keys.join(", ")
)
}
RecipeKind::Shapeless(ingredients) => {
let all: Vec<String> = ingredients.iter().map(|i| list(i)).collect();
format!("Shapeless {{ ingredients: vec![{}] }}", all.join(", "))
}
RecipeKind::Cooking {
method,
input,
experience,
cook_time,
} => format!(
"Cooking {{ method: ::yog_api::Cooking::{method:?}, ingredient: {}, experience: {}_f32, cook_time: {cook_time} }}",
list(input),
float(*experience)
),
RecipeKind::Stonecutting(input) => {
format!("Stonecutting {{ ingredient: {} }}", list(input))
}
RecipeKind::Custom { .. } => {
return format!(
"registry.add_recipe_data({:?}, {});",
self.id,
included(&self.data_path())
);
}
RecipeKind::Smithing {
template,
base,
addition,
} => format!(
"Smithing {{ template: {}, base: {}, addition: {} }}",
list(template),
list(base),
list(addition)
),
};
let opt = |o: &Option<String>| {
o.as_ref()
.map_or("None".to_string(), |v| format!("Some({v:?}.into())"))
};
format!(
"registry.add_recipe(::yog_api::Recipe {{ id: {:?}.into(), kind: ::yog_api::RecipeKind::{kind}, result: {:?}.into(), count: {}, group: {}, category: {} }});",
self.id,
self.result,
self.count,
opt(&self.group),
opt(&self.category)
)
}
}