1use crate::recipe_kinds;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum Cooking {
8 Smelting,
9 Blasting,
10 Smoking,
11 Campfire,
12}
13
14impl Cooking {
15 pub const ALL: [Cooking; 4] = [
17 Cooking::Smelting,
18 Cooking::Blasting,
19 Cooking::Smoking,
20 Cooking::Campfire,
21 ];
22
23 pub fn word(self) -> &'static str {
25 recipe_kinds::ids::COOKING[self as usize]
26 }
27
28 pub fn of_kind(id: &str) -> Option<Cooking> {
30 Cooking::ALL.into_iter().find(|c| c.word() == id)
31 }
32}
33
34#[derive(Debug, Clone, PartialEq)]
36pub enum RecipeKind {
37 Shaped {
39 pattern: Vec<String>,
40 key: Vec<(char, Vec<String>)>,
41 },
42 Shapeless {
44 ingredients: Vec<Vec<String>>,
45 },
46 Cooking {
47 method: Cooking,
48 ingredient: Vec<String>,
49 experience: f32,
50 cook_time: u32,
51 },
52 Stonecutting {
53 ingredient: Vec<String>,
54 },
55 Smithing {
57 template: Vec<String>,
58 base: Vec<String>,
59 addition: Vec<String>,
60 },
61}
62
63#[derive(Debug, Clone, PartialEq)]
77pub struct Recipe {
78 pub id: String,
79 pub kind: RecipeKind,
80 pub result: String,
81 pub count: u32,
82 pub group: Option<String>,
84 pub category: Option<String>,
86}
87
88const SMELT_TICKS: u32 = 200;
91const FAST_COOK_TICKS: u32 = 100;
92const CAMPFIRE_TICKS: u32 = 600;
93pub const COOK_EXPERIENCE: f64 = 0.1;
94
95impl Recipe {
96 fn of(id: impl Into<String>, kind: RecipeKind, result: impl Into<String>, count: u32) -> Self {
97 Recipe {
98 id: id.into(),
99 kind,
100 result: result.into(),
101 count,
102 group: None,
103 category: None,
104 }
105 }
106
107 pub fn shaped(id: impl Into<String>, result: impl Into<String>, count: u32) -> Self {
108 Self::of(
109 id,
110 RecipeKind::Shaped {
111 pattern: Vec::new(),
112 key: Vec::new(),
113 },
114 result,
115 count,
116 )
117 }
118
119 pub fn shapeless(id: impl Into<String>, result: impl Into<String>, count: u32) -> Self {
120 Self::of(
121 id,
122 RecipeKind::Shapeless {
123 ingredients: Vec::new(),
124 },
125 result,
126 count,
127 )
128 }
129
130 pub fn cooking(
133 id: impl Into<String>,
134 method: Cooking,
135 ingredient: impl Into<String>,
136 result: impl Into<String>,
137 count: u32,
138 ) -> Self {
139 let cook_time = match method {
140 Cooking::Smelting => SMELT_TICKS,
141 Cooking::Blasting | Cooking::Smoking => FAST_COOK_TICKS,
142 Cooking::Campfire => CAMPFIRE_TICKS,
143 };
144 let kind = RecipeKind::Cooking {
145 method,
146 ingredient: vec![ingredient.into()],
147 experience: COOK_EXPERIENCE as f32,
148 cook_time,
149 };
150 Self::of(id, kind, result, count)
151 }
152
153 pub fn stonecutting(
154 id: impl Into<String>,
155 ingredient: impl Into<String>,
156 result: impl Into<String>,
157 count: u32,
158 ) -> Self {
159 Self::of(
160 id,
161 RecipeKind::Stonecutting {
162 ingredient: vec![ingredient.into()],
163 },
164 result,
165 count,
166 )
167 }
168
169 pub fn smithing(
170 id: impl Into<String>,
171 template: impl Into<String>,
172 base: impl Into<String>,
173 addition: impl Into<String>,
174 result: impl Into<String>,
175 ) -> Self {
176 let kind = RecipeKind::Smithing {
177 template: vec![template.into()],
178 base: vec![base.into()],
179 addition: vec![addition.into()],
180 };
181 Self::of(id, kind, result, 1)
182 }
183
184 pub fn row(mut self, pattern: impl Into<String>) -> Self {
186 if let RecipeKind::Shaped { pattern: p, .. } = &mut self.kind {
187 p.push(pattern.into());
188 }
189 self
190 }
191
192 pub fn key(self, symbol: char, item: impl Into<String>) -> Self {
194 self.key_any(symbol, [item.into()])
195 }
196
197 pub fn key_any(
199 mut self,
200 symbol: char,
201 items: impl IntoIterator<Item = impl Into<String>>,
202 ) -> Self {
203 if let RecipeKind::Shaped { key, .. } = &mut self.kind {
204 key.push((symbol, items.into_iter().map(Into::into).collect()));
205 }
206 self
207 }
208
209 pub fn ingredient(self, item: impl Into<String>) -> Self {
211 self.ingredient_any([item.into()])
212 }
213
214 pub fn ingredient_any(mut self, items: impl IntoIterator<Item = impl Into<String>>) -> Self {
216 if let RecipeKind::Shapeless { ingredients } = &mut self.kind {
217 ingredients.push(items.into_iter().map(Into::into).collect());
218 }
219 self
220 }
221
222 pub fn experience(mut self, xp: f32) -> Self {
224 if let RecipeKind::Cooking { experience, .. } = &mut self.kind {
225 *experience = xp;
226 }
227 self
228 }
229
230 pub fn cook_time(mut self, ticks: u32) -> Self {
232 if let RecipeKind::Cooking { cook_time, .. } = &mut self.kind {
233 *cook_time = ticks;
234 }
235 self
236 }
237
238 pub fn group(mut self, group: impl Into<String>) -> Self {
239 self.group = Some(group.into());
240 self
241 }
242
243 pub fn category(mut self, category: impl Into<String>) -> Self {
244 self.category = Some(category.into());
245 self
246 }
247
248 pub fn to_data(&self) -> yog_data::Value {
251 use yog_data::{Map, Value};
252 let ing = |items: &[String]| match items {
253 [one] => Value::from(one.as_str()),
254 many => Value::List(many.iter().map(|i| Value::from(i.as_str())).collect()),
255 };
256 let mut m = Map::with_capacity(8);
257 let ty = |m: &mut Map, t: &str| m.insert("type", Value::from(t));
258 match &self.kind {
259 RecipeKind::Shaped { pattern, key } => {
260 ty(&mut m, "shaped");
261 m.insert(
262 "pattern",
263 Value::List(pattern.iter().map(|r| Value::from(r.as_str())).collect()),
264 );
265 let mut keys = Map::with_capacity(key.len());
266 for (c, items) in key {
267 keys.insert(c.to_string(), ing(items));
268 }
269 m.insert("key", Value::Map(keys));
270 }
271 RecipeKind::Shapeless { ingredients } => {
272 ty(&mut m, "shapeless");
273 m.insert(
274 "ingredients",
275 Value::List(ingredients.iter().map(|i| ing(i)).collect()),
276 );
277 }
278 RecipeKind::Cooking {
279 method,
280 ingredient,
281 experience,
282 cook_time,
283 } => {
284 ty(&mut m, method.word());
285 m.insert("ingredient", ing(ingredient));
286 m.insert(
288 "experience",
289 Value::Float(experience.to_string().parse().unwrap_or(0.0)),
290 );
291 m.insert("cook_time", Value::Int(*cook_time as i64));
292 }
293 RecipeKind::Stonecutting { ingredient } => {
294 ty(&mut m, "stonecutting");
295 m.insert("ingredient", ing(ingredient));
296 }
297 RecipeKind::Smithing {
298 template,
299 base,
300 addition,
301 } => {
302 ty(&mut m, "smithing");
303 m.insert("template", ing(template));
304 m.insert("base", ing(base));
305 m.insert("addition", ing(addition));
306 }
307 }
308 m.insert("result", Value::from(self.result.as_str()));
309 if self.count != 1 {
310 m.insert("count", Value::Int(self.count as i64));
311 }
312 if let Some(g) = &self.group {
313 m.insert("group", Value::from(g.as_str()));
314 }
315 if let Some(c) = &self.category {
316 m.insert("category", Value::from(c.as_str()));
317 }
318 Value::Map(m)
319 }
320
321 pub fn ns_name(&self) -> (&str, &str) {
323 yog_ids::split(&self.id)
324 }
325}
326
327#[derive(yog_data::Schema, Debug, Clone)]
336pub struct StartupGrant {
337 pub id: String,
338 pub items: Vec<String>,
339 pub book: Option<String>,
340 pub command: Option<String>,
341}
342
343impl StartupGrant {
344 pub fn new(id: impl Into<String>) -> Self {
345 Self {
346 id: id.into(),
347 items: Vec::new(),
348 book: None,
349 command: None,
350 }
351 }
352
353 pub fn item(mut self, item_id: impl Into<String>) -> Self {
354 self.items.push(item_id.into());
355 self
356 }
357
358 pub fn book(mut self, book: impl Into<String>) -> Self {
359 self.book = Some(book.into());
360 self
361 }
362
363 pub fn command(mut self, cmd: impl Into<String>) -> Self {
364 self.command = Some(cmd.into());
365 self
366 }
367}