aboutsummaryrefslogtreecommitdiff
path: root/server/data/src/recipes.rs
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2026-01-24 18:43:16 +0100
committertpart <tpart120@proton.me>2026-02-26 20:48:35 +0100
commit6801c48360f4ffa47b174c0607d88d720733080f (patch)
treee7d57e8825d623a20fceea8c1aca11848c7c344c /server/data/src/recipes.rs
parentb634bad931f530ee0a207e1461ffc5e52ebb83e3 (diff)
downloadhurrycurry-6801c48360f4ffa47b174c0607d88d720733080f.tar
hurrycurry-6801c48360f4ffa47b174c0607d88d720733080f.tar.bz2
hurrycurry-6801c48360f4ffa47b174c0607d88d720733080f.tar.zst
modify data code to load tile stack maps
Diffstat (limited to 'server/data/src/recipes.rs')
-rw-r--r--server/data/src/recipes.rs125
1 files changed, 125 insertions, 0 deletions
diff --git a/server/data/src/recipes.rs b/server/data/src/recipes.rs
new file mode 100644
index 00000000..e4d68a7d
--- /dev/null
+++ b/server/data/src/recipes.rs
@@ -0,0 +1,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))
+}