diff --git a/common/src/comp/inventory/item/armor.rs b/common/src/comp/inventory/item/armor.rs index 4ef7b7c595..a26ef044bb 100644 --- a/common/src/comp/inventory/item/armor.rs +++ b/common/src/comp/inventory/item/armor.rs @@ -27,20 +27,19 @@ pub enum ArmorKind { impl ArmorKind { pub fn has_durability(self) -> bool { - use ArmorKind::*; match self { - Shoulder => true, - Chest => true, - Belt => true, - Hand => true, - Pants => true, - Foot => true, - Back => true, - Ring => false, - Neck => false, - Head => true, - Tabard => false, - Bag => false, + ArmorKind::Shoulder => true, + ArmorKind::Chest => true, + ArmorKind::Belt => true, + ArmorKind::Hand => true, + ArmorKind::Pants => true, + ArmorKind::Foot => true, + ArmorKind::Back => true, + ArmorKind::Ring => false, + ArmorKind::Neck => false, + ArmorKind::Head => true, + ArmorKind::Tabard => false, + ArmorKind::Bag => false, } } } @@ -262,8 +261,7 @@ impl Armor { set_stats * multiplier }, }; - let DurabilityMultiplier(mult) = durability_multiplier; - base_stats * mult + base_stats * durability_multiplier.0 } #[cfg(test)] diff --git a/common/src/comp/inventory/item/mod.rs b/common/src/comp/inventory/item/mod.rs index b00bf7a19c..4eb7c2ddea 100644 --- a/common/src/comp/inventory/item/mod.rs +++ b/common/src/comp/inventory/item/mod.rs @@ -399,7 +399,7 @@ pub struct Item { /// Tracks how many deaths occurred while item was equipped, which is /// converted into the items durability. Only tracked for tools and armor /// currently. - durability: Option, + durability_lost: Option, } use std::hash::{Hash, Hasher}; @@ -793,9 +793,9 @@ impl Item { // These fields are updated immediately below item_config: None, hash: 0, - durability: None, + durability_lost: None, }; - item.durability = item.has_durability().then_some(0); + item.durability_lost = item.has_durability().then_some(0); item.update_item_state(ability_map, msm); item } @@ -1198,16 +1198,17 @@ impl Item { } } - pub fn durability(&self) -> Option { self.durability.map(|x| x.min(Self::MAX_DURABILITY)) } + pub fn durability(&self) -> Option { + self.durability_lost.map(|x| x.min(Self::MAX_DURABILITY)) + } pub fn stats_durability_multiplier(&self) -> DurabilityMultiplier { - let durability = self - .durability - .map_or(0, |x| x.clamp(0, Self::MAX_DURABILITY)); + let durability_lost = self.durability_lost.unwrap_or(0); + debug_assert!(durability_lost <= Self::MAX_DURABILITY); const DURABILITY_THRESHOLD: u32 = 4; const MIN_FRAC: f32 = 0.2; let mult = (1.0 - - durability.saturating_sub(DURABILITY_THRESHOLD) as f32 + - durability_lost.saturating_sub(DURABILITY_THRESHOLD) as f32 / (Self::MAX_DURABILITY - DURABILITY_THRESHOLD) as f32) * (1.0 - MIN_FRAC) + MIN_FRAC; @@ -1218,13 +1219,22 @@ impl Item { match &*self.kind() { ItemKind::Tool(_) => true, ItemKind::Armor(armor) => armor.kind.has_durability(), - _ => false, + ItemKind::ModularComponent(_) + | ItemKind::Lantern(_) + | ItemKind::Glider + | ItemKind::Consumable { .. } + | ItemKind::Throwable { .. } + | ItemKind::Utility { .. } + | ItemKind::Ingredient { .. } + | ItemKind::TagExamples { .. } => false, } } - pub fn apply_durability(&mut self, ability_map: &AbilityMap, msm: &MaterialStatManifest) { - if let Some(durability) = &mut self.durability { - *durability += 1; + pub fn increment_damage(&mut self, ability_map: &AbilityMap, msm: &MaterialStatManifest) { + if let Some(durability_lost) = &mut self.durability_lost { + if *durability_lost < Self::MAX_DURABILITY { + *durability_lost += 1; + } } // Update item state after applying durability because stats have potential to // change from different durability @@ -1232,23 +1242,23 @@ impl Item { } pub fn persistence_durability(&self) -> Option { - self.durability.and_then(NonZeroU32::new) + self.durability_lost.and_then(NonZeroU32::new) } pub fn persistence_set_durability(&mut self, value: Option) { // If changes have been made so that item no longer needs to track durability, // set to None if !self.has_durability() { - self.durability = None; + self.durability_lost = None; } else { // Set durability to persisted value, and if item previously had no durability, // set to Some(0) so that durability will be tracked - self.durability = Some(value.map_or(0, NonZeroU32::get)); + self.durability_lost = Some(value.map_or(0, NonZeroU32::get)); } } pub fn reset_durability(&mut self, ability_map: &AbilityMap, msm: &MaterialStatManifest) { - self.durability = self.durability.map(|_| 0); + self.durability_lost = self.has_durability().then_some(0); // Update item state after applying durability because stats have potential to // change from different durability self.update_item_state(ability_map, msm); diff --git a/common/src/comp/inventory/item/tool.rs b/common/src/comp/inventory/item/tool.rs index 71df384890..ee0d36ca3c 100644 --- a/common/src/comp/inventory/item/tool.rs +++ b/common/src/comp/inventory/item/tool.rs @@ -146,6 +146,20 @@ impl Stats { let diminished = (self.buff_strength - base + 1.0).log(5.0); base + diminished } + + pub fn with_durability_mult(&self, dur_mult: DurabilityMultiplier) -> Self { + let less_scaled = dur_mult.0 * 0.5 + 0.5; + Self { + equip_time_secs: self.equip_time_secs / less_scaled.max(0.01), + power: self.power * dur_mult.0, + effect_power: self.effect_power * dur_mult.0, + speed: self.speed * less_scaled, + crit_chance: self.crit_chance * dur_mult.0, + range: self.range * dur_mult.0, + energy_efficiency: self.energy_efficiency * dur_mult.0, + buff_strength: self.buff_strength * dur_mult.0, + } + } } impl Asset for Stats { @@ -213,25 +227,6 @@ impl MulAssign for Stats { fn mul_assign(&mut self, other: Stats) { *self = *self * other; } } -impl Mul for Stats { - type Output = Self; - - fn mul(self, value: DurabilityMultiplier) -> Self { - let DurabilityMultiplier(value) = value; - let less_scaled = value * 0.5 + 0.5; - Self { - equip_time_secs: self.equip_time_secs / less_scaled.max(0.01), - power: self.power * value, - effect_power: self.effect_power * value, - speed: self.speed * less_scaled, - crit_chance: self.crit_chance * value, - range: self.range * value, - energy_efficiency: self.energy_efficiency * value, - buff_strength: self.buff_strength * value, - } - } -} - impl Div for Stats { type Output = Self; @@ -249,6 +244,12 @@ impl Div for Stats { } } +impl Mul for Stats { + type Output = Self; + + fn mul(self, value: DurabilityMultiplier) -> Self { self.with_durability_mult(value) } +} + #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Tool { pub kind: ToolKind, diff --git a/common/src/comp/inventory/loadout.rs b/common/src/comp/inventory/loadout.rs index 08192c8c18..3c2f01576a 100644 --- a/common/src/comp/inventory/loadout.rs +++ b/common/src/comp/inventory/loadout.rs @@ -424,19 +424,16 @@ impl Loadout { } /// Increments durability by 1 of all valid items - pub(super) fn apply_durability( + pub(super) fn damage_items( &mut self, ability_map: &item::tool::AbilityMap, msm: &item::MaterialStatManifest, ) { self.slots .iter_mut() - .filter(|slot| slot.slot.as_ref().map_or(false, |i| i.has_durability())) - .for_each(|slot| { - if let Some(item) = &mut slot.slot { - item.apply_durability(ability_map, msm); - } - }) + .filter_map(|slot| slot.slot.as_mut()) + .filter(|item| item.has_durability()) + .for_each(|item| item.increment_damage(ability_map, msm)); } /// Resets durability of item in specified slot diff --git a/common/src/comp/inventory/mod.rs b/common/src/comp/inventory/mod.rs index 1965786433..51d1db3ead 100644 --- a/common/src/comp/inventory/mod.rs +++ b/common/src/comp/inventory/mod.rs @@ -884,12 +884,12 @@ impl Inventory { } /// Increments durability of all valid items equipped in loaodut by 1 - pub fn apply_durability( + pub fn damage_items( &mut self, ability_map: &item::tool::AbilityMap, msm: &item::MaterialStatManifest, ) { - self.loadout.apply_durability(ability_map, msm) + self.loadout.damage_items(ability_map, msm) } /// Resets durability of item in specified slot @@ -901,7 +901,7 @@ impl Inventory { ) { match slot { Slot::Inventory(invslot) => { - if let Some(Some(item)) = self.slot_mut(invslot).map(Option::as_mut) { + if let Some(Some(item)) = self.slot_mut(invslot) { item.reset_durability(ability_map, msm); } }, diff --git a/common/src/recipe.rs b/common/src/recipe.rs index 4c1ecf78b3..b14abb1a7c 100644 --- a/common/src/recipe.rs +++ b/common/src/recipe.rs @@ -14,7 +14,7 @@ use crate::{ }; use hashbrown::HashMap; use serde::{Deserialize, Serialize}; -use std::{borrow::Cow, ops::Mul, sync::Arc}; +use std::{borrow::Cow, sync::Arc}; #[derive(Clone, Debug, Serialize, Deserialize)] pub enum RecipeInput { @@ -517,13 +517,11 @@ impl assets::Compound for RecipeBook { cache: assets::AnyCache, specifier: &assets::SharedString, ) -> Result { - #[inline] fn load_item_def(spec: &(String, u32)) -> Result<(Arc, u32), assets::Error> { let def = Arc::::load_cloned(&spec.0)?; Ok((def, spec.1)) } - #[inline] fn load_recipe_input( (input, amount, is_mod_comp): &(RawRecipeInput, u32, bool), ) -> Result<(RecipeInput, u32, bool), assets::Error> { @@ -835,7 +833,6 @@ impl assets::Compound for ComponentRecipeBook { cache: assets::AnyCache, specifier: &assets::SharedString, ) -> Result { - #[inline] fn create_recipe_key(raw_recipe: &RawComponentRecipe) -> ComponentKey { match &raw_recipe.output { RawComponentOutput::ToolPrimaryComponent { toolkind, item: _ } => { @@ -853,7 +850,6 @@ impl assets::Compound for ComponentRecipeBook { } } - #[inline] fn load_recipe(raw_recipe: &RawComponentRecipe) -> Result { let output = match &raw_recipe.output { RawComponentOutput::ToolPrimaryComponent { toolkind: _, item } => { @@ -979,18 +975,20 @@ impl RepairRecipe { inventory_contains_ingredients(self.inputs(item), inv, 1) } - pub fn inputs(&self, item: &Item) -> impl ExactSizeIterator { + pub fn inputs(&self, item: &Item) -> impl Iterator { let item_durability = item.durability().unwrap_or(0); - // TODO: Figure out how to avoid vec collection to maintain exact size iterator - self.inputs.iter().filter_map(move |(input, original_amount)| { - let amount = original_amount.mul(item_durability).div_floor(Item::MAX_DURABILITY); - // If original repair recipe consumed ingredients, but item not damaged enough to actually need to consume item, remove item as requirement. - if *original_amount > 0 && amount == 0 { - None - } else { - Some((input, amount)) - } - }).collect::>().into_iter() + self.inputs + .iter() + .filter_map(move |(input, original_amount)| { + let amount = (original_amount * item_durability) / Item::MAX_DURABILITY; + // If original repair recipe consumed ingredients, but item not damaged enough + // to actually need to consume item, remove item as requirement. + if *original_amount > 0 && amount == 0 { + None + } else { + Some((input, amount)) + } + }) } } @@ -1068,7 +1066,6 @@ impl assets::Compound for RepairRecipeBook { cache: assets::AnyCache, specifier: &assets::SharedString, ) -> Result { - #[inline] fn load_recipe_input( (input, amount): &(RawRecipeInput, u32), ) -> Result<(RecipeInput, u32), assets::Error> { diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 55976ca2f5..bddc7423ae 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -515,7 +515,7 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, last_change: Healt if let Some(mut inventory) = state.ecs().write_storage::().get_mut(entity) { let ability_map = state.ecs().read_resource::(); let msm = state.ecs().read_resource::(); - inventory.apply_durability(&ability_map, &msm); + inventory.damage_items(&ability_map, &msm); } if should_delete { diff --git a/voxygen/src/hud/crafting.rs b/voxygen/src/hud/crafting.rs index 4207147773..227e62a868 100644 --- a/voxygen/src/hud/crafting.rs +++ b/voxygen/src/hud/crafting.rs @@ -1747,7 +1747,7 @@ impl<'a> Widget for Crafting<'a> { None => None, } { if let Some(recipe) = self.client.repair_recipe_book().repair_recipe(item) { - iter_d = recipe.inputs(item); + iter_d = recipe.inputs(item).collect::>().into_iter(); &mut iter_d as &mut dyn ExactSizeIterator } else { iter_b = core::iter::empty();