yog_registry/
recipe_kinds.rs1use yog_sync::LazyLock;
8
9use yog_data::{Ordered, Schema};
10
11#[derive(Schema, Debug, Clone, PartialEq)]
13pub struct SlotSpec {
14 pub name: Option<String>,
16 pub description: Option<String>,
18 #[data(default = "one", range = "1..=64")]
20 pub max: u32,
21 #[data(default)]
23 pub required: bool,
24 pub field: Option<String>,
27 #[data(default)]
30 pub grid: bool,
31}
32
33fn one() -> u32 {
34 1
35}
36
37#[derive(Schema, Debug, Clone, Copy, PartialEq, Eq)]
39pub enum ValueType {
40 Number,
41 Text,
42 Bool,
43}
44
45#[derive(Schema, Debug, Clone, PartialEq)]
50pub struct RecipeKindDef {
51 pub id: String,
54 pub name: Option<String>,
56 #[data(default)]
57 pub slots: Ordered<SlotSpec>,
58 #[data(default)]
59 pub values: Ordered<ValueType>,
60 #[data(default)]
62 pub defaults: Ordered<f64>,
63}
64
65impl RecipeKindDef {
66 pub fn slot(&self, name: &str) -> Option<&SlotSpec> {
68 self.slots.0.iter().find(|(n, _)| n == name).map(|(_, s)| s)
69 }
70
71 pub fn default_of(&self, name: &str) -> Option<f64> {
73 self.defaults
74 .0
75 .iter()
76 .find(|(n, _)| n == name)
77 .map(|(_, v)| *v)
78 }
79}
80
81pub mod ids {
85 pub use yog_ids::recipe_kind::{
86 BLASTING, BREWING, CAMPFIRE_COOKING, SHAPED, SHAPELESS, SMELTING, SMITHING, SMOKING,
87 STONECUTTING,
88 };
89
90 pub const ALL: &[&str] = yog_ids::recipe_kind::ALL_VALUES;
92 pub const COOKING: [&str; 4] = [SMELTING, BLASTING, SMOKING, CAMPFIRE_COOKING];
94}
95
96pub mod fields {
99 pub use yog_ids::recipe_field::{COUNT, KEY, PATTERN, RESULT, SLOTS, TYPE, VALUES};
100}
101
102pub const GRID_SIDE: usize = 3;
104
105pub static GAME: LazyLock<Vec<RecipeKindDef>> = LazyLock::new(|| {
107 yog_data::built_in::<Vec<RecipeKindDef>>(
108 "yog-registry's recipe kinds",
109 include_str!("../data/recipe_kinds.ydata"),
110 )
111});
112
113pub fn game_kind(id: &str) -> Option<&'static RecipeKindDef> {
115 GAME.iter().find(|k| k.id == id)
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121
122 #[test]
123 fn the_game_kinds_are_data() {
124 let data: Vec<&str> = GAME.iter().map(|k| k.id.as_str()).collect();
125 assert_eq!(data, ids::ALL, "the data and the code name the same kinds");
126 let smelting = game_kind(ids::SMELTING).unwrap();
127 assert_eq!(
128 smelting.slot("input").and_then(|s| s.field.as_deref()),
129 Some("ingredient")
130 );
131 assert_eq!(smelting.default_of("cook_time"), Some(200.0));
132 assert!(game_kind(ids::SHAPED).unwrap().slot("grid").unwrap().grid);
133 let brewing = game_kind(ids::BREWING).unwrap();
134 assert_eq!(
135 brewing.slot("from").and_then(|s| s.field.as_deref()),
136 Some("from")
137 );
138 }
139}