From 08b7bb781f86e8c2162fd29934210b5108ed080f Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 18 Nov 2021 10:41:08 -0500 Subject: [PATCH] Changed Item to have ItemBase instead of ItemDef. NO ASSETS. --- client/src/lib.rs | 5 +- common/src/combat.rs | 76 ++-- common/src/comp/ability.rs | 14 +- common/src/comp/body/item_drop.rs | 2 +- common/src/comp/inventory/item/item_key.rs | 15 +- common/src/comp/inventory/item/mod.rs | 376 ++++++++++-------- common/src/comp/inventory/item/modular.rs | 272 +++++++------ common/src/comp/inventory/item/tool.rs | 208 +++------- common/src/comp/inventory/loadout.rs | 125 +++--- common/src/comp/inventory/loadout_builder.rs | 5 +- common/src/comp/inventory/mod.rs | 20 +- common/src/comp/inventory/slot.rs | 20 +- common/src/comp/poise.rs | 2 +- common/src/recipe.rs | 43 +- common/src/states/utils.rs | 52 +-- server/src/events/entity_manipulation.rs | 2 +- server/src/events/interaction.rs | 12 +- server/src/events/inventory_manip.rs | 27 +- server/src/sys/agent.rs | 10 +- .../src/audio/sfx/event_mapper/combat/mod.rs | 2 +- voxygen/src/audio/sfx/mod.rs | 2 +- voxygen/src/hud/crafting.rs | 18 +- voxygen/src/hud/util.rs | 16 +- voxygen/src/scene/figure/cache.rs | 21 +- voxygen/src/scene/figure/mod.rs | 10 +- voxygen/src/scene/simple.rs | 7 +- voxygen/src/session/mod.rs | 4 +- voxygen/src/ui/widgets/item_tooltip.rs | 30 +- 28 files changed, 680 insertions(+), 716 deletions(-) diff --git a/client/src/lib.rs b/client/src/lib.rs index 97c6ae0a90..334d542d6b 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -1091,8 +1091,9 @@ impl Client { // Closure to get inner modular component info from item in a given slot let unwrap_modular = |slot| { - if let Some(ItemKind::ModularComponent(mod_comp)) = - inventory.and_then(|inv| inv.get(slot).map(|item| &item.kind)) + if let Some(ItemKind::ModularComponent(mod_comp)) = inventory + .and_then(|inv| inv.get(slot).map(|item| item.kind())) + .as_deref() { Some(mod_comp.modkind) } else { diff --git a/common/src/combat.rs b/common/src/combat.rs index 3d271016ac..fcfe717b52 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -3,11 +3,7 @@ use crate::comp::buff::{Buff, BuffChange, BuffData, BuffKind, BuffSource}; use crate::{ comp::{ inventory::{ - item::{ - armor::Protection, - tool::{self, Tool, ToolKind}, - Item, ItemDesc, ItemKind, MaterialStatManifest, - }, + item::{armor::Protection, tool::ToolKind, ItemDesc, ItemKind, MaterialStatManifest}, slot::EquipSlot, }, skillset::SkillGroupKind, @@ -949,26 +945,28 @@ impl CombatBuff { } #[cfg(not(target_arch = "wasm32"))] -fn equipped_item_and_tool(inv: &Inventory, slot: EquipSlot) -> Option<(&Item, &Tool)> { - inv.equipped(slot).and_then(|i| { - if let ItemKind::Tool(tool) = &i.kind() { - Some((i, tool)) - } else { - None - } - }) -} - -#[cfg(not(target_arch = "wasm32"))] -pub fn get_weapons(inv: &Inventory) -> (Option, Option) { +pub fn get_weapon_kinds(inv: &Inventory) -> (Option, Option) { ( - equipped_item_and_tool(inv, EquipSlot::ActiveMainhand).map(|(_, tool)| tool.kind), - equipped_item_and_tool(inv, EquipSlot::ActiveOffhand).map(|(_, tool)| tool.kind), + inv.equipped(EquipSlot::ActiveMainhand).and_then(|i| { + if let ItemKind::Tool(tool) = &*i.kind() { + Some(tool.kind) + } else { + None + } + }), + inv.equipped(EquipSlot::ActiveOffhand).and_then(|i| { + if let ItemKind::Tool(tool) = &*i.kind() { + Some(tool.kind) + } else { + None + } + }), ) } #[cfg(not(target_arch = "wasm32"))] -pub fn weapon_rating(item: &T, msm: &MaterialStatManifest) -> f32 { +// TODO: Either remove msm or use it as argument in fn kind +pub fn weapon_rating(item: &T, _msm: &MaterialStatManifest) -> f32 { const DAMAGE_WEIGHT: f32 = 2.0; const SPEED_WEIGHT: f32 = 3.0; const CRIT_CHANCE_WEIGHT: f32 = 1.25; @@ -978,8 +976,8 @@ pub fn weapon_rating(item: &T, msm: &MaterialStatManifest) -> f32 { const ENERGY_EFFICIENCY_WEIGHT: f32 = 0.0; const BUFF_STRENGTH_WEIGHT: f32 = 0.0; - if let ItemKind::Tool(tool) = item.kind() { - let stats = tool::Stats::from((msm, item.components(), tool)); + if let ItemKind::Tool(tool) = &*item.kind() { + let stats = tool.stats; // TODO: Look into changing the 0.5 to reflect armor later maybe? // Since it is only for weapon though, it probably makes sense to leave @@ -1018,7 +1016,7 @@ pub fn weapon_rating(item: &T, msm: &MaterialStatManifest) -> f32 { #[cfg(not(target_arch = "wasm32"))] fn weapon_skills(inventory: &Inventory, skill_set: &SkillSet) -> f32 { - let (mainhand, offhand) = get_weapons(inventory); + let (mainhand, offhand) = get_weapon_kinds(inventory); let mainhand_skills = if let Some(tool) = mainhand { skill_set.earned_sp(SkillGroupKind::Weapon(tool)) as f32 } else { @@ -1034,19 +1032,17 @@ fn weapon_skills(inventory: &Inventory, skill_set: &SkillSet) -> f32 { #[cfg(not(target_arch = "wasm32"))] fn get_weapon_rating(inventory: &Inventory, msm: &MaterialStatManifest) -> f32 { - let mainhand_rating = - if let Some((item, _)) = equipped_item_and_tool(inventory, EquipSlot::ActiveMainhand) { - weapon_rating(item, msm) - } else { - 0.0 - }; + let mainhand_rating = if let Some(item) = inventory.equipped(EquipSlot::ActiveMainhand) { + weapon_rating(item, msm) + } else { + 0.0 + }; - let offhand_rating = - if let Some((item, _)) = equipped_item_and_tool(inventory, EquipSlot::ActiveOffhand) { - weapon_rating(item, msm) - } else { - 0.0 - }; + let offhand_rating = if let Some(item) = inventory.equipped(EquipSlot::ActiveOffhand) { + weapon_rating(item, msm) + } else { + 0.0 + }; mainhand_rating.max(offhand_rating) } @@ -1118,7 +1114,7 @@ pub fn compute_crit_mult(inventory: Option<&Inventory>) -> f32 { inventory.map_or(1.25, |inv| { inv.equipped_items() .filter_map(|item| { - if let ItemKind::Armor(armor) = &item.kind() { + if let ItemKind::Armor(armor) = &*item.kind() { armor.crit_power() } else { None @@ -1136,7 +1132,7 @@ pub fn compute_energy_reward_mod(inventory: Option<&Inventory>) -> f32 { inventory.map_or(1.0, |inv| { inv.equipped_items() .filter_map(|item| { - if let ItemKind::Armor(armor) = &item.kind() { + if let ItemKind::Armor(armor) = &*item.kind() { armor.energy_reward() } else { None @@ -1154,7 +1150,7 @@ pub fn compute_max_energy_mod(inventory: Option<&Inventory>) -> f32 { inventory.map_or(0.0, |inv| { inv.equipped_items() .filter_map(|item| { - if let ItemKind::Armor(armor) = &item.kind() { + if let ItemKind::Armor(armor) = &*item.kind() { armor.energy_max() } else { None @@ -1186,7 +1182,7 @@ pub fn stealth_multiplier_from_items(inventory: Option<&Inventory>) -> f32 { let stealth_sum = inventory.map_or(0.0, |inv| { inv.equipped_items() .filter_map(|item| { - if let ItemKind::Armor(armor) = &item.kind() { + if let ItemKind::Armor(armor) = &*item.kind() { armor.stealth() } else { None @@ -1206,7 +1202,7 @@ pub fn compute_protection(inventory: Option<&Inventory>) -> Option { inventory.map_or(Some(0.0), |inv| { inv.equipped_items() .filter_map(|item| { - if let ItemKind::Armor(armor) = &item.kind() { + if let ItemKind::Armor(armor) = &*item.kind() { armor.protection() } else { None diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index a79eb54b6c..85c480aa73 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -94,7 +94,7 @@ impl ActiveAbilities { ) -> [AuxiliaryAbility; MAX_ABILITIES] { let tool_kind = |slot| { inv.and_then(|inv| inv.equipped(slot)) - .and_then(|item| match item.kind() { + .and_then(|item| match &*item.kind() { ItemKind::Tool(tool) => Some(tool.kind), _ => None, }) @@ -148,12 +148,12 @@ impl ActiveAbilities { }; let scale_ability = |ability: CharacterAbility, equip_slot| { - let tool_kind = - inv.and_then(|inv| inv.equipped(equip_slot)) - .and_then(|item| match &item.kind { - ItemKind::Tool(tool) => Some(tool.kind), - _ => None, - }); + let tool_kind = inv + .and_then(|inv| inv.equipped(equip_slot)) + .and_then(|item| match &*item.kind() { + ItemKind::Tool(tool) => Some(tool.kind), + _ => None, + }); ability.adjusted_by_skills(skill_set, tool_kind) }; diff --git a/common/src/comp/body/item_drop.rs b/common/src/comp/body/item_drop.rs index 24811c6d75..f53a705423 100644 --- a/common/src/comp/body/item_drop.rs +++ b/common/src/comp/body/item_drop.rs @@ -62,7 +62,7 @@ impl From for super::Body { impl From<&Item> for Body { fn from(item: &Item) -> Self { - match item.kind() { + match &*item.kind() { ItemKind::Tool(Tool { kind, .. }) => Body::Tool(*kind), ItemKind::ModularComponent(_) => Body::ModularComponent, ItemKind::Lantern(_) => Body::Lantern, diff --git a/common/src/comp/inventory/item/item_key.rs b/common/src/comp/inventory/item/item_key.rs index c251f902b7..f8f199926f 100644 --- a/common/src/comp/inventory/item/item_key.rs +++ b/common/src/comp/inventory/item/item_key.rs @@ -2,7 +2,7 @@ use crate::{ assets::AssetExt, comp::inventory::item::{ armor::{Armor, ArmorKind}, - modular, tool, Glider, ItemDef, ItemDesc, ItemKind, Lantern, Throwable, Utility, + modular, Glider, ItemDef, ItemDesc, ItemKind, Lantern, Throwable, Utility, }, }; use serde::{Deserialize, Serialize}; @@ -26,15 +26,14 @@ pub enum ItemKey { impl From<&T> for ItemKey { fn from(item_desc: &T) -> Self { - let item_kind = item_desc.kind(); let item_definition_id = item_desc.item_definition_id(); - match item_kind { - ItemKind::Tool(tool) => { - use tool::StatKind; - match tool.stats { - StatKind::Direct(_) => ItemKey::Tool(item_definition_id.to_owned()), - StatKind::Modular => ItemKey::ModularWeapon(modular::weapon_to_key(item_desc)), + match &*item_desc.kind() { + ItemKind::Tool(_) => { + if item_desc.is_modular() { + ItemKey::ModularWeapon(modular::weapon_to_key(item_desc)) + } else { + ItemKey::Tool(item_definition_id.to_owned()) } }, ItemKind::ModularComponent(_) => { diff --git a/common/src/comp/inventory/item/mod.rs b/common/src/comp/inventory/item/mod.rs index 9ebe0d55eb..280690e96a 100644 --- a/common/src/comp/inventory/item/mod.rs +++ b/common/src/comp/inventory/item/mod.rs @@ -4,7 +4,7 @@ pub mod modular; pub mod tool; // Reexports -pub use modular::{ModularComponent, ModularComponentKind}; +pub use modular::{ModularBase, ModularComponent, ModularComponentKind}; pub use tool::{AbilitySet, AbilitySpec, Hands, MaterialStatManifest, Tool, ToolKind}; use crate::{ @@ -18,7 +18,6 @@ use core::{ convert::TryFrom, mem, num::{NonZeroU32, NonZeroU64}, - ops::Deref, }; use crossbeam_utils::atomic::AtomicCell; use serde::{de, Deserialize, Serialize, Serializer}; @@ -71,12 +70,6 @@ pub struct Glider { pub kind: String, } -#[derive(Clone, Copy, Debug, Serialize, Deserialize)] -pub enum QualityKind { - Direct(Quality), - Modular, -} - #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Copy, PartialOrd, Ord)] pub enum Quality { Low, // Grey @@ -90,10 +83,10 @@ pub enum Quality { } pub trait TagExampleInfo { - fn name(&self) -> Cow<'static, str>; + fn name(&self) -> &str; /// What item to show in the crafting hud if the player has nothing with the /// tag - fn exemplar_identifier(&self) -> Cow<'static, str>; + fn exemplar_identifier(&self) -> &str; } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, IntoStaticStr)] @@ -215,11 +208,9 @@ impl Material { } impl TagExampleInfo for Material { - fn name(&self) -> Cow<'static, str> { Cow::Borrowed(self.into()) } + fn name(&self) -> &str { self.into() } - fn exemplar_identifier(&self) -> Cow<'static, str> { - Cow::Borrowed(self.asset_identifier().unwrap_or("")) - } + fn exemplar_identifier(&self) -> &str { self.asset_identifier().unwrap_or("") } } #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] @@ -238,36 +229,36 @@ pub enum ItemTag { } impl TagExampleInfo for ItemTag { - fn name(&self) -> Cow<'static, str> { + fn name(&self) -> &str { match self { ItemTag::Material(material) => material.name(), - ItemTag::MaterialKind(material_kind) => Cow::Borrowed(material_kind.into()), - ItemTag::Leather => Cow::Borrowed("leather"), - ItemTag::Cultist => Cow::Borrowed("cultist"), - ItemTag::Potion => Cow::Borrowed("potion"), - ItemTag::Food => Cow::Borrowed("food"), - ItemTag::BaseMaterial => Cow::Borrowed("basemat"), - ItemTag::CraftingTool => Cow::Borrowed("tool"), - ItemTag::Utility => Cow::Borrowed("utility"), - ItemTag::Bag => Cow::Borrowed("bag"), - ItemTag::SalvageInto(_) => Cow::Borrowed("salvage"), + ItemTag::MaterialKind(material_kind) => material_kind.into(), + ItemTag::Leather => "leather", + ItemTag::Cultist => "cultist", + ItemTag::Potion => "potion", + ItemTag::Food => "food", + ItemTag::BaseMaterial => "basemat", + ItemTag::CraftingTool => "tool", + ItemTag::Utility => "utility", + ItemTag::Bag => "bag", + ItemTag::SalvageInto(_) => "salvage", } } // TODO: Autogenerate these? - fn exemplar_identifier(&self) -> Cow<'static, str> { + fn exemplar_identifier(&self) -> &str { match self { - ItemTag::Material(_) => Cow::Borrowed("common.items.tag_examples.placeholder"), - ItemTag::MaterialKind(_) => Cow::Borrowed("common.items.tag_examples.placeholder"), - ItemTag::Leather => Cow::Borrowed("common.items.tag_examples.leather"), - ItemTag::Cultist => Cow::Borrowed("common.items.tag_examples.cultist"), - ItemTag::Potion => Cow::Borrowed("common.items.tag_examples.placeholder"), - ItemTag::Food => Cow::Borrowed("common.items.tag_examples.placeholder"), - ItemTag::BaseMaterial => Cow::Borrowed("common.items.tag_examples.placeholder"), - ItemTag::CraftingTool => Cow::Borrowed("common.items.tag_examples.placeholder"), - ItemTag::Utility => Cow::Borrowed("common.items.tag_examples.placeholder"), - ItemTag::Bag => Cow::Borrowed("common.items.tag_examples.placeholder"), - ItemTag::SalvageInto(_) => Cow::Borrowed("common.items.tag_examples.placeholder"), + ItemTag::Material(_) => "common.items.tag_examples.placeholder", + ItemTag::MaterialKind(_) => "common.items.tag_examples.placeholder", + ItemTag::Leather => "common.items.tag_examples.leather", + ItemTag::Cultist => "common.items.tag_examples.cultist", + ItemTag::Potion => "common.items.tag_examples.placeholder", + ItemTag::Food => "common.items.tag_examples.placeholder", + ItemTag::BaseMaterial => "common.items.tag_examples.placeholder", + ItemTag::CraftingTool => "common.items.tag_examples.placeholder", + ItemTag::Utility => "common.items.tag_examples.placeholder", + ItemTag::Bag => "common.items.tag_examples.placeholder", + ItemTag::SalvageInto(_) => "common.items.tag_examples.placeholder", } } } @@ -346,10 +337,10 @@ pub struct Item { /// could change invariants like whether it was stackable (invalidating /// the amount). #[serde( - serialize_with = "serialize_item_def", - deserialize_with = "deserialize_item_def" + serialize_with = "serialize_item_base", + deserialize_with = "deserialize_item_base" )] - item_def: Arc, + item_base: ItemBase, /// components is hidden to maintain the following invariants: /// - It should only contain modular components (and enhancements, once they /// exist) @@ -374,7 +365,7 @@ use std::hash::{Hash, Hasher}; // Used to find inventory item corresponding to hotbar slot impl Hash for Item { fn hash(&self, state: &mut H) { - self.item_def.item_definition_id.hash(state); + self.item_definition_id().hash(state); self.components.hash(state); } } @@ -382,33 +373,43 @@ impl Hash for Item { // Custom serialization for ItemDef, we only want to send the item_definition_id // over the network, the client will use deserialize_item_def to fetch the // ItemDef from assets. -fn serialize_item_def(field: &Arc, serializer: S) -> Result +fn serialize_item_base(field: &ItemBase, serializer: S) -> Result where S: Serializer, { - serializer.serialize_str(&field.item_definition_id) + serializer.serialize_str(match field { + ItemBase::Raw(item_def) => &item_def.item_definition_id, + // TODO: Encode this data somehow + ItemBase::Modular(_) => "modular", + }) } // Custom de-serialization for ItemDef to retrieve the ItemDef from assets using // its asset specifier (item_definition_id) -fn deserialize_item_def<'de, D>(deserializer: D) -> Result, D::Error> +fn deserialize_item_base<'de, D>(deserializer: D) -> Result where D: de::Deserializer<'de>, { struct ItemDefStringVisitor; impl<'de> de::Visitor<'de> for ItemDefStringVisitor { - type Value = Arc; + type Value = ItemBase; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("item def string") } - fn visit_str(self, item_definition_id: &str) -> Result + fn visit_str(self, serialized_item_base: &str) -> Result where E: de::Error, { - Ok(Arc::::load_expect_cloned(item_definition_id)) + Ok(match serialized_item_base { + // TODO: Make this work + "modular" => ItemBase::Modular(ModularBase::Tool(ToolKind::Empty)), + item_definition_id => { + ItemBase::Raw(Arc::::load_expect_cloned(item_definition_id)) + }, + }) } } @@ -422,14 +423,36 @@ pub enum ItemName { Component(String), } +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum ItemBase { + Raw(Arc), + Modular(modular::ModularBase), +} + +impl ItemBase { + fn num_slots(&self) -> u16 { + match self { + ItemBase::Raw(item_def) => item_def.num_slots(), + ItemBase::Modular(_) => 0, + } + } + + fn item_definition_id(&self) -> &str { + match &self { + ItemBase::Raw(item_def) => &item_def.item_definition_id, + ItemBase::Modular(mod_base) => mod_base.pseudo_item_id(), + } + } +} + #[derive(Debug, Serialize, Deserialize)] pub struct ItemDef { #[serde(default)] item_definition_id: String, - pub name: ItemName, + pub name: String, pub description: String, pub kind: ItemKind, - pub quality: QualityKind, + pub quality: Quality, pub tags: Vec, #[serde(default)] pub slots: u16, @@ -457,17 +480,18 @@ impl TryFrom<(&Item, &AbilityMap, &MaterialStatManifest)> for ItemConfig { type Error = ItemConfigError; fn try_from( - (item, ability_map, msm): (&Item, &AbilityMap, &MaterialStatManifest), + // TODO: Either remove msm or use it as argument in fn kind + (item, ability_map, _msm): (&Item, &AbilityMap, &MaterialStatManifest), ) -> Result { - if let ItemKind::Tool(tool) = &item.kind { + if let ItemKind::Tool(tool) = &*item.kind() { // If no custom ability set is specified, fall back to abilityset of tool kind. let tool_default = |tool_kind| { let key = &AbilitySpec::Tool(tool_kind); ability_map.get_ability_set(key) }; let abilities = if let Some(set_key) = item.ability_spec() { - if let Some(set) = ability_map.get_ability_set(set_key) { - set.clone().modified_by_tool(tool, msm, &item.components) + if let Some(set) = ability_map.get_ability_set(&*set_key) { + set.clone().modified_by_tool(tool) } else { error!( "Custom ability set: {:?} references non-existent set, falling back to \ @@ -477,7 +501,7 @@ impl TryFrom<(&Item, &AbilityMap, &MaterialStatManifest)> for ItemConfig { tool_default(tool.kind).cloned().unwrap_or_default() } } else if let Some(set) = tool_default(tool.kind) { - set.clone().modified_by_tool(tool, msm, &item.components) + set.clone().modified_by_tool(tool) } else { error!( "No ability set defined for tool: {:?}, falling back to default ability set.", @@ -504,16 +528,6 @@ impl ItemDef { ) } - pub fn is_modular(&self) -> bool { - matches!( - &self.kind, - ItemKind::Tool(tool::Tool { - stats: tool::StatKind::Modular, - .. - }) | ItemKind::ModularComponent(_) - ) - } - pub fn is_component(&self, kind: ModularComponentKind) -> bool { if let ItemKind::ModularComponent(ModularComponent { modkind, .. }) = self.kind { kind == modkind @@ -568,28 +582,21 @@ impl ItemDef { /// please don't rely on this for anything! impl PartialEq for Item { fn eq(&self, other: &Self) -> bool { - self.item_def.item_definition_id == other.item_def.item_definition_id + if let (ItemBase::Raw(self_def), ItemBase::Raw(other_def)) = + (&self.item_base, &other.item_base) + { + self_def.item_definition_id == other_def.item_definition_id + } else { + false + } } } -impl Deref for Item { - type Target = ItemDef; - - fn deref(&self) -> &Self::Target { &self.item_def } -} - impl assets::Compound for ItemDef { fn load( cache: &assets::AssetCache, specifier: &str, ) -> Result { - // load from the filesystem first, but if the file doesn't exist, see if it's a - // programmatically-generated asset - let raw = match cache.load::(specifier) { - Ok(handle) => handle.cloned(), - Err(e) => modular::synthesize_modular_asset(specifier).ok_or(e)?, - }; - let RawItemDef { name, description, @@ -598,7 +605,7 @@ impl assets::Compound for ItemDef { tags, slots, ability_spec, - } = raw; + } = cache.load::(specifier)?.cloned(); // Some commands like /give_item provide the asset specifier separated with \ // instead of . @@ -622,10 +629,10 @@ impl assets::Compound for ItemDef { #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename = "ItemDef")] struct RawItemDef { - name: ItemName, + name: String, description: String, kind: ItemKind, - quality: QualityKind, + quality: Quality, tags: Vec, #[serde(default)] slots: u16, @@ -646,25 +653,22 @@ impl Item { // loadout when no weapon is present pub fn empty() -> Self { Item::new_from_asset_expect("common.items.weapons.empty.empty") } - pub fn new_from_item_def( - inner_item: Arc, + pub fn new_from_item_base( + inner_item: ItemBase, input_components: &[Item], ability_map: &AbilityMap, msm: &MaterialStatManifest, ) -> Self { let mut components = Vec::new(); - if inner_item.is_modular() { - // recipe ensures that types match (i.e. no axe heads on a sword hilt, or double - // sword blades) - components.extend( - input_components - .iter() - .map(|comp| comp.duplicate(ability_map, msm)), - ); - } + components.extend( + input_components + .iter() + .map(|comp| comp.duplicate(ability_map, msm)), + ); + let item_hash = { let mut s = DefaultHasher::new(); - inner_item.item_definition_id.hash(&mut s); + inner_item.item_definition_id().hash(&mut s); components.hash(&mut s); s.finish() }; @@ -673,8 +677,8 @@ impl Item { item_id: Arc::new(AtomicCell::new(None)), amount: NonZeroU32::new(1).unwrap(), components, - slots: vec![None; inner_item.slots as usize], - item_def: inner_item, + slots: vec![None; inner_item.num_slots() as usize], + item_base: inner_item, item_config: None, hash: item_hash, }; @@ -685,11 +689,7 @@ impl Item { /// Creates a new instance of an `Item` from the provided asset identifier /// Panics if the asset does not exist. pub fn new_from_asset_expect(asset_specifier: &str) -> Self { - let inner_item = Arc::::load_expect_cloned(asset_specifier); - // TODO: Figure out better way to get msm and ability_map - let msm = MaterialStatManifest::default(); - let ability_map = AbilityMap::default(); - Item::new_from_item_def(inner_item, &[], &ability_map, &msm) + Item::new_from_asset(asset_specifier).expect("Expected asset to exist") } /// Creates a Vec containing one of each item that matches the provided @@ -703,18 +703,26 @@ impl Item { /// Creates a new instance of an `Item from the provided asset identifier if /// it exists pub fn new_from_asset(asset: &str) -> Result { - let inner_item = Arc::::load_cloned(asset)?; + let inner_item = ItemBase::Raw(Arc::::load_cloned(asset)?); // TODO: Get msm and ability_map less hackily let msm = MaterialStatManifest::default(); let ability_map = AbilityMap::default(); - Ok(Item::new_from_item_def(inner_item, &[], &ability_map, &msm)) + Ok(Item::new_from_item_base( + inner_item, + &[], + &ability_map, + &msm, + )) } /// Duplicates an item, creating an exact copy but with a new item ID #[must_use] pub fn duplicate(&self, ability_map: &AbilityMap, msm: &MaterialStatManifest) -> Self { - let mut new_item = Item::new_from_item_def( - Arc::clone(&self.item_def), + let mut new_item = Item::new_from_item_base( + match &self.item_base { + ItemBase::Raw(item_def) => ItemBase::Raw(Arc::clone(item_def)), + ItemBase::Modular(mod_base) => ItemBase::Modular(mod_base.duplicate()), + }, &self.components, ability_map, msm, @@ -827,18 +835,28 @@ impl Item { self.slots.iter_mut().filter_map(mem::take) } - pub fn item_definition_id(&self) -> &str { &self.item_def.item_definition_id } + pub fn item_definition_id(&self) -> &str { + match &self.item_base { + ItemBase::Raw(item_def) => &item_def.item_definition_id, + // TODO: Should this be handled better? + ItemBase::Modular(_) => "", + } + } pub fn is_same_item_def(&self, item_def: &ItemDef) -> bool { - self.item_def.item_definition_id == item_def.item_definition_id + if let ItemBase::Raw(self_def) = &self.item_base { + self_def.item_definition_id == item_def.item_definition_id + } else { + false + } } pub fn matches_recipe_input(&self, recipe_input: &RecipeInput) -> bool { match recipe_input { RecipeInput::Item(item_def) => self.is_same_item_def(item_def), - RecipeInput::Tag(tag) => self.item_def.tags.contains(tag), + RecipeInput::Tag(tag) => self.tags().contains(tag), RecipeInput::TagSameItem(tag, amount) => { - self.item_def.tags.contains(tag) && u32::from(self.amount) >= *amount + self.tags().contains(tag) && u32::from(self.amount) >= *amount }, RecipeInput::ListSameItem(item_defs, amount) => item_defs.iter().any(|item_def| { self.is_same_item_def(item_def) && u32::from(self.amount) >= *amount @@ -847,48 +865,65 @@ impl Item { } pub fn is_salvageable(&self) -> bool { - self.item_def - .tags + self.tags() .iter() .any(|tag| matches!(tag, ItemTag::SalvageInto(_))) } pub fn salvage_output(&self) -> impl Iterator { - self.item_def - .tags - .iter() - .filter_map(|tag| { - if let ItemTag::SalvageInto(material) = tag { - Some(material) - } else { - None - } - }) - .filter_map(|material| material.asset_identifier()) + self.tags().iter().filter_map(|tag| { + if let ItemTag::SalvageInto(material) = tag { + material.asset_identifier() + } else { + None + } + }) } - pub fn name(&self) -> Cow<'_, str> { - match &self.item_def.name { - ItemName::Direct(name) => Cow::Borrowed(name), - ItemName::Modular => modular::modular_name(self, ""), - ItemName::Component(name) => modular::modular_name(self, name), + pub fn name(&self) -> Cow { + match &self.item_base { + ItemBase::Raw(item_def) => Cow::Borrowed(&item_def.name), + ItemBase::Modular(mod_base) => mod_base.generate_name(self.components()), } } - pub fn description(&self) -> &str { &self.item_def.description } + pub fn description(&self) -> &str { + match &self.item_base { + ItemBase::Raw(item_def) => &item_def.description, + // TODO: See if James wanted to make description, else leave with none + ItemBase::Modular(_) => "", + } + } - pub fn kind(&self) -> &ItemKind { &self.item_def.kind } + pub fn kind(&self) -> Cow { + // TODO: Try to move further upward + let msm = MaterialStatManifest::default(); + match &self.item_base { + ItemBase::Raw(item_def) => Cow::Borrowed(&item_def.kind), + ItemBase::Modular(mod_base) => mod_base.kind(self.components(), &msm), + } + } pub fn amount(&self) -> u32 { u32::from(self.amount) } + pub fn is_stackable(&self) -> bool { + match &self.item_base { + ItemBase::Raw(item_def) => item_def.is_stackable(), + // TODO: Let whoever implements stackable modular items deal with this + ItemBase::Modular(_) => false, + } + } + + pub fn num_slots(&self) -> u16 { self.item_base.num_slots() } + /// NOTE: invariant that amount() ≤ max_amount(), 1 ≤ max_amount(), /// and if !self.is_stackable(), self.max_amount() = 1. pub fn max_amount(&self) -> u32 { if self.is_stackable() { u32::MAX } else { 1 } } pub fn quality(&self) -> Quality { - match self.item_def.quality { - QualityKind::Direct(quality) => quality, - QualityKind::Modular => modular::resolve_quality(self), + match &self.item_base { + ItemBase::Raw(item_def) => item_def.quality, + ItemBase::Modular(mod_base) => mod_base.compute_quality(self.components()), } } @@ -916,7 +951,27 @@ impl Item { block.get_sprite()?.collectible_id()?.to_item() } - pub fn ability_spec(&self) -> Option<&AbilitySpec> { self.item_def.ability_spec.as_ref() } + pub fn ability_spec(&self) -> Option> { + match &self.item_base { + ItemBase::Raw(item_def) => item_def.ability_spec.as_ref().map(Cow::Borrowed), + ItemBase::Modular(mod_base) => mod_base.ability_spec(self.components()), + } + } + + pub fn tags(&self) -> &[ItemTag] { + match &self.item_base { + ItemBase::Raw(item_def) => &item_def.tags, + // TODO: Do this properly. It'll probably be important at some point. + ItemBase::Modular(_) => &[], + } + } + + pub fn is_modular(&self) -> bool { + match &self.item_base { + ItemBase::Raw(_) => false, + ItemBase::Modular(_) => true, + } + } pub fn item_hash(&self) -> u64 { self.hash } @@ -935,19 +990,21 @@ impl Item { /// for either an `Item` containing the definition, or the actual `ItemDef` pub trait ItemDesc { fn description(&self) -> &str; - fn name(&self) -> Cow<'_, str>; - fn kind(&self) -> &ItemKind; + fn name(&self) -> Cow; + fn kind(&self) -> Cow; fn quality(&self) -> Quality; fn num_slots(&self) -> u16; fn item_definition_id(&self) -> &str; fn tags(&self) -> &[ItemTag]; fn concrete_item(&self) -> Option<&Item>; + fn is_modular(&self) -> bool; + fn components(&self) -> &[Item] { self.concrete_item().map_or(&[], |i| i.components()) } - fn tool(&self) -> Option<&Tool> { - if let ItemKind::Tool(tool) = self.kind() { - Some(tool) + fn tool_info(&self) -> Option { + if let ItemKind::Tool(tool) = &*self.kind() { + Some(tool.kind) } else { None } @@ -955,48 +1012,33 @@ pub trait ItemDesc { } impl ItemDesc for Item { - fn description(&self) -> &str { &self.item_def.description } + fn description(&self) -> &str { self.description() } - fn name(&self) -> Cow<'_, str> { self.name() } + fn name(&self) -> Cow { self.name() } - fn kind(&self) -> &ItemKind { &self.item_def.kind } + fn kind(&self) -> Cow { self.kind() } fn quality(&self) -> Quality { self.quality() } - fn num_slots(&self) -> u16 { self.item_def.slots } + fn num_slots(&self) -> u16 { self.num_slots() } - fn item_definition_id(&self) -> &str { &self.item_def.item_definition_id } + fn item_definition_id(&self) -> &str { self.item_definition_id() } - fn tags(&self) -> &[ItemTag] { &self.item_def.tags } + fn tags(&self) -> &[ItemTag] { self.tags() } fn concrete_item(&self) -> Option<&Item> { Some(self) } + + fn is_modular(&self) -> bool { self.is_modular() } } impl ItemDesc for ItemDef { fn description(&self) -> &str { &self.description } - fn name(&self) -> Cow<'_, str> { - match &self.name { - ItemName::Direct(name) | ItemName::Component(name) => Cow::Borrowed(name), - ItemName::Modular => { - let toolkind = if let ItemKind::Tool(tool) = &self.kind { - tool.kind.identifier_name() - } else { - "Weapon" - }; - Cow::Owned(format!("Modular {}", toolkind)) - }, - } - } + fn name(&self) -> Cow { Cow::Borrowed(&self.name) } - fn kind(&self) -> &ItemKind { &self.kind } + fn kind(&self) -> Cow { Cow::Borrowed(&self.kind) } - fn quality(&self) -> Quality { - match &self.quality { - QualityKind::Direct(quality) => *quality, - QualityKind::Modular => Quality::Common, - } - } + fn quality(&self) -> Quality { self.quality } fn num_slots(&self) -> u16 { self.slots } @@ -1005,6 +1047,8 @@ impl ItemDesc for ItemDef { fn tags(&self) -> &[ItemTag] { &self.tags } fn concrete_item(&self) -> Option<&Item> { None } + + fn is_modular(&self) -> bool { false } } impl Component for Item { @@ -1021,9 +1065,9 @@ impl Component for ItemDrop { impl<'a, T: ItemDesc + ?Sized> ItemDesc for &'a T { fn description(&self) -> &str { (*self).description() } - fn name(&self) -> Cow<'_, str> { (*self).name() } + fn name(&self) -> Cow { (*self).name() } - fn kind(&self) -> &ItemKind { (*self).kind() } + fn kind(&self) -> Cow { (*self).kind() } fn quality(&self) -> Quality { (*self).quality() } @@ -1036,6 +1080,8 @@ impl<'a, T: ItemDesc + ?Sized> ItemDesc for &'a T { fn tags(&self) -> &[ItemTag] { (*self).tags() } fn concrete_item(&self) -> Option<&Item> { None } + + fn is_modular(&self) -> bool { (*self).is_modular() } } /// Returns all item asset specifiers diff --git a/common/src/comp/inventory/item/modular.rs b/common/src/comp/inventory/item/modular.rs index 65cf856496..3fbc23b676 100644 --- a/common/src/comp/inventory/item/modular.rs +++ b/common/src/comp/inventory/item/modular.rs @@ -1,13 +1,136 @@ use super::{ - tool::{self, Hands}, - Item, ItemDesc, ItemKind, ItemName, RawItemDef, ToolKind, + tool::{self, AbilitySpec, Hands, MaterialStatManifest, Stats}, + Item, ItemBase, ItemDesc, ItemKind, Quality, ToolKind, }; use crate::{assets::AssetExt, lottery::Lottery, recipe}; -use hashbrown::HashMap; -use lazy_static::lazy_static; use serde::{Deserialize, Serialize}; use std::borrow::Cow; +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum ModularBase { + Tool(ToolKind), +} + +impl ModularBase { + pub(super) fn duplicate(&self) -> Self { + match self { + ModularBase::Tool(toolkind) => ModularBase::Tool(*toolkind), + } + } + + pub fn kind(&self, components: &[Item], msm: &MaterialStatManifest) -> Cow { + fn resolve_hands(components: &[Item]) -> Hands { + // Checks if weapon has components that restrict hands to two. Restrictions to + // one hand or no restrictions default to one-handed weapon. + let is_two_handed = components.iter().any(|item| matches!(&*item.kind(), ItemKind::ModularComponent(mc) if matches!(mc.hand_restriction, Some(Hands::Two)))); + // If weapon is two handed, make it two handed + if is_two_handed { + Hands::Two + } else { + Hands::One + } + } + + pub fn resolve_stats(components: &[Item], msm: &MaterialStatManifest) -> Stats { + let mut stats = Stats::one(); + let mut material_multipliers: Vec = Vec::new(); + for item in components.iter() { + match &*item.kind() { + // Modular components directly multiply against the base stats + ItemKind::ModularComponent(mc) => { + let inner_stats = mc.stats * resolve_stats(item.components(), msm); + stats *= inner_stats; + }, + // Ingredients push multiplier to vec as the ingredient multipliers are averaged + ItemKind::Ingredient { .. } => { + if let Some(mult_stats) = msm.0.get(item.item_definition_id()) { + material_multipliers.push(*mult_stats); + } + }, + _ => (), + } + } + + // Take the average of the material multipliers + if !material_multipliers.is_empty() { + let mut average_mult = Stats::zero(); + for stat in material_multipliers.iter() { + average_mult += *stat; + } + average_mult /= material_multipliers.len(); + stats *= average_mult; + } + stats + } + + match self { + ModularBase::Tool(toolkind) => Cow::Owned(ItemKind::Tool(tool::Tool { + kind: *toolkind, + hands: resolve_hands(components), + stats: resolve_stats(components, msm), + })), + } + } + + /// Modular weapons are named as "{Material} {Weapon}" where {Weapon} is + /// from the damage component used and {Material} is from the material + /// the damage component is created from. + pub fn generate_name(&self, components: &[Item]) -> Cow { + match self { + ModularBase::Tool(toolkind) => { + // Closure to get material name from an item + fn material_name(component: &Item) -> String { + component + .components() + .iter() + .filter_map(|comp| match &*comp.kind() { + ItemKind::Ingredient { descriptor, .. } => Some(descriptor.to_owned()), + _ => None, + }) + .next() + .unwrap_or_else(|| "Modular".to_owned()) + } + + let main_component = components.iter().find(|comp| { + matches!(&*comp.kind(), ItemKind::ModularComponent(ModularComponent { modkind, .. }) + if *modkind == ModularComponentKind::main_component(*toolkind) + ) + }); + let (material_name, weapon_name) = if let Some(component) = main_component { + let material_name = material_name(component); + let weapon_name = if let ItemKind::ModularComponent(ModularComponent { + weapon_name, + .. + }) = &*component.kind() + { + weapon_name.to_owned() + } else { + toolkind.identifier_name().to_owned() + }; + (material_name, weapon_name) + } else { + ("Modular".to_owned(), toolkind.identifier_name().to_owned()) + }; + + Cow::Owned(format!("{} {}", material_name, weapon_name)) + }, + } + } + + pub fn compute_quality(&self, components: &[Item]) -> Quality { + components + .iter() + .fold(Quality::Low, |a, b| a.max(b.quality())) + } + + pub fn ability_spec(&self, _components: &[Item]) -> Option> { + match self { + ModularBase::Tool(toolkind) => Some(Cow::Owned(AbilitySpec::Tool(*toolkind))), + } + } +} + +// TODO: Look into changing to: Primary, Secondary #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum ModularComponentKind { Damage, @@ -53,106 +176,8 @@ const SUPPORTED_TOOLKINDS: [ToolKind; 6] = [ const WEAPON_PREFIX: &str = "common.items.weapons.modular"; -fn make_weapon_def(toolkind: ToolKind) -> (String, RawItemDef) { - let identifier = format!("{}.{}", WEAPON_PREFIX, toolkind.identifier_name()); - let name = ItemName::Modular; - let tool = tool::Tool { - kind: toolkind, - hands: tool::HandsKind::Modular, - stats: tool::StatKind::Modular, - }; - let kind = ItemKind::Tool(tool); - let item = RawItemDef { - name, - description: "".to_string(), - kind, - quality: super::QualityKind::Modular, - tags: Vec::new(), - slots: 0, - ability_spec: None, - }; - (identifier, item) -} - -fn initialize_modular_assets() -> HashMap { - let mut itemdefs = HashMap::new(); - for &toolkind in &SUPPORTED_TOOLKINDS { - let (identifier, item) = make_weapon_def(toolkind); - itemdefs.insert(identifier, item); - } - itemdefs -} - -lazy_static! { - static ref ITEM_DEFS: HashMap = initialize_modular_assets(); -} - -/// Synthesize modular assets programmatically, to allow for the following: -/// - Allow the modular tag_examples to auto-update with the list of applicable -/// components -pub(super) fn synthesize_modular_asset(specifier: &str) -> Option { - let ret = ITEM_DEFS.get(specifier).cloned(); - tracing::trace!("synthesize_modular_asset({:?}) -> {:?}", specifier, ret); - ret -} - -/// Modular weapons are named as "{Material} {Weapon}" where {Weapon} is from -/// the damage component used and {Material} is from the material the damage -/// component is created from. -pub(super) fn modular_name<'a>(item: &'a Item, arg1: &'a str) -> Cow<'a, str> { - // Closure to get material name from an item - let material_name = |component: &Item| { - component - .components() - .iter() - .filter_map(|comp| match comp.kind() { - ItemKind::Ingredient { descriptor, .. } => Some(descriptor.to_owned()), - _ => None, - }) - .last() - .unwrap_or_else(|| "Modular".to_owned()) - }; - - match item.kind() { - ItemKind::Tool(tool) => { - let main_components = item.components().iter().filter(|comp| { - matches!(comp.kind(), ItemKind::ModularComponent(ModularComponent { modkind, .. }) - if *modkind == ModularComponentKind::main_component(tool.kind) - ) - }); - // Last fine as there should only ever be one damage component on a weapon - let (material_name, weapon_name) = if let Some(component) = main_components.last() { - let material_name = material_name(component); - let weapon_name = - if let ItemKind::ModularComponent(ModularComponent { weapon_name, .. }) = - component.kind() - { - weapon_name - } else { - tool.kind.identifier_name() - }; - (material_name, weapon_name) - } else { - ("Modular".to_owned(), tool.kind.identifier_name()) - }; - - Cow::Owned(format!("{} {}", material_name, weapon_name)) - }, - ItemKind::ModularComponent(comp) => match comp.modkind { - ModularComponentKind::Damage => { - let material_name = material_name(item); - Cow::Owned(format!("{} {}", material_name, arg1)) - }, - ModularComponentKind::Held => Cow::Borrowed(arg1), - }, - _ => Cow::Borrowed("Modular Item"), - } -} - -pub(super) fn resolve_quality(item: &Item) -> super::Quality { - item.components - .iter() - .fold(super::Quality::Low, |a, b| a.max(b.quality())) +fn make_weapon_id(toolkind: ToolKind) -> String { + format!("{}.{}", WEAPON_PREFIX, toolkind.identifier_name()) } /// Returns directory that contains components for a particular combination of @@ -167,18 +192,14 @@ fn make_mod_comp_dir_spec(tool: ToolKind, mod_kind: ModularComponentKind) -> Str ) } -/// Creates initial item for a modular weapon -pub fn initialize_modular_weapon(toolkind: ToolKind) -> Item { - Item::new_from_asset_expect(&make_weapon_def(toolkind).0) -} - /// Creates a random modular weapon when provided with a toolkind, material, and /// optionally the handedness pub fn random_weapon(tool: ToolKind, material: super::Material, hands: Option) -> Item { // Returns inner modular component of an item if it has one - fn unwrap_modular_component(item: &Item) -> Option<&ModularComponent> { - if let ItemKind::ModularComponent(mod_comp) = item.kind() { - Some(mod_comp) + fn unwrap_modular_component(item: &Item) -> Option { + if let ItemKind::ModularComponent(mod_comp) = &*item.kind() { + // TODO: Maybe get rid of clone? + Some(mod_comp.clone()) } else { None } @@ -188,9 +209,6 @@ pub fn random_weapon(tool: ToolKind, material: super::Material, hands: Option ModularWeaponKey { - let hands = if let ItemKind::Tool(tool) = mod_weap.kind() { - tool.hands.resolve_hands(mod_weap.components()) + let hands = if let ItemKind::Tool(tool) = &*mod_weap.kind() { + tool.hands } else { Hands::One }; let (main_comp, material) = if let Some(main_comp) = mod_weap.components().iter().find(|comp| { - matches!(comp.kind(), ItemKind::ModularComponent(mod_comp) if ModularComponentKind::main_component(mod_comp.toolkind) == mod_comp.modkind) + matches!(&*comp.kind(), ItemKind::ModularComponent(mod_comp) if ModularComponentKind::main_component(mod_comp.toolkind) == mod_comp.modkind) }) { let material = if let Some(material) = main_comp.components().iter().filter_map(|mat| { if let Some(super::ItemTag::Material(material)) = mat.tags().iter().find(|tag| matches!(tag, super::ItemTag::Material(_))) { @@ -326,7 +346,7 @@ pub enum ModularWeaponComponentKeyError { pub fn weapon_component_to_key( mod_weap_comp: &dyn ItemDesc, ) -> Result { - if let ItemKind::ModularComponent(mod_comp) = mod_weap_comp.kind() { + if let ItemKind::ModularComponent(mod_comp) = &*mod_weap_comp.kind() { if ModularComponentKind::main_component(mod_comp.toolkind) == mod_comp.modkind { let material = if let Some(material) = mod_weap_comp .components() diff --git a/common/src/comp/inventory/item/tool.rs b/common/src/comp/inventory/item/tool.rs index ff932632d2..b9c1018a85 100644 --- a/common/src/comp/inventory/item/tool.rs +++ b/common/src/comp/inventory/item/tool.rs @@ -3,12 +3,12 @@ use crate::{ assets::{self, Asset, AssetExt}, - comp::{item::ItemKind, skills::Skill, CharacterAbility, Item}, + comp::{skills::Skill, CharacterAbility}, }; use hashbrown::HashMap; use serde::{Deserialize, Serialize}; use std::{ - ops::{AddAssign, DivAssign, MulAssign, Sub}, + ops::{AddAssign, DivAssign, Mul, MulAssign, Sub}, time::Duration, }; @@ -75,30 +75,16 @@ impl ToolKind { | ToolKind::Shield ) } -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub enum HandsKind { - Direct(Hands), - Modular, -} - -impl HandsKind { - pub fn resolve_hands(&self, components: &[Item]) -> Hands { - match self { - HandsKind::Direct(hands) => *hands, - HandsKind::Modular => { - // Checks if weapon has components that restrict hands to two. Restrictions to - // one hand or no restrictions default to one-handed weapon. - let is_two_handed = components.iter().any(|item| matches!(item.kind(), ItemKind::ModularComponent(mc) if matches!(mc.hand_restriction, Some(Hands::Two)))); - // If weapon is two handed, make it two handed - if is_two_handed { - Hands::Two - } else { - Hands::One - } - }, - } + pub fn can_block(&self) -> bool { + matches!( + self, + ToolKind::Sword + | ToolKind::Axe + | ToolKind::Hammer + | ToolKind::Shield + | ToolKind::Dagger + ) } } @@ -108,16 +94,6 @@ pub enum Hands { Two, } -impl Hands { - // Changing this will break persistence of modular weapons - pub fn identifier_name(&self) -> &'static str { - match self { - Hands::One => "one-handed", - Hands::Two => "two-handed", - } - } -} - #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] pub struct Stats { pub equip_time_secs: f32, @@ -131,7 +107,7 @@ pub struct Stats { } impl Stats { - pub fn zeroed() -> Stats { + pub fn zero() -> Stats { Stats { equip_time_secs: 0.0, power: 0.0, @@ -156,14 +132,6 @@ impl Stats { buff_strength: 1.0, } } - - #[must_use] - pub fn clamp_speed(mut self) -> Self { - // if a tool has 0.0 speed, that panics due to being infinite duration, so - // enforce speed >= 0.1 on the final product (but not the intermediates) - self.speed = self.speed.max(0.1); - self - } } impl Asset for Stats { @@ -180,6 +148,7 @@ impl AddAssign for Stats { self.speed += other.speed; self.crit_chance += other.crit_chance; self.range += other.range; + self.energy_efficiency += other.energy_efficiency; self.buff_strength += other.buff_strength; } } @@ -191,20 +160,35 @@ impl MulAssign for Stats { self.speed *= other.speed; self.crit_chance *= other.crit_chance; self.range *= other.range; + self.energy_efficiency *= other.energy_efficiency; self.buff_strength *= other.buff_strength; } } +impl Mul for Stats { + type Output = Self; + + fn mul(self, other: Self) -> Self { + Self { + equip_time_secs: self.equip_time_secs * other.equip_time_secs, + power: self.power * other.power, + effect_power: self.effect_power * other.effect_power, + speed: self.speed * other.speed, + crit_chance: self.crit_chance * other.crit_chance, + range: self.range * other.range, + energy_efficiency: self.energy_efficiency * other.energy_efficiency, + buff_strength: self.buff_strength * other.buff_strength, + } + } +} impl DivAssign for Stats { fn div_assign(&mut self, scalar: usize) { self.equip_time_secs /= scalar as f32; - // since averaging occurs when the stats are used multiplicatively, don't permit - // multiplying an equip_time_secs by 0, since that would be overpowered - self.equip_time_secs = self.equip_time_secs.max(0.001); self.power /= scalar as f32; self.effect_power /= scalar as f32; self.speed /= scalar as f32; self.crit_chance /= scalar as f32; self.range /= scalar as f32; + self.energy_efficiency /= scalar as f32; self.buff_strength /= scalar as f32; } } @@ -244,80 +228,24 @@ impl Default for MaterialStatManifest { } } -#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] -pub enum StatKind { - Direct(Stats), - Modular, -} - -impl StatKind { - pub fn resolve_stats(&self, msm: &MaterialStatManifest, components: &[Item]) -> Stats { - let mut stats = match self { - StatKind::Direct(stats) => *stats, - StatKind::Modular => Stats::one(), - }; - let mut multipliers: Vec = Vec::new(); - for item in components.iter() { - match item.kind() { - // Modular components directly multiply against the base stats - ItemKind::ModularComponent(mc) => { - let inner_stats = - StatKind::Direct(mc.stats).resolve_stats(msm, item.components()); - stats *= inner_stats; - }, - // Ingredients push multiplier to vec as the ingredient multipliers are averaged - ItemKind::Ingredient { .. } => { - if let Some(mult_stats) = msm.0.get(item.item_definition_id()) { - multipliers.push(*mult_stats); - } - }, - // TODO: add stats from enhancement slots - _ => (), - } - } - // Take the average of the material multipliers - if !multipliers.is_empty() { - let mut average_mult = Stats::zeroed(); - for stat in multipliers.iter() { - average_mult += *stat; - } - average_mult /= multipliers.len(); - stats *= average_mult; - } - stats - } -} - -impl From<(&MaterialStatManifest, &[Item], &Tool)> for Stats { - fn from((msm, components, tool): (&MaterialStatManifest, &[Item], &Tool)) -> Self { - tool.stats.resolve_stats(msm, components).clamp_speed() - } -} - #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Tool { pub kind: ToolKind, - pub hands: HandsKind, - pub stats: StatKind, + pub hands: Hands, + pub stats: Stats, // TODO: item specific abilities } impl Tool { // DO NOT USE UNLESS YOU KNOW WHAT YOU ARE DOING // Added for CSV import of stats - pub fn new(kind: ToolKind, hands: Hands, stats: Stats) -> Self { - Self { - kind, - hands: HandsKind::Direct(hands), - stats: StatKind::Direct(stats), - } - } + pub fn new(kind: ToolKind, hands: Hands, stats: Stats) -> Self { Self { kind, hands, stats } } pub fn empty() -> Self { Self { kind: ToolKind::Empty, - hands: HandsKind::Direct(Hands::One), - stats: StatKind::Direct(Stats { + hands: Hands::One, + stats: Stats { equip_time_secs: 0.0, power: 1.00, effect_power: 1.00, @@ -326,56 +254,30 @@ impl Tool { range: 1.0, energy_efficiency: 1.0, buff_strength: 1.0, - }), + }, } } // Keep power between 0.5 and 2.00 - pub fn base_power(&self, msm: &MaterialStatManifest, components: &[Item]) -> f32 { - self.stats.resolve_stats(msm, components).power + pub fn base_power(&self) -> f32 { self.stats.power } + + pub fn base_effect_power(&self) -> f32 { self.stats.effect_power } + + pub fn base_speed(&self) -> f32 { + // Has floor to prevent infinite durations being created later down due to a + // divide by zero + self.stats.speed.max(0.1) } - pub fn base_effect_power(&self, msm: &MaterialStatManifest, components: &[Item]) -> f32 { - self.stats.resolve_stats(msm, components).effect_power - } + pub fn base_crit_chance(&self) -> f32 { self.stats.crit_chance } - pub fn base_speed(&self, msm: &MaterialStatManifest, components: &[Item]) -> f32 { - self.stats - .resolve_stats(msm, components) - .clamp_speed() - .speed - } + pub fn base_range(&self) -> f32 { self.stats.range } - pub fn base_crit_chance(&self, msm: &MaterialStatManifest, components: &[Item]) -> f32 { - self.stats.resolve_stats(msm, components).crit_chance - } + pub fn base_energy_efficiency(&self) -> f32 { self.stats.energy_efficiency } - pub fn base_range(&self, msm: &MaterialStatManifest, components: &[Item]) -> f32 { - self.stats.resolve_stats(msm, components).range - } + pub fn base_buff_strength(&self) -> f32 { self.stats.buff_strength } - pub fn base_energy_efficiency(&self, msm: &MaterialStatManifest, components: &[Item]) -> f32 { - self.stats.resolve_stats(msm, components).energy_efficiency - } - - pub fn base_buff_strength(&self, msm: &MaterialStatManifest, components: &[Item]) -> f32 { - self.stats.resolve_stats(msm, components).buff_strength - } - - pub fn equip_time(&self, msm: &MaterialStatManifest, components: &[Item]) -> Duration { - Duration::from_secs_f32(self.stats.resolve_stats(msm, components).equip_time_secs) - } - - pub fn can_block(&self) -> bool { - matches!( - self.kind, - ToolKind::Sword - | ToolKind::Axe - | ToolKind::Hammer - | ToolKind::Shield - | ToolKind::Dagger - ) - } + pub fn equip_time(&self) -> Duration { Duration::from_secs_f32(self.stats.equip_time_secs) } } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -387,16 +289,10 @@ pub struct AbilitySet { impl AbilitySet { #[must_use] - pub fn modified_by_tool( - self, - tool: &Tool, - msm: &MaterialStatManifest, - components: &[Item], - ) -> Self { - let stats = Stats::from((msm, components, tool)); + pub fn modified_by_tool(self, tool: &Tool) -> Self { self.map(|a| AbilityItem { id: a.id, - ability: a.ability.adjusted_by_stats(stats), + ability: a.ability.adjusted_by_stats(tool.stats), }) } } diff --git a/common/src/comp/inventory/loadout.rs b/common/src/comp/inventory/loadout.rs index ecbfd78b4c..45fb848c16 100644 --- a/common/src/comp/inventory/loadout.rs +++ b/common/src/comp/inventory/loadout.rs @@ -1,6 +1,6 @@ use crate::comp::{ inventory::{ - item::{Hands, ItemKind}, + item::{tool::Tool, Hands, ItemKind}, slot::{ArmorSlot, EquipSlot}, InvSlot, }, @@ -168,9 +168,13 @@ impl Loadout { assert_eq!(self.swap(equip_slot_a, item_b), None); // Check if items are valid in their new positions - if !self.slot_can_hold(equip_slot_a, self.equipped(equip_slot_a)) - || !self.slot_can_hold(equip_slot_b, self.equipped(equip_slot_b)) - { + if !self.slot_can_hold( + equip_slot_a, + self.equipped(equip_slot_a).map(|x| x.kind()).as_deref(), + ) || !self.slot_can_hold( + equip_slot_b, + self.equipped(equip_slot_b).map(|x| x.kind()).as_deref(), + ) { // If not, revert the swap let item_a = self.swap(equip_slot_a, None); let item_b = self.swap(equip_slot_b, item_a); @@ -183,11 +187,11 @@ impl Loadout { /// returned, or if there are no free slots then the first occupied slot /// will be returned. The bool part of the tuple indicates whether an item /// is already equipped in the slot. - pub(super) fn get_slot_to_equip_into(&self, item: &Item) -> Option { + pub(super) fn get_slot_to_equip_into(&self, item_kind: &ItemKind) -> Option { let mut suitable_slots = self .slots .iter() - .filter(|s| self.slot_can_hold(s.equip_slot, Some(item))); + .filter(|s| self.slot_can_hold(s.equip_slot, Some(item_kind))); let first = suitable_slots.next(); @@ -199,14 +203,15 @@ impl Loadout { .or_else(|| first.map(|x| x.equip_slot)) } - /// Returns all items currently equipped that an item could replace - pub(super) fn equipped_items_replaceable_by<'a>( + /// Returns all items currently equipped that an item of the given ItemKind + /// could replace + pub(super) fn equipped_items_of_kind<'a>( &'a self, - item: &'a Item, + item_kind: &'a ItemKind, ) -> impl Iterator { self.slots .iter() - .filter(move |s| self.slot_can_hold(s.equip_slot, Some(item))) + .filter(move |s| self.slot_can_hold(s.equip_slot, Some(item_kind))) .filter_map(|s| s.slot.as_ref()) } @@ -288,7 +293,7 @@ impl Loadout { let loadout_slot = self .slots .iter() - .find(|s| s.slot.is_none() && self.slot_can_hold(s.equip_slot, Some(&item))) + .find(|s| s.slot.is_none() && self.slot_can_hold(s.equip_slot, Some(&*item.kind()))) .map(|s| s.equip_slot); if let Some(slot) = self .slots @@ -307,55 +312,53 @@ impl Loadout { } /// Checks that a slot can hold a given item - pub(super) fn slot_can_hold(&self, equip_slot: EquipSlot, item: Option<&Item>) -> bool { + pub(super) fn slot_can_hold( + &self, + equip_slot: EquipSlot, + item_kind: Option<&ItemKind>, + ) -> bool { // Disallow equipping incompatible weapon pairs (i.e a two-handed weapon and a // one-handed weapon) if !(match equip_slot { - EquipSlot::ActiveMainhand => { - Loadout::is_valid_weapon_pair(item, self.equipped(EquipSlot::ActiveOffhand)) - }, - EquipSlot::ActiveOffhand => { - Loadout::is_valid_weapon_pair(self.equipped(EquipSlot::ActiveMainhand), item) - }, - EquipSlot::InactiveMainhand => { - Loadout::is_valid_weapon_pair(item, self.equipped(EquipSlot::InactiveOffhand)) - }, - EquipSlot::InactiveOffhand => { - Loadout::is_valid_weapon_pair(self.equipped(EquipSlot::InactiveMainhand), item) - }, + EquipSlot::ActiveMainhand => Loadout::is_valid_weapon_pair( + item_kind, + self.equipped(EquipSlot::ActiveOffhand) + .map(|x| x.kind()) + .as_deref(), + ), + EquipSlot::ActiveOffhand => Loadout::is_valid_weapon_pair( + self.equipped(EquipSlot::ActiveMainhand) + .map(|x| x.kind()) + .as_deref(), + item_kind, + ), + EquipSlot::InactiveMainhand => Loadout::is_valid_weapon_pair( + item_kind, + self.equipped(EquipSlot::InactiveOffhand) + .map(|x| x.kind()) + .as_deref(), + ), + EquipSlot::InactiveOffhand => Loadout::is_valid_weapon_pair( + self.equipped(EquipSlot::InactiveMainhand) + .map(|x| x.kind()) + .as_deref(), + item_kind, + ), _ => true, }) { return false; } - item.map_or(true, |item| equip_slot.can_hold(item)) + item_kind.map_or(true, |item| equip_slot.can_hold(item)) } - fn is_valid_weapon_pair(main_hand: Option<&Item>, off_hand: Option<&Item>) -> bool { - // Checks that a valid weapon pair is equipped, returns true if... - match ( - main_hand.map(|i| (i.kind(), i.components())), - off_hand.map(|i| (i.kind(), i.components())), - ) { - // A weapon is being equipped in the mainhand, but not in the offhand - (Some((ItemKind::Tool(_), _)), None) => true, - // A weapon is being equipped in both slots, and both weapons are 1 handed - ( - Some((ItemKind::Tool(tool_1), components_1)), - Some((ItemKind::Tool(tool_2), components_2)), - ) => { - matches!( - ( - tool_1.hands.resolve_hands(components_1), - tool_2.hands.resolve_hands(components_2) - ), - (Hands::One, Hands::One) - ) - }, - // A weapon is being unequipped that will result in both slots being empty - (None, None) => true, - _ => false, - } + #[rustfmt::skip] + fn is_valid_weapon_pair(main_hand: Option<&ItemKind>, off_hand: Option<&ItemKind>) -> bool { + matches!((main_hand, off_hand), + (Some(ItemKind::Tool(Tool { hands: Hands::One, .. })), None) | + (Some(ItemKind::Tool(Tool { hands: Hands::Two, .. })), None) | + (Some(ItemKind::Tool(Tool { hands: Hands::One, .. })), Some(ItemKind::Tool(Tool { hands: Hands::One, .. }))) | + (None, None)) } pub(super) fn swap_equipped_weapons(&mut self) { @@ -363,7 +366,7 @@ impl Loadout { // nothing is equipped in slot let valid_slot = |equip_slot| { self.equipped(equip_slot) - .map_or(true, |i| self.slot_can_hold(equip_slot, Some(i))) + .map_or(true, |i| self.slot_can_hold(equip_slot, Some(&*i.kind()))) }; // If every weapon is currently in a valid slot, after this change they will @@ -456,12 +459,10 @@ mod tests { loadout.swap(EquipSlot::Armor(ArmorSlot::Bag1), Some(get_test_bag(1))); let result = loadout - .get_slot_to_equip_into(&Item::create_test_item_from_kind(ItemKind::Armor( - Armor::test_armor( - ArmorKind::Bag("test".to_string()), - Protection::Normal(0.0), - Protection::Normal(0.0), - ), + .get_slot_to_equip_into(&ItemKind::Armor(Armor::test_armor( + ArmorKind::Bag("test".to_string()), + Protection::Normal(0.0), + Protection::Normal(0.0), ))) .unwrap(); @@ -478,12 +479,10 @@ mod tests { loadout.swap(EquipSlot::Armor(ArmorSlot::Bag4), Some(get_test_bag(1))); let result = loadout - .get_slot_to_equip_into(&Item::create_test_item_from_kind(ItemKind::Armor( - Armor::test_armor( - ArmorKind::Bag("test".to_string()), - Protection::Normal(0.0), - Protection::Normal(0.0), - ), + .get_slot_to_equip_into(&ItemKind::Armor(Armor::test_armor( + ArmorKind::Bag("test".to_string()), + Protection::Normal(0.0), + Protection::Normal(0.0), ))) .unwrap(); diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index b3f3179f86..11c32b6c2b 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -1073,7 +1073,10 @@ impl LoadoutBuilder { #[must_use = "Method consumes builder and returns updated builder."] fn with_equipment(mut self, equip_slot: EquipSlot, item: Option) -> Self { // Panic if item doesn't correspond to slot - assert!(item.as_ref().map_or(true, |item| equip_slot.can_hold(item))); + assert!( + item.as_ref() + .map_or(true, |item| equip_slot.can_hold(&*item.kind())) + ); self.0.swap(equip_slot, item); self diff --git a/common/src/comp/inventory/mod.rs b/common/src/comp/inventory/mod.rs index f0ddeb3da2..0a2afe2ccf 100644 --- a/common/src/comp/inventory/mod.rs +++ b/common/src/comp/inventory/mod.rs @@ -2,14 +2,14 @@ use core::ops::Not; use serde::{Deserialize, Serialize}; use specs::{Component, DerefFlaggedStorage}; use specs_idvs::IdvStorage; -use std::{borrow::Cow, convert::TryFrom, mem, ops::Range}; +use std::{convert::TryFrom, mem, ops::Range}; use tracing::{debug, trace, warn}; use vek::Vec3; use crate::{ comp::{ inventory::{ - item::{tool::AbilityMap, ItemDef, MaterialStatManifest, TagExampleInfo}, + item::{tool::AbilityMap, ItemDef, ItemKind, MaterialStatManifest, TagExampleInfo}, loadout::Loadout, slot::{EquipSlot, Slot, SlotError}, }, @@ -128,8 +128,8 @@ impl Inventory { // Quality is sorted in reverse since we want high quality items first InventorySortOrder::Quality => Ord::cmp(&b.quality(), &a.quality()), InventorySortOrder::Tag => Ord::cmp( - &a.tags.first().map_or(Cow::Borrowed(""), |tag| tag.name()), - &b.tags.first().map_or(Cow::Borrowed(""), |tag| tag.name()), + &a.tags().first().map_or("", |tag| tag.name()), + &b.tags().first().map_or("", |tag| tag.name()), ), }); @@ -540,7 +540,7 @@ impl Inventory { #[must_use = "Returned items will be lost if not used"] pub fn equip(&mut self, inv_slot: InvSlotId) -> Vec { self.get(inv_slot) - .and_then(|item| self.loadout.get_slot_to_equip_into(item)) + .and_then(|item| self.loadout.get_slot_to_equip_into(&*item.kind())) .map(|equip_slot| self.swap_inventory_loadout(inv_slot, equip_slot)) .unwrap_or_else(Vec::new) } @@ -551,7 +551,7 @@ impl Inventory { pub fn free_after_equip(&self, inv_slot: InvSlotId) -> i32 { let (inv_slot_for_equipped, slots_from_equipped) = self .get(inv_slot) - .and_then(|item| self.loadout.get_slot_to_equip_into(item)) + .and_then(|item| self.loadout.get_slot_to_equip_into(&*item.kind())) .and_then(|equip_slot| self.equipped(equip_slot)) .map_or((1, 0), |item| (0, item.slots().len())); @@ -758,7 +758,7 @@ impl Inventory { pub fn can_swap(&self, inv_slot_id: InvSlotId, equip_slot: EquipSlot) -> bool { // Check if loadout slot can hold item if !self.get(inv_slot_id).map_or(true, |item| { - self.loadout.slot_can_hold(equip_slot, Some(item)) + self.loadout.slot_can_hold(equip_slot, Some(&*item.kind())) }) { trace!("can_swap = false, equip slot can't hold item"); return false; @@ -775,11 +775,11 @@ impl Inventory { true } - pub fn equipped_items_replaceable_by<'a>( + pub fn equipped_items_of_kind<'a>( &'a self, - item: &'a Item, + item_kind: &'a ItemKind, ) -> impl Iterator { - self.loadout.equipped_items_replaceable_by(item) + self.loadout.equipped_items_of_kind(item_kind) } pub fn swap_equipped_weapons(&mut self) { self.loadout.swap_equipped_weapons() } diff --git a/common/src/comp/inventory/slot.rs b/common/src/comp/inventory/slot.rs index 9cb408a23a..48f2789264 100644 --- a/common/src/comp/inventory/slot.rs +++ b/common/src/comp/inventory/slot.rs @@ -110,28 +110,22 @@ pub enum ArmorSlot { } impl Slot { - pub fn can_hold(self, item: &item::Item) -> bool { - match (self, item) { + pub fn can_hold(self, item_kind: &item::ItemKind) -> bool { + match (self, item_kind) { (Self::Inventory(_), _) => true, - (Self::Equip(slot), item) => slot.can_hold(item), + (Self::Equip(slot), item_kind) => slot.can_hold(item_kind), } } } impl EquipSlot { - pub fn can_hold(self, item: &item::Item) -> bool { - match (self, item.kind()) { + pub fn can_hold(self, item_kind: &item::ItemKind) -> bool { + match (self, item_kind) { (Self::Armor(slot), ItemKind::Armor(armor::Armor { kind, .. })) => slot.can_hold(kind), (Self::ActiveMainhand, ItemKind::Tool(_)) => true, - (Self::ActiveOffhand, ItemKind::Tool(tool)) => matches!( - tool.hands.resolve_hands(item.components()), - tool::Hands::One - ), + (Self::ActiveOffhand, ItemKind::Tool(tool)) => matches!(tool.hands, tool::Hands::One), (Self::InactiveMainhand, ItemKind::Tool(_)) => true, - (Self::InactiveOffhand, ItemKind::Tool(tool)) => matches!( - tool.hands.resolve_hands(item.components()), - tool::Hands::One - ), + (Self::InactiveOffhand, ItemKind::Tool(tool)) => matches!(tool.hands, tool::Hands::One), (Self::Lantern, ItemKind::Lantern(_)) => true, (Self::Glider, ItemKind::Glider(_)) => true, _ => false, diff --git a/common/src/comp/poise.rs b/common/src/comp/poise.rs index 3cd52e052a..dcc20e7416 100644 --- a/common/src/comp/poise.rs +++ b/common/src/comp/poise.rs @@ -230,7 +230,7 @@ impl Poise { let protection = inventory .equipped_items() .filter_map(|item| { - if let ItemKind::Armor(armor) = &item.kind() { + if let ItemKind::Armor(armor) = &*item.kind() { armor.poise_resilience() } else { None diff --git a/common/src/recipe.rs b/common/src/recipe.rs index 96fc2bd5a0..670c46fbab 100644 --- a/common/src/recipe.rs +++ b/common/src/recipe.rs @@ -2,7 +2,9 @@ use crate::{ assets::{self, AssetExt, AssetHandle}, comp::{ inventory::slot::InvSlotId, - item::{modular, tool::AbilityMap, ItemDef, ItemKind, ItemTag, MaterialStatManifest}, + item::{ + modular, tool::AbilityMap, ItemBase, ItemDef, ItemKind, ItemTag, MaterialStatManifest, + }, Inventory, Item, }, terrain::SpriteKind, @@ -123,8 +125,12 @@ impl Recipe { } let (item_def, quantity) = &self.output; - let mut crafted_item = - Item::new_from_item_def(Arc::clone(item_def), &[], ability_map, msm); + let mut crafted_item = Item::new_from_item_base( + ItemBase::Raw(Arc::clone(item_def)), + &[], + ability_map, + msm, + ); for component in components { crafted_item.add_component(component, ability_map, msm); } @@ -244,19 +250,22 @@ pub fn modular_weapon( ) -> Result { use modular::{ModularComponent, ModularComponentKind}; // Closure to get inner modular component info from item in a given slot - let unwrap_modular = |slot| -> Option<&ModularComponent> { - if let Some(ItemKind::ModularComponent(mod_comp)) = inv.get(slot).map(|item| &item.kind) { - Some(mod_comp) + fn unwrap_modular(inv: &Inventory, slot: InvSlotId) -> Option { + if let Some(ItemKind::ModularComponent(mod_comp)) = + inv.get(slot).map(|item| item.kind()).as_deref() + { + // TODO: Remove + Some(mod_comp.clone()) } else { None } - }; + } // Checks if both components are comptabile, and if so returns the toolkind to // make weapon of let compatiblity = if let (Some(damage_component), Some(held_component)) = ( - unwrap_modular(damage_component), - unwrap_modular(held_component), + unwrap_modular(inv, damage_component), + unwrap_modular(inv, held_component), ) { // Checks that damage and held component slots each contain a damage and held // modular component respectively @@ -296,14 +305,14 @@ pub fn modular_weapon( .take(held_component, ability_map, msm) .expect("Expected component to exist"); - // Initialize modular weapon - let mut modular_weapon = modular::initialize_modular_weapon(tool_kind); - - // Insert components into modular weapon item - modular_weapon.add_component(damage_component, ability_map, msm); - modular_weapon.add_component(held_component, ability_map, msm); - - Ok(modular_weapon) + // Create modular weapon + let components = vec![damage_component, held_component]; + Ok(Item::new_from_item_base( + ItemBase::Modular(modular::ModularBase::Tool(tool_kind)), + &components, + ability_map, + msm, + )) }, Err(err) => Err(err), } diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 09ffe2dcaf..d26339c023 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -5,7 +5,7 @@ use crate::{ arthropod, biped_large, biped_small, character_state::OutputEvents, inventory::slot::{EquipSlot, Slot}, - item::{Hands, Item, ItemKind, Tool, ToolKind}, + item::{Hands, ItemKind, ToolKind}, quadruped_low, quadruped_medium, quadruped_small, skills::{Skill, SwimSkill, SKILL_MODIFIERS}, theropod, Body, CharacterAbility, CharacterState, Density, InputAttr, InputKind, @@ -581,11 +581,10 @@ pub fn attempt_wield(data: &JoinData<'_>, update: &mut StateUpdate) { let equip_time = |equip_slot| { data.inventory .and_then(|inv| inv.equipped(equip_slot)) - .and_then(|item| match item.kind() { - ItemKind::Tool(tool) => Some((item, tool)), + .and_then(|item| match &*item.kind() { + ItemKind::Tool(tool) => Some(tool.equip_time()), _ => None, }) - .map(|(item, tool)| tool.equip_time(data.msm, item.components())) }; // Calculates time required to equip weapons, if weapon in mainhand and offhand, @@ -704,7 +703,7 @@ pub fn handle_manipulate_loadout( if let Some((item_kind, item)) = data .inventory .and_then(|inv| inv.get(inv_slot)) - .and_then(|item| Option::::from(item.kind()).zip(Some(item))) + .and_then(|item| Option::::from(&*item.kind()).zip(Some(item))) { let (buildup_duration, use_duration, recover_duration) = item_kind.durations(); // If item returns a valid kind for item use, do into use item character state @@ -948,7 +947,7 @@ pub fn attempt_input( /// Checks that player can block, then attempts to block pub fn handle_block_input(data: &JoinData<'_>, update: &mut StateUpdate) { - let can_block = |equip_slot| matches!(unwrap_tool_data(data, equip_slot), Some((tool, _)) if tool.can_block()); + let can_block = |equip_slot| matches!(unwrap_tool_data(data, equip_slot), Some((kind, _)) if kind.can_block()); let hands = get_hands(data); if input_is_pressed(data, InputKind::Block) && (can_block(EquipSlot::ActiveMainhand) @@ -1000,16 +999,14 @@ pub fn is_strafing(data: &JoinData<'_>, update: &StateUpdate) -> bool { } /// Returns tool and components -pub fn unwrap_tool_data<'a>( - data: &'a JoinData, - equip_slot: EquipSlot, -) -> Option<(&'a Tool, &'a [Item])> { - if let Some((ItemKind::Tool(tool), components)) = data +pub fn unwrap_tool_data(data: &JoinData, equip_slot: EquipSlot) -> Option<(ToolKind, Hands)> { + if let Some(ItemKind::Tool(tool)) = data .inventory .and_then(|inv| inv.equipped(equip_slot)) - .map(|i| (i.kind(), i.components())) + .map(|i| i.kind()) + .as_deref() { - Some((tool, components)) + Some((tool.kind, tool.hands)) } else { None } @@ -1017,12 +1014,13 @@ pub fn unwrap_tool_data<'a>( pub fn get_hands(data: &JoinData<'_>) -> (Option, Option) { let hand = |slot| { - if let Some((ItemKind::Tool(tool), components)) = data + if let Some(ItemKind::Tool(tool)) = data .inventory .and_then(|inv| inv.equipped(slot)) - .map(|i| (i.kind(), i.components())) + .map(|i| i.kind()) + .as_deref() { - Some(tool.hands.resolve_hands(components)) + Some(tool.hands) } else { None } @@ -1046,8 +1044,8 @@ pub fn get_crit_data(data: &JoinData<'_>, ai: AbilityInfo) -> (f32, f32) { }) .and_then(|slot| data.inventory.and_then(|inv| inv.equipped(slot))) .and_then(|item| { - if let ItemKind::Tool(tool) = item.kind() { - Some(tool.base_crit_chance(data.msm, item.components())) + if let ItemKind::Tool(tool) = &*item.kind() { + Some(tool.base_crit_chance()) } else { None } @@ -1068,8 +1066,8 @@ pub fn get_buff_strength(data: &JoinData<'_>, ai: AbilityInfo) -> f32 { }) .and_then(|slot| data.inventory.and_then(|inv| inv.equipped(slot))) .and_then(|item| { - if let ItemKind::Tool(tool) = item.kind() { - Some(tool.base_buff_strength(data.msm, item.components())) + if let ItemKind::Tool(tool) = &*item.kind() { + Some(tool.base_buff_strength()) } else { None } @@ -1178,10 +1176,12 @@ impl AbilityInfo { } else { unwrap_tool_data(data, EquipSlot::ActiveMainhand) }; - let (tool, hand) = ( - tool_data.map(|(t, _)| t.kind), - tool_data.map(|(t, components)| HandInfo::from_main_tool(t, components, from_offhand)), - ); + let (tool, hand) = tool_data.map_or((None, None), |(kind, hands)| { + ( + Some(kind), + Some(HandInfo::from_main_tool(hands, from_offhand)), + ) + }); Self { tool, @@ -1200,8 +1200,8 @@ pub enum HandInfo { } impl HandInfo { - pub fn from_main_tool(tool: &Tool, components: &[Item], from_offhand: bool) -> Self { - match tool.hands.resolve_hands(components) { + pub fn from_main_tool(tool_hands: Hands, from_offhand: bool) -> Self { + match tool_hands { Hands::Two => Self::TwoHanded, Hands::One => { if from_offhand { diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 8055c25a8d..29499abff7 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -1111,7 +1111,7 @@ fn handle_exp_gain( let mut add_tool_from_slot = |equip_slot| { let tool_kind = inventory .equipped(equip_slot) - .and_then(|i| match &i.kind() { + .and_then(|i| match &*i.kind() { ItemKind::Tool(tool) if tool.kind.gains_combat_xp() => Some(tool.kind), _ => None, }); diff --git a/server/src/events/interaction.rs b/server/src/events/interaction.rs index 29f3dac6eb..3a364999ff 100644 --- a/server/src/events/interaction.rs +++ b/server/src/events/interaction.rs @@ -51,22 +51,22 @@ pub fn handle_lantern(server: &mut Server, entity: EcsEntity, enable: bool) { .map_or(true, |h| !h.is_dead) { let inventory_storage = ecs.read_storage::(); - let lantern_opt = inventory_storage + let lantern_info = inventory_storage .get(entity) .and_then(|inventory| inventory.equipped(EquipSlot::Lantern)) .and_then(|item| { - if let comp::item::ItemKind::Lantern(l) = item.kind() { - Some(l) + if let comp::item::ItemKind::Lantern(l) = &*item.kind() { + Some((l.color(), l.strength())) } else { None } }); - if let Some(lantern) = lantern_opt { + if let Some((col, strength)) = lantern_info { let _ = ecs.write_storage::() .insert(entity, comp::LightEmitter { - col: lantern.color(), - strength: lantern.strength(), + col, + strength, flicker: 0.35, animated: true, }); diff --git a/server/src/events/inventory_manip.rs b/server/src/events/inventory_manip.rs index f473c350ae..91fb7c590f 100644 --- a/server/src/events/inventory_manip.rs +++ b/server/src/events/inventory_manip.rs @@ -32,11 +32,11 @@ use common_net::msg::ServerGeneral; pub fn swap_lantern( storage: &mut WriteStorage, entity: EcsEntity, - lantern: &item::Lantern, + (lantern_color, lantern_strength): (Rgb, f32), ) { if let Some(mut light) = storage.get_mut(entity) { - light.strength = lantern.strength(); - light.col = lantern.color(); + light.strength = lantern_strength; + light.col = lantern_color; } } @@ -264,16 +264,19 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv Slot::Inventory(slot) => { use item::ItemKind; - let (is_equippable, lantern_opt) = - inventory.get(slot).map_or((false, None), |i| { - (i.kind().is_equippable(), match i.kind() { - ItemKind::Lantern(lantern) => Some(lantern), + let is_equippable = inventory + .get(slot) + .map_or(false, |i| i.kind().is_equippable()); + if is_equippable { + if let Some(lantern_info) = + inventory.get(slot).and_then(|i| match &*i.kind() { + ItemKind::Lantern(lantern) => { + Some((lantern.color(), lantern.strength())) + }, _ => None, }) - }); - if is_equippable { - if let Some(lantern) = lantern_opt { - swap_lantern(&mut state.ecs().write_storage(), entity, lantern); + { + swap_lantern(&mut state.ecs().write_storage(), entity, lantern_info); } if let Some(pos) = state.ecs().read_storage::().get(entity) { dropped_items.extend(inventory.equip(slot).into_iter().map(|x| { @@ -292,7 +295,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv &state.ecs().read_resource::(), &state.ecs().read_resource::(), ) { - match item.kind() { + match &*item.kind() { ItemKind::Consumable { effects, .. } => { maybe_effect = Some(effects.clone()); Some(comp::InventoryUpdateEvent::Consumed( diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index 88f7a687b8..e9e79b2105 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -167,7 +167,7 @@ impl<'a> System<'a> for Sys { .equipped(EquipSlot::Glider) .as_ref() .map_or(false, |item| { - matches!(item.kind(), comp::item::ItemKind::Glider(_)) + matches!(&*item.kind(), comp::item::ItemKind::Glider(_)) }); let is_gliding = matches!( @@ -701,7 +701,7 @@ impl<'a> AgentData<'a> { .equipped(EquipSlot::Lantern) .as_ref() .map_or(false, |item| { - matches!(item.kind(), comp::item::ItemKind::Lantern(_)) + matches!(&*item.kind(), comp::item::ItemKind::Lantern(_)) }); let lantern_turned_on = self.light_emitter.is_some(); let day_period = DayPeriod::from(read_data.time_of_day.0); @@ -1496,7 +1496,7 @@ impl<'a> AgentData<'a> { let healing_value = |item: &Item| { let mut value = 0.0; - if let ItemKind::Consumable { kind, effects, .. } = &item.kind { + if let ItemKind::Consumable { kind, effects, .. } = &*item.kind() { if matches!(kind, ConsumableKind::Drink) || (relaxed && matches!(kind, ConsumableKind::Food)) { @@ -1639,7 +1639,7 @@ impl<'a> AgentData<'a> { .as_ref() .map(|item| { if let Some(ability_spec) = item.ability_spec() { - match ability_spec { + match &*ability_spec { AbilitySpec::Custom(spec) => match spec.as_str() { "Oni" | "Sword Simple" => Tactic::Sword, "Staff Simple" => Tactic::Staff, @@ -1697,7 +1697,7 @@ impl<'a> AgentData<'a> { }, AbilitySpec::Tool(tool_kind) => tool_tactic(*tool_kind), } - } else if let ItemKind::Tool(tool) = &item.kind() { + } else if let ItemKind::Tool(tool) = &*item.kind() { tool_tactic(tool.kind) } else { Tactic::SimpleMelee diff --git a/voxygen/src/audio/sfx/event_mapper/combat/mod.rs b/voxygen/src/audio/sfx/event_mapper/combat/mod.rs index 9014010c56..24f5e5b9b9 100644 --- a/voxygen/src/audio/sfx/event_mapper/combat/mod.rs +++ b/voxygen/src/audio/sfx/event_mapper/combat/mod.rs @@ -146,7 +146,7 @@ impl CombatEventMapper { inventory: &Inventory, ) -> SfxEvent { if let Some(item) = inventory.equipped(EquipSlot::ActiveMainhand) { - if let ItemKind::Tool(data) = item.kind() { + if let ItemKind::Tool(data) = &*item.kind() { if character_state.is_attack() { return SfxEvent::Attack( CharacterAbilityType::from(character_state), diff --git a/voxygen/src/audio/sfx/mod.rs b/voxygen/src/audio/sfx/mod.rs index d58d0d8852..2685da7f2a 100644 --- a/voxygen/src/audio/sfx/mod.rs +++ b/voxygen/src/audio/sfx/mod.rs @@ -302,7 +302,7 @@ impl From<&InventoryUpdateEvent> for SfxEvent { InventoryUpdateEvent::Collected(item) => { // Handle sound effects for types of collected items, falling // back to the default Collected event - match &item.kind() { + match &*item.kind() { ItemKind::Tool(tool) => { SfxEvent::Inventory(SfxInventoryEvent::CollectedTool(tool.kind)) }, diff --git a/voxygen/src/hud/crafting.rs b/voxygen/src/hud/crafting.rs index 830b7a35ca..48db0e2da6 100644 --- a/voxygen/src/hud/crafting.rs +++ b/voxygen/src/hud/crafting.rs @@ -192,11 +192,11 @@ impl CraftingTab { match self { CraftingTab::All | CraftingTab::Dismantle => true, CraftingTab::Food => item.tags().contains(&ItemTag::Food), - CraftingTab::Armor => match item.kind() { + CraftingTab::Armor => match &*item.kind() { ItemKind::Armor(_) => !item.tags().contains(&ItemTag::Bag), _ => false, }, - CraftingTab::Glider => matches!(item.kind(), ItemKind::Glider(_)), + CraftingTab::Glider => matches!(&*item.kind(), ItemKind::Glider(_)), CraftingTab::Potion => item.tags().contains(&ItemTag::Potion), CraftingTab::ProcessedMaterial => item.tags().iter().any(|tag| { matches!( @@ -207,7 +207,7 @@ impl CraftingTab { CraftingTab::Bag => item.tags().contains(&ItemTag::Bag), CraftingTab::Tool => item.tags().contains(&ItemTag::CraftingTool), CraftingTab::Utility => item.tags().contains(&ItemTag::Utility), - CraftingTab::Weapon => match item.kind() { + CraftingTab::Weapon => match &*item.kind() { ItemKind::Tool(_) => !item.tags().contains(&ItemTag::CraftingTool), _ => false, }, @@ -468,8 +468,8 @@ impl<'a> Widget for Crafting<'a> { SearchFilter::Input => recipe.inputs().any(|(input, _, _)| { let input_name = match input { RecipeInput::Item(def) => def.name(), - RecipeInput::Tag(tag) => tag.name(), - RecipeInput::TagSameItem(tag, _) => tag.name(), + RecipeInput::Tag(tag) => Cow::Borrowed(tag.name()), + RecipeInput::TagSameItem(tag, _) => Cow::Borrowed(tag.name()), // Works, but probably will have some...interesting false positives // Code reviewers probably required to do magic to make not hacky RecipeInput::ListSameItem(defs, _) => { @@ -916,8 +916,7 @@ impl<'a> Widget for Crafting<'a> { RecipeInput::Item(item_def) => Arc::clone(item_def), RecipeInput::Tag(tag) | RecipeInput::TagSameItem(tag, _) => { Arc::::load_expect_cloned( - self - .inventory + self.inventory .slots() .find_map(|slot| { slot.as_ref().and_then(|item| { @@ -928,12 +927,11 @@ impl<'a> Widget for Crafting<'a> { } }) }) - .unwrap_or(&tag.exemplar_identifier()), + .unwrap_or_else(|| tag.exemplar_identifier()), ) }, RecipeInput::ListSameItem(item_defs, _) => Arc::::load_expect_cloned( - self - .inventory + self.inventory .slots() .find_map(|slot| { slot.as_ref().and_then(|item| { diff --git a/voxygen/src/hud/util.rs b/voxygen/src/hud/util.rs index 3b2dd69ea6..d19e71ce54 100644 --- a/voxygen/src/hud/util.rs +++ b/voxygen/src/hud/util.rs @@ -4,7 +4,7 @@ use common::{ inventory::trade_pricing::TradePricing, item::{ armor::{Armor, ArmorKind, Protection}, - tool::{Hands, StatKind, Stats, Tool, ToolKind}, + tool::{Hands, Stats, Tool, ToolKind}, Item, ItemDesc, ItemKind, MaterialKind, MaterialStatManifest, ModularComponent, }, BuffKind, @@ -71,12 +71,12 @@ pub fn price_desc( } pub fn kind_text<'a>(item: &dyn ItemDesc, i18n: &'a Localization) -> Cow<'a, str> { - match item.kind() { + match &*item.kind() { ItemKind::Armor(armor) => Cow::Borrowed(armor_kind(armor, i18n)), ItemKind::Tool(tool) => Cow::Owned(format!( "{} ({})", tool_kind(tool, i18n), - tool_hands(tool, item.components(), i18n) + tool_hands(tool, i18n) )), ItemKind::ModularComponent(_mc) => Cow::Borrowed(i18n.get("common.bag.shoulders")), ItemKind::Glider(_glider) => Cow::Borrowed(i18n.get("common.kind.glider")), @@ -106,7 +106,7 @@ pub fn modular_component_desc( msm: &MaterialStatManifest, description: &str, ) -> String { - let stats = StatKind::Direct(mc.stats).resolve_stats(msm, components); + let stats = mc.stats; let statblock = statblock_desc(&stats); let mut result = format!("Modular Component\n\n{}\n\n{}", statblock, description); if !components.is_empty() { @@ -121,7 +121,7 @@ pub fn modular_component_desc( } pub fn stats_count(item: &dyn ItemDesc) -> usize { - let mut count = match item.kind() { + let mut count = match &*item.kind() { ItemKind::Armor(armor) => { if matches!(armor.kind, ArmorKind::Bag(_)) { 0 @@ -139,7 +139,7 @@ pub fn stats_count(item: &dyn ItemDesc) -> usize { _ => 0, }; - let is_bag = match item.kind() { + let is_bag = match &*item.kind() { ItemKind::Armor(armor) => matches!(armor.kind, ArmorKind::Bag(_)), _ => false, }; @@ -274,8 +274,8 @@ fn tool_kind<'a>(tool: &Tool, i18n: &'a Localization) -> &'a str { } /// Output the number of hands needed to hold a tool -pub fn tool_hands<'a>(tool: &Tool, components: &[Item], i18n: &'a Localization) -> &'a str { - let hands = match tool.hands.resolve_hands(components) { +pub fn tool_hands<'a>(tool: &Tool, i18n: &'a Localization) -> &'a str { + let hands = match tool.hands { Hands::One => i18n.get("common.hands.one"), Hands::Two => i18n.get("common.hands.two"), }; diff --git a/voxygen/src/scene/figure/cache.rs b/voxygen/src/scene/figure/cache.rs index 0fb817ca33..df1670fd33 100644 --- a/voxygen/src/scene/figure/cache.rs +++ b/voxygen/src/scene/figure/cache.rs @@ -152,6 +152,7 @@ impl CharacterCacheKey { })) = inventory .equipped(EquipSlot::Armor(ArmorSlot::Head)) .map(|i| i.kind()) + .as_deref() { Some(armor.clone()) } else { @@ -163,6 +164,7 @@ impl CharacterCacheKey { })) = inventory .equipped(EquipSlot::Armor(ArmorSlot::Shoulders)) .map(|i| i.kind()) + .as_deref() { Some(armor.clone()) } else { @@ -174,6 +176,7 @@ impl CharacterCacheKey { })) = inventory .equipped(EquipSlot::Armor(ArmorSlot::Chest)) .map(|i| i.kind()) + .as_deref() { Some(armor.clone()) } else { @@ -185,6 +188,7 @@ impl CharacterCacheKey { })) = inventory .equipped(EquipSlot::Armor(ArmorSlot::Belt)) .map(|i| i.kind()) + .as_deref() { Some(armor.clone()) } else { @@ -196,6 +200,7 @@ impl CharacterCacheKey { })) = inventory .equipped(EquipSlot::Armor(ArmorSlot::Back)) .map(|i| i.kind()) + .as_deref() { Some(armor.clone()) } else { @@ -207,6 +212,7 @@ impl CharacterCacheKey { })) = inventory .equipped(EquipSlot::Armor(ArmorSlot::Legs)) .map(|i| i.kind()) + .as_deref() { Some(armor.clone()) } else { @@ -233,15 +239,19 @@ impl CharacterCacheKey { } else { None }, - lantern: if let Some(ItemKind::Lantern(lantern)) = - inventory.equipped(EquipSlot::Lantern).map(|i| i.kind()) + lantern: if let Some(ItemKind::Lantern(lantern)) = inventory + .equipped(EquipSlot::Lantern) + .map(|i| i.kind()) + .as_deref() { Some(lantern.kind.clone()) } else { None }, - glider: if let Some(ItemKind::Glider(glider)) = - inventory.equipped(EquipSlot::Glider).map(|i| i.kind()) + glider: if let Some(ItemKind::Glider(glider)) = inventory + .equipped(EquipSlot::Glider) + .map(|i| i.kind()) + .as_deref() { Some(glider.kind.clone()) } else { @@ -253,6 +263,7 @@ impl CharacterCacheKey { })) = inventory .equipped(EquipSlot::Armor(ArmorSlot::Hands)) .map(|i| i.kind()) + .as_deref() { Some(armor.clone()) } else { @@ -264,6 +275,7 @@ impl CharacterCacheKey { })) = inventory .equipped(EquipSlot::Armor(ArmorSlot::Feet)) .map(|i| i.kind()) + .as_deref() { Some(armor.clone()) } else { @@ -275,6 +287,7 @@ impl CharacterCacheKey { })) = inventory .equipped(EquipSlot::Armor(ArmorSlot::Head)) .map(|i| i.kind()) + .as_deref() { Some(armor.clone()) } else { diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index 6fa8496048..6ab216b478 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -873,12 +873,8 @@ impl FigureMgr { inventory .and_then(|i| i.equipped(equip_slot)) .map(|i| { - if let ItemKind::Tool(tool) = i.kind() { - ( - Some(tool.kind), - Some(tool.hands.resolve_hands(i.components())), - i.ability_spec(), - ) + if let ItemKind::Tool(tool) = &*i.kind() { + (Some(tool.kind), Some(tool.hands), i.ability_spec()) } else { (None, None, None) } @@ -888,8 +884,10 @@ impl FigureMgr { let (active_tool_kind, active_tool_hand, active_tool_spec) = tool_info(EquipSlot::ActiveMainhand); + let active_tool_spec = active_tool_spec.as_deref(); let (second_tool_kind, second_tool_hand, second_tool_spec) = tool_info(EquipSlot::ActiveOffhand); + let second_tool_spec = second_tool_spec.as_deref(); let hands = (active_tool_hand, second_tool_hand); diff --git a/voxygen/src/scene/simple.rs b/voxygen/src/scene/simple.rs index b939e21e88..4bda1f2d92 100644 --- a/voxygen/src/scene/simple.rs +++ b/voxygen/src/scene/simple.rs @@ -283,11 +283,8 @@ impl Scene { inventory .and_then(|inv| inv.equipped(equip_slot)) .and_then(|i| { - if let ItemKind::Tool(tool) = i.kind() { - Some(( - Some(tool.kind), - Some(tool.hands.resolve_hands(i.components())), - )) + if let ItemKind::Tool(tool) = &*i.kind() { + Some((Some(tool.kind), Some(tool.hands))) } else { None } diff --git a/voxygen/src/session/mod.rs b/voxygen/src/session/mod.rs index f345a9a665..accaea9b3a 100644 --- a/voxygen/src/session/mod.rs +++ b/voxygen/src/session/mod.rs @@ -410,8 +410,8 @@ impl PlayState for SessionState { .inventories() .get(player_entity) .and_then(|inv| inv.equipped(EquipSlot::ActiveMainhand)) - .and_then(|item| item.tool()) - .map_or(false, |tool| tool.kind == ToolKind::Pick) + .and_then(|item| item.tool_info()) + .map_or(false, |tool_kind| tool_kind == ToolKind::Pick) && client.is_wielding() == Some(true); // Check to see whether we're aiming at anything diff --git a/voxygen/src/ui/widgets/item_tooltip.rs b/voxygen/src/ui/widgets/item_tooltip.rs index f9b40bce42..84aa8161af 100644 --- a/voxygen/src/ui/widgets/item_tooltip.rs +++ b/voxygen/src/ui/widgets/item_tooltip.rs @@ -462,11 +462,9 @@ impl<'a> Widget for ItemTooltip<'a> { let quality = get_quality_col(item); - let equip_slot = item - .concrete_item() - .map(|item| inventory.equipped_items_replaceable_by(item)) - .into_iter() - .flatten(); + let item_kind = &*item.kind(); + + let equip_slot = inventory.equipped_items_of_kind(item_kind); let (title, desc) = (item.name().to_string(), item.description().to_string()); @@ -582,12 +580,12 @@ impl<'a> Widget for ItemTooltip<'a> { .set(state.ids.subtitle, ui); // Stats - match item.kind() { + match &*item.kind() { ItemKind::Tool(tool) => { - let power = tool.base_power(self.msm, item.components()) * 10.0; - let speed = tool.base_speed(self.msm, item.components()); - let effect_power = tool.base_effect_power(self.msm, item.components()) * 10.0; - let crit_chance = tool.base_crit_chance(self.msm, item.components()) * 100.0; + let power = tool.base_power() * 10.0; + let speed = tool.base_speed(); + let effect_power = tool.base_effect_power() * 10.0; + let crit_chance = tool.base_crit_chance() * 100.0; let combat_rating = combat::weapon_rating(&item, self.msm) * 10.0; // Combat Rating @@ -667,14 +665,8 @@ impl<'a> Widget for ItemTooltip<'a> { if let Some(equipped_item) = first_equipped { if let ItemKind::Tool(equipped_tool) = equipped_item.kind() { - let tool_stats = tool - .stats - .resolve_stats(self.msm, item.components()) - .clamp_speed(); - let equipped_tool_stats = equipped_tool - .stats - .resolve_stats(self.msm, equipped_item.components()) - .clamp_speed(); + let tool_stats = tool.stats; + let equipped_tool_stats = equipped_tool.stats; let diff = tool_stats - equipped_tool_stats; let power_diff = util::comparison(tool_stats.power, equipped_tool_stats.power); @@ -970,7 +962,7 @@ impl<'a> Widget for ItemTooltip<'a> { } if let Some(equipped_item) = first_equipped { - if let ItemKind::Armor(equipped_armor) = equipped_item.kind() { + if let ItemKind::Armor(equipped_armor) = &*equipped_item.kind() { let diff = armor.stats - equipped_armor.stats; let protection_diff = util::option_comparison( &armor.protection(),