1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
/*
Hurry Curry! - a game about cooking
Copyright (C) 2026 Hurry Curry! Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, version 3 of the License only.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use crate::registry::ItemTileRegistry;
use anyhow::{Result, anyhow};
use hurrycurry_protocol::{Demand, ItemIndex, Recipe};
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, BTreeSet};
#[rustfmt::skip]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct RecipeDecl {
tile: Option<String>,
#[serde(default)] inputs: Vec<String>,
#[serde(default)] outputs: Vec<String>,
#[serde(default)] action: RecipeDeclAction,
#[serde(default)] warn: bool,
revert_duration: Option<f32>,
duration: Option<f32>,
points: Option<i64>,
group: Option<String>,
#[serde(default)] group_hidden: bool,
}
#[derive(Debug, Deserialize, Serialize, Clone, Copy, Default)]
#[serde(rename_all = "snake_case")]
pub enum RecipeDeclAction {
#[default]
Never,
Passive,
Active,
Instant,
Demand,
}
#[allow(clippy::type_complexity)]
pub(crate) fn load_recipes(
recipes_in: Vec<RecipeDecl>,
reg: &ItemTileRegistry,
) -> Result<(
Vec<Recipe>,
Vec<Demand>,
BTreeMap<String, BTreeSet<ItemIndex>>,
)> {
let mut recipes = Vec::new();
let mut demands = Vec::new();
let mut recipe_groups = BTreeMap::<String, BTreeSet<ItemIndex>>::new();
for mut r in recipes_in {
#[cfg(feature = "fast_recipes")]
match r.action {
RecipeDeclAction::Passive | RecipeDeclAction::Active => {
if !r.warn {
r.duration = Some(0.5)
}
}
_ => (),
}
let r2 = r.clone();
let mut inputs = r.inputs.into_iter().map(|i| reg.register_item(i));
let mut outputs = r.outputs.into_iter().map(|o| reg.register_item(o));
let tile = r.tile.map(|t| reg.register_tile(t));
if let Some(g) = r.group
&& !r.group_hidden
{
recipe_groups.entry(g).or_default().extend(inputs.clone());
}
match r.action {
RecipeDeclAction::Never => {}
RecipeDeclAction::Passive => recipes.push(Recipe::Passive {
speed: 1. / r.duration.ok_or(anyhow!("duration for passive missing"))?,
warn: r.warn,
tile,
revert_speed: r.revert_duration.map(|d| 1. / d),
input: inputs
.next()
.ok_or(anyhow!("passive recipe without input"))?,
output: outputs.next(),
}),
RecipeDeclAction::Active => recipes.push(Recipe::Active {
speed: 1. / r.duration.ok_or(anyhow!("duration for active missing"))?,
tile,
input: inputs
.next()
.ok_or(anyhow!("active recipe without input"))?,
outputs: [outputs.next(), outputs.next()],
}),
RecipeDeclAction::Instant => {
recipes.push(Recipe::Instant {
points: r.points.take().unwrap_or(0),
tile,
inputs: [inputs.next(), inputs.next()],
outputs: [outputs.next(), outputs.next()],
});
}
RecipeDeclAction::Demand => demands.push(Demand {
input: inputs.next().ok_or(anyhow!("demand needs inputs"))?,
output: outputs.next(),
duration: r.duration.unwrap_or(10.),
points: 0, // assigned later when filtering
}),
}
assert_eq!(inputs.next(), None, "{r2:?} inputs left over");
assert_eq!(outputs.next(), None, "{r2:?} outputs left over");
assert_eq!(r.points, None, "points specified where not possible")
}
Ok((recipes, demands, recipe_groups))
}
|