2020-07-14 20:11:39 +00:00
|
|
|
use crate::{
|
|
|
|
assets::{self, Asset},
|
2020-09-26 15:20:46 +00:00
|
|
|
comp::{item::ItemDef, Inventory, Item},
|
2020-07-14 20:11:39 +00:00
|
|
|
};
|
|
|
|
use hashbrown::HashMap;
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use std::{fs::File, io::BufReader, sync::Arc};
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
|
|
pub struct Recipe {
|
2020-09-26 15:20:46 +00:00
|
|
|
pub output: (Arc<ItemDef>, u32),
|
|
|
|
pub inputs: Vec<(Arc<ItemDef>, u32)>,
|
2020-07-14 20:11:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(clippy::type_complexity)]
|
|
|
|
impl Recipe {
|
|
|
|
/// Perform a recipe, returning a list of missing items on failure
|
2020-09-26 15:20:46 +00:00
|
|
|
pub fn perform(
|
|
|
|
&self,
|
|
|
|
inv: &mut Inventory,
|
|
|
|
) -> Result<Option<(Item, u32)>, Vec<(&ItemDef, u32)>> {
|
2020-07-14 20:11:39 +00:00
|
|
|
// Get ingredient cells from inventory,
|
|
|
|
inv.contains_ingredients(self)?
|
|
|
|
.into_iter()
|
|
|
|
.enumerate()
|
|
|
|
.for_each(|(i, n)| {
|
|
|
|
(0..n).for_each(|_| {
|
|
|
|
inv.take(i).expect("Expected item to exist in inventory");
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
|
|
|
for i in 0..self.output.1 {
|
2020-09-26 15:20:46 +00:00
|
|
|
let crafted_item = Item::new(Arc::clone(&self.output.0));
|
|
|
|
if let Some(item) = inv.push(crafted_item) {
|
2020-07-14 20:11:39 +00:00
|
|
|
return Ok(Some((item, self.output.1 - i)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
|
2020-09-26 15:20:46 +00:00
|
|
|
pub fn inputs(&self) -> impl ExactSizeIterator<Item = (&Arc<ItemDef>, u32)> {
|
|
|
|
self.inputs
|
|
|
|
.iter()
|
|
|
|
.map(|(item_def, amount)| (item_def, *amount))
|
2020-07-14 20:11:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
|
|
pub struct RecipeBook {
|
|
|
|
recipes: HashMap<String, Recipe>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl RecipeBook {
|
|
|
|
pub fn get(&self, recipe: &str) -> Option<&Recipe> { self.recipes.get(recipe) }
|
|
|
|
|
|
|
|
pub fn iter(&self) -> impl ExactSizeIterator<Item = (&String, &Recipe)> { self.recipes.iter() }
|
|
|
|
|
|
|
|
pub fn get_available(&self, inv: &Inventory) -> Vec<(String, Recipe)> {
|
|
|
|
self.recipes
|
|
|
|
.iter()
|
|
|
|
.filter(|(_, recipe)| inv.contains_ingredients(recipe).is_ok())
|
|
|
|
.map(|(name, recipe)| (name.clone(), recipe.clone()))
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Asset for RecipeBook {
|
|
|
|
const ENDINGS: &'static [&'static str] = &["ron"];
|
|
|
|
|
2020-09-17 23:02:14 +00:00
|
|
|
fn parse(buf_reader: BufReader<File>, _specifier: &str) -> Result<Self, assets::Error> {
|
2020-07-14 20:11:39 +00:00
|
|
|
ron::de::from_reader::<
|
|
|
|
BufReader<File>,
|
2020-09-17 23:02:14 +00:00
|
|
|
HashMap<String, ((String, u32), Vec<(String, u32)>)>,
|
2020-07-14 20:11:39 +00:00
|
|
|
>(buf_reader)
|
|
|
|
.map_err(assets::Error::parse_error)
|
|
|
|
.and_then(|recipes| {
|
|
|
|
Ok(RecipeBook {
|
|
|
|
recipes: recipes
|
|
|
|
.into_iter()
|
|
|
|
.map::<Result<(String, Recipe), assets::Error>, _>(
|
|
|
|
|(name, ((output, amount), inputs))| {
|
|
|
|
Ok((name, Recipe {
|
2020-09-26 15:20:46 +00:00
|
|
|
output: (ItemDef::load(&output)?, amount),
|
2020-07-14 20:11:39 +00:00
|
|
|
inputs: inputs
|
|
|
|
.into_iter()
|
2020-09-26 15:20:46 +00:00
|
|
|
.map::<Result<(Arc<ItemDef>, u32), assets::Error>, _>(
|
|
|
|
|(name, amount)| Ok((ItemDef::load(&name)?, amount)),
|
2020-07-14 20:11:39 +00:00
|
|
|
)
|
|
|
|
.collect::<Result<_, _>>()?,
|
|
|
|
}))
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.collect::<Result<_, _>>()?,
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-28 01:02:17 +00:00
|
|
|
pub fn default_recipe_book() -> Arc<RecipeBook> { RecipeBook::load_expect("common.recipe_book") }
|