From 37442b638c0504d392845361af0c88b14de28894 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 5 May 2022 13:25:29 -0400 Subject: [PATCH] Changed component recipe book to generate some stuff automatically to avoid potential for stuff inside to become unsynced (no assets). --- common/src/comp/inventory/item/mod.rs | 22 +-- common/src/comp/inventory/trade_pricing.rs | 5 +- common/src/recipe.rs | 179 ++++++++---------- server/src/cmd.rs | 2 +- .../src/migrations/V49__modular_weapons.sql | 10 - voxygen/src/hud/crafting.rs | 8 +- 6 files changed, 94 insertions(+), 132 deletions(-) diff --git a/common/src/comp/inventory/item/mod.rs b/common/src/comp/inventory/item/mod.rs index 78dddb9568..fb5992e8c7 100644 --- a/common/src/comp/inventory/item/mod.rs +++ b/common/src/comp/inventory/item/mod.rs @@ -965,19 +965,15 @@ impl Item { pub fn ability_spec(&self) -> Option> { match &self.item_base { ItemBase::Raw(item_def) => { - item_def - .ability_spec - .as_ref() - .map(Cow::Borrowed) - .or_else(|| { - // If no custom ability set is specified, fall back to abilityset of tool - // kind. - if let ItemKind::Tool(tool) = &item_def.kind { - Some(Cow::Owned(AbilitySpec::Tool(tool.kind))) - } else { - None - } - }) + item_def.ability_spec.as_ref().map(Cow::Borrowed).or({ + // If no custom ability set is specified, fall back to abilityset of tool + // kind. + if let ItemKind::Tool(tool) = &item_def.kind { + Some(Cow::Owned(AbilitySpec::Tool(tool.kind))) + } else { + None + } + }) }, ItemBase::Modular(mod_base) => mod_base.ability_spec(self.components()), } diff --git a/common/src/comp/inventory/trade_pricing.rs b/common/src/comp/inventory/trade_pricing.rs index 70e3d966d5..cab564c6e7 100644 --- a/common/src/comp/inventory/trade_pricing.rs +++ b/common/src/comp/inventory/trade_pricing.rs @@ -812,11 +812,12 @@ mod tests { assert!((lootsum2 - 1.0).abs() < 1e-4); // highly nested - // let loot3 = expand_loot_table("common.loot_tables.creature.biped_large.wendigo"); - // let lootsum3 = loot3.iter().fold(0.0, |s, i| s + i.0); // TODO: Re-enable this. See note at top of test (though this specific // table can also be fixed by properly integrating modular weapons into // probability files) + // let loot3 = + // expand_loot_table("common.loot_tables.creature.biped_large.wendigo"); + // let lootsum3 = loot3.iter().fold(0.0, |s, i| s + i.0); // assert!((lootsum3 - 1.0).abs() < 1e-5); } diff --git a/common/src/recipe.rs b/common/src/recipe.rs index be8b6eaad3..719a9dacb1 100644 --- a/common/src/recipe.rs +++ b/common/src/recipe.rs @@ -372,6 +372,25 @@ pub enum RawRecipeInput { ListSameItem(String), } +impl RawRecipeInput { + fn load_recipe_input(&self) -> Result { + let input = match self { + RawRecipeInput::Item(name) => RecipeInput::Item(Arc::::load_cloned(name)?), + RawRecipeInput::Tag(tag) => RecipeInput::Tag(*tag), + RawRecipeInput::TagSameItem(tag) => RecipeInput::TagSameItem(*tag), + RawRecipeInput::ListSameItem(list) => { + let assets = &ItemList::load_expect(list).read().0; + let items = assets + .iter() + .map(|asset| Arc::::load_expect_cloned(asset)) + .collect(); + RecipeInput::ListSameItem(items) + }, + }; + Ok(input) + } +} + #[derive(Clone, Deserialize)] pub(crate) struct RawRecipe { pub(crate) output: (String, u32), @@ -392,7 +411,7 @@ impl assets::Asset for RawRecipeBook { } #[derive(Deserialize, Clone)] -pub struct ItemList(Vec); +struct ItemList(Vec); impl assets::Asset for ItemList { type Loader = assets::RonLoader; @@ -415,19 +434,7 @@ impl assets::Compound for RecipeBook { fn load_recipe_input( (input, amount, is_mod_comp): &(RawRecipeInput, u32, bool), ) -> Result<(RecipeInput, u32, bool), assets::Error> { - let def = match &input { - RawRecipeInput::Item(name) => RecipeInput::Item(Arc::::load_cloned(name)?), - RawRecipeInput::Tag(tag) => RecipeInput::Tag(*tag), - RawRecipeInput::TagSameItem(tag) => RecipeInput::TagSameItem(*tag), - RawRecipeInput::ListSameItem(list) => { - let assets = &ItemList::load_expect(list).read().0; - let items = assets - .iter() - .map(|asset| Arc::::load_expect_cloned(asset)) - .collect(); - RecipeInput::ListSameItem(items) - }, - }; + let def = input.load_recipe_input()?; Ok((def, *amount, *is_mod_comp)) } @@ -478,7 +485,7 @@ impl ComponentRecipeBook { #[derive(Clone, Deserialize)] #[serde(transparent)] -struct RawComponentRecipeBook(HashMap); +struct RawComponentRecipeBook(Vec); impl assets::Asset for RawComponentRecipeBook { type Loader = assets::RonLoader; @@ -494,7 +501,7 @@ pub struct ComponentKey { pub toolkind: ToolKind, /// Refers to the item definition id of the material pub material: String, - /// Refers to the item definition id of the material + /// Refers to the item definition id of the modifier pub modifier: Option, } @@ -671,10 +678,6 @@ impl ComponentRecipe { pub fn inputs(&self) -> impl ExactSizeIterator { pub struct ComponentRecipeInputsIterator<'a> { - // material: bool, - // modifier: bool, - // index: usize, - // recipe: &'a ComponentRecipe, material: Option<&'a (RecipeInput, u32)>, modifier: Option<&'a (RecipeInput, u32)>, additional_inputs: std::slice::Iter<'a, (RecipeInput, u32)>, @@ -684,21 +687,6 @@ impl ComponentRecipe { type Item = &'a (RecipeInput, u32); fn next(&mut self) -> Option<&'a (RecipeInput, u32)> { - // if !self.material { - // self.material = true; - // Some(&self.recipe.material) - // } else if !self.modifier { - // self.modifier = true; - // if self.recipe.modifier.is_some() { - // self.recipe.modifier.as_ref() - // } else { - // self.index += 1; - // self.recipe.additional_inputs.get(self.index - 1) - // } - // } else { - // self.index += 1; - // self.recipe.additional_inputs.get(self.index - 1) - // } self.material .take() .or_else(|| self.modifier.take()) @@ -712,10 +700,6 @@ impl ComponentRecipe { fn into_iter(self) -> Self::IntoIter { ComponentRecipeInputsIterator { - // material: false, - // modifier: false, - // index: 0, - // recipe: self, material: Some(&self.material), modifier: self.modifier.as_ref(), additional_inputs: self.additional_inputs.as_slice().iter(), @@ -725,8 +709,6 @@ impl ComponentRecipe { impl<'a> ExactSizeIterator for ComponentRecipeInputsIterator<'a> { fn len(&self) -> usize { - // 1 + self.recipe.modifier.is_some() as usize + - // self.recipe.additional_inputs.len() self.material.is_some() as usize + self.modifier.is_some() as usize + self.additional_inputs.len() @@ -740,8 +722,10 @@ impl ComponentRecipe { #[derive(Clone, Deserialize)] struct RawComponentRecipe { output: RawComponentOutput, - material: (RawRecipeInput, u32), - modifier: Option<(RawRecipeInput, u32)>, + /// String refers to an item definition id + material: (String, u32), + /// String refers to an item definition id + modifier: Option<(String, u32)>, additional_inputs: Vec<(RawRecipeInput, u32)>, craft_sprite: Option, } @@ -756,10 +740,9 @@ enum ComponentOutput { #[derive(Clone, Debug, Serialize, Deserialize)] enum RawComponentOutput { - ItemComponents { - item: String, - components: Vec, - }, + /// Creates the primary component of a modular tool. Assumes that the + /// material used is the only component in the item. + ToolPrimaryComponent { toolkind: ToolKind, item: String }, } impl assets::Compound for ComponentRecipeBook { @@ -768,42 +751,54 @@ impl assets::Compound for ComponentRecipeBook { specifier: &str, ) -> Result { #[inline] - fn load_recipe_input( - (input, amount): &(RawRecipeInput, u32), - ) -> Result<(RecipeInput, u32), assets::Error> { - let def = match &input { - RawRecipeInput::Item(name) => RecipeInput::Item(Arc::::load_cloned(name)?), - RawRecipeInput::Tag(tag) => RecipeInput::Tag(*tag), - RawRecipeInput::TagSameItem(tag) => RecipeInput::TagSameItem(*tag), - RawRecipeInput::ListSameItem(list) => { - let assets = &ItemList::load_expect(list).read().0; - let items = assets - .iter() - .map(|asset| Arc::::load_expect_cloned(asset)) - .collect(); - RecipeInput::ListSameItem(items) + fn load_recipe_key(raw_recipe: &RawComponentRecipe) -> ComponentKey { + match &raw_recipe.output { + RawComponentOutput::ToolPrimaryComponent { toolkind, item: _ } => { + let material = String::from(&raw_recipe.material.0); + let modifier = raw_recipe + .modifier + .as_ref() + .map(|(modifier, _amount)| String::from(modifier)); + ComponentKey { + toolkind: *toolkind, + material, + modifier, + } }, - }; - Ok((def, *amount)) + } } #[inline] - fn load_recipe_output( - output: &RawComponentOutput, - ) -> Result { - let def = match &output { - RawComponentOutput::ItemComponents { - item: def, - components: defs, - } => ComponentOutput::ItemComponents { - item: Arc::::load_cloned(def)?, - components: defs - .iter() - .map(|def| Arc::::load_cloned(def)) - .collect::, _>>()?, + fn load_recipe(raw_recipe: &RawComponentRecipe) -> Result { + let output = match &raw_recipe.output { + RawComponentOutput::ToolPrimaryComponent { toolkind: _, item } => { + let item = Arc::::load_cloned(item)?; + let components = vec![Arc::::load_cloned(&raw_recipe.material.0)?]; + ComponentOutput::ItemComponents { item, components } }, }; - Ok(def) + let material = ( + RecipeInput::Item(Arc::::load_cloned(&raw_recipe.material.0)?), + raw_recipe.material.1, + ); + let modifier = if let Some((modifier, amount)) = &raw_recipe.modifier { + let modifier = Arc::::load_cloned(modifier)?; + Some((RecipeInput::Item(modifier), *amount)) + } else { + None + }; + let additional_inputs = raw_recipe + .additional_inputs + .iter() + .map(|(input, amount)| input.load_recipe_input().map(|input| (input, *amount))) + .collect::, _>>()?; + Ok(ComponentRecipe { + output, + material, + modifier, + additional_inputs, + craft_sprite: raw_recipe.craft_sprite, + }) } let raw = cache.load::(specifier)?.cloned(); @@ -811,33 +806,9 @@ impl assets::Compound for ComponentRecipeBook { let recipes = raw .0 .iter() - .map( - |( - key, - RawComponentRecipe { - output, - material, - modifier, - additional_inputs, - craft_sprite, - }, - )| { - let additional_inputs = additional_inputs - .iter() - .map(load_recipe_input) - .collect::, _>>()?; - let material = load_recipe_input(material)?; - let modifier = modifier.as_ref().map(load_recipe_input).transpose()?; - let output = load_recipe_output(output)?; - Ok((key.clone(), ComponentRecipe { - output, - material, - modifier, - additional_inputs, - craft_sprite: *craft_sprite, - })) - }, - ) + .map(|raw_recipe| { + load_recipe(raw_recipe).map(|recipe| (load_recipe_key(raw_recipe), recipe)) + }) .collect::>()?; Ok(ComponentRecipeBook { recipes }) diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 6be7521a46..e4393c156d 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -1940,8 +1940,8 @@ where if target_inventory.free_slots() < count { return Err("Inventory doesn't have enough slots".to_owned()); } + let mut rng = thread_rng(); for (item_id, quantity) in kit { - let mut rng = thread_rng(); let mut item = match &item_id { KitSpec::Item(item_id) => comp::Item::new_from_asset(item_id) .map_err(|_| format!("Unknown item: {:#?}", item_id))?, diff --git a/server/src/migrations/V49__modular_weapons.sql b/server/src/migrations/V49__modular_weapons.sql index c4feb0cca4..1713338f50 100644 --- a/server/src/migrations/V49__modular_weapons.sql +++ b/server/src/migrations/V49__modular_weapons.sql @@ -882,13 +882,3 @@ FROM item_graph WHERE parent_pseudo_id IS NOT NULL; PRAGMA defer_foreign_keys = false; - -SELECT i.item_id, - i.item_id, - i.parent_container_item_id, - t.new_item_definition_id, - t.pseudo_id, - parent_pseudo_id -FROM _temp_modular_component_items t -JOIN item i ON (i.item_definition_id = t.new_item_definition_id) -WHERE t.parent_pseudo_id IS NULL; \ No newline at end of file diff --git a/voxygen/src/hud/crafting.rs b/voxygen/src/hud/crafting.rs index 517ae910fe..afdf862c68 100644 --- a/voxygen/src/hud/crafting.rs +++ b/voxygen/src/hud/crafting.rs @@ -511,6 +511,9 @@ impl<'a> Widget for Crafting<'a> { let metal_comp_recipe = make_psuedo_recipe(SpriteKind::Anvil); let wood_comp_recipe = make_psuedo_recipe(SpriteKind::CraftingBench); let modular_entries = { + // A BTreeMap is used over a HashMap as when a HashMap is used, the UI shuffles + // the positions of these every tick, so a BTreeMap is necessary to keep it + // ordered. let mut modular_entries = BTreeMap::new(); modular_entries.insert( String::from("veloren.core.pseudo_recipe.modular_weapon"), @@ -1288,7 +1291,8 @@ impl<'a> Widget for Crafting<'a> { .mid_bottom_with_margin_on(state.ids.align_ing, -31.0) .parent(state.ids.window_frame) .set(state.ids.btn_craft, ui) - .was_clicked() && can_perform + .was_clicked() + && can_perform { match recipe_kind { RecipeKind::ModularWeapon => { @@ -1501,7 +1505,7 @@ impl<'a> Widget for Crafting<'a> { }) }) .or_else(|| tag.exemplar_identifier()) - .unwrap_or_else(|| "common.items.weapons.empty.empty"), + .unwrap_or("common.items.weapons.empty.empty"), ) }, RecipeInput::ListSameItem(item_defs) => Arc::::load_expect_cloned(