diff --git a/assets/common/items/crafting_ing/tin_ingot.ron b/assets/common/items/crafting_ing/tin_ingot.ron index 8995168457..1d404b0317 100644 --- a/assets/common/items/crafting_ing/tin_ingot.ron +++ b/assets/common/items/crafting_ing/tin_ingot.ron @@ -5,5 +5,5 @@ ItemDef( kind: "TinIngot", ), quality: Common, - tags: [MetalIngot(0.25)], + tags: [MetalIngot], ) diff --git a/assets/common/recipe_book.ron b/assets/common/recipe_book.ron index 9eed04e03f..39bee54c22 100644 --- a/assets/common/recipe_book.ron +++ b/assets/common/recipe_book.ron @@ -363,11 +363,11 @@ (Item("common.items.crafting_tools.sewing_set"), 0), ], ), - "metal_blade": ( - ("common.items.crafting_ing.modular.damage.sword.metal_blade", 1), - [ - (Tag(MetalIngot), 5), - (Item("common.items.crafting_tools.craftsman_hammer"), 0), - ], - ), + //"metal_blade": ( + // ("common.items.crafting_ing.modular.damage.sword.metal_blade", 1), + // [ + // (Tag(MetalIngot), 5), + // (Item("common.items.crafting_tools.craftsman_hammer"), 0), + // ], + //), } diff --git a/common/src/comp/inventory/item/tool.rs b/common/src/comp/inventory/item/tool.rs index 80f928f28e..11dad4f3a9 100644 --- a/common/src/comp/inventory/item/tool.rs +++ b/common/src/comp/inventory/item/tool.rs @@ -72,6 +72,13 @@ impl Stats { speed: 0.0, } } + + pub fn clamp_speed(mut self) -> Stats { + // 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 { @@ -112,7 +119,7 @@ impl DivAssign<usize> for Stats { } #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct MaterialStatManifest(HashMap<String, Stats>); +pub struct MaterialStatManifest(pub HashMap<String, Stats>); // This could be a Compound that also loads the keys, but the RecipeBook // Compound impl already does that, so checking for existence here is redundant. @@ -166,16 +173,13 @@ impl StatKind { average_mult /= multipliers.len(); stats *= average_mult; } - // if an item has 0.0 speed, that panics due to being infinite duration, so - // enforce speed >= 0.1 - stats.speed = stats.speed.max(0.1); stats } } impl From<(&MaterialStatManifest, &[Item], &Tool)> for Stats { fn from((msm, components, tool): (&MaterialStatManifest, &[Item], &Tool)) -> Self { - let raw_stats = tool.stats.resolve_stats(msm, components); + let raw_stats = tool.stats.resolve_stats(msm, components).clamp_speed(); let (power, speed) = match tool.hands { Hands::One => (0.67, 1.33), // TODO: Restore this when one-handed weapons are made accessible @@ -245,7 +249,10 @@ impl Tool { } pub fn base_speed(&self, msm: &MaterialStatManifest, components: &[Item]) -> f32 { - self.stats.resolve_stats(msm, components).speed + self.stats + .resolve_stats(msm, components) + .clamp_speed() + .speed } pub fn equip_time(&self, msm: &MaterialStatManifest, components: &[Item]) -> Duration { diff --git a/voxygen/src/hud/bag.rs b/voxygen/src/hud/bag.rs index 7460a3bb74..f5a5db77ba 100644 --- a/voxygen/src/hud/bag.rs +++ b/voxygen/src/hud/bag.rs @@ -509,6 +509,7 @@ impl<'a> Widget for Bag<'a> { let (title, desc) = loadout_slot_text( inventory.equipped(EquipSlot::Armor(ArmorSlot::Head)), || (i18n.get("hud.bag.head"), ""), + &self.msm, ); let head_q_col = inventory .equipped(EquipSlot::Armor(ArmorSlot::Head)) @@ -531,6 +532,7 @@ impl<'a> Widget for Bag<'a> { let (title, desc) = loadout_slot_text( inventory.equipped(EquipSlot::Armor(ArmorSlot::Neck)), || (i18n.get("hud.bag.neck"), ""), + &self.msm, ); let neck_q_col = inventory .equipped(EquipSlot::Armor(ArmorSlot::Neck)) @@ -554,6 +556,7 @@ impl<'a> Widget for Bag<'a> { let (title, desc) = loadout_slot_text( inventory.equipped(EquipSlot::Armor(ArmorSlot::Chest)), || (i18n.get("hud.bag.chest"), ""), + &self.msm, ); let chest_q_col = inventory .equipped(EquipSlot::Armor(ArmorSlot::Chest)) @@ -576,6 +579,7 @@ impl<'a> Widget for Bag<'a> { let (title, desc) = loadout_slot_text( inventory.equipped(EquipSlot::Armor(ArmorSlot::Shoulders)), || (i18n.get("hud.bag.shoulders"), ""), + &self.msm, ); let shoulder_q_col = inventory .equipped(EquipSlot::Armor(ArmorSlot::Shoulders)) @@ -598,6 +602,7 @@ impl<'a> Widget for Bag<'a> { let (title, desc) = loadout_slot_text( inventory.equipped(EquipSlot::Armor(ArmorSlot::Hands)), || (i18n.get("hud.bag.hands"), ""), + &self.msm, ); let chest_q_col = inventory .equipped(EquipSlot::Armor(ArmorSlot::Hands)) @@ -620,6 +625,7 @@ impl<'a> Widget for Bag<'a> { let (title, desc) = loadout_slot_text( inventory.equipped(EquipSlot::Armor(ArmorSlot::Belt)), || (i18n.get("hud.bag.belt"), ""), + &self.msm, ); let belt_q_col = inventory .equipped(EquipSlot::Armor(ArmorSlot::Belt)) @@ -642,6 +648,7 @@ impl<'a> Widget for Bag<'a> { let (title, desc) = loadout_slot_text( inventory.equipped(EquipSlot::Armor(ArmorSlot::Legs)), || (i18n.get("hud.bag.legs"), ""), + &self.msm, ); let legs_q_col = inventory .equipped(EquipSlot::Armor(ArmorSlot::Legs)) @@ -664,6 +671,7 @@ impl<'a> Widget for Bag<'a> { let (title, desc) = loadout_slot_text( inventory.equipped(EquipSlot::Armor(ArmorSlot::Ring1)), || (i18n.get("hud.bag.ring"), ""), + &self.msm, ); let ring_q_col = inventory .equipped(EquipSlot::Armor(ArmorSlot::Ring1)) @@ -686,6 +694,7 @@ impl<'a> Widget for Bag<'a> { let (title, desc) = loadout_slot_text( inventory.equipped(EquipSlot::Armor(ArmorSlot::Ring2)), || (i18n.get("hud.bag.ring"), ""), + &self.msm, ); let ring2_q_col = inventory .equipped(EquipSlot::Armor(ArmorSlot::Ring2)) @@ -708,6 +717,7 @@ impl<'a> Widget for Bag<'a> { let (title, desc) = loadout_slot_text( inventory.equipped(EquipSlot::Armor(ArmorSlot::Back)), || (i18n.get("hud.bag.back"), ""), + &self.msm, ); let back_q_col = inventory .equipped(EquipSlot::Armor(ArmorSlot::Back)) @@ -730,6 +740,7 @@ impl<'a> Widget for Bag<'a> { let (title, desc) = loadout_slot_text( inventory.equipped(EquipSlot::Armor(ArmorSlot::Feet)), || (i18n.get("hud.bag.feet"), ""), + &self.msm, ); let foot_q_col = inventory .equipped(EquipSlot::Armor(ArmorSlot::Feet)) @@ -749,9 +760,11 @@ impl<'a> Widget for Bag<'a> { ) .set(state.ids.feet_slot, ui); // Lantern - let (title, desc) = loadout_slot_text(inventory.equipped(EquipSlot::Lantern), || { - (i18n.get("hud.bag.lantern"), "") - }); + let (title, desc) = loadout_slot_text( + inventory.equipped(EquipSlot::Lantern), + || (i18n.get("hud.bag.lantern"), ""), + &self.msm, + ); let lantern_q_col = inventory .equipped(EquipSlot::Lantern) .map(|item| get_quality_col(item)) @@ -770,9 +783,11 @@ impl<'a> Widget for Bag<'a> { ) .set(state.ids.lantern_slot, ui); // Glider - let (title, desc) = loadout_slot_text(inventory.equipped(EquipSlot::Glider), || { - (i18n.get("hud.bag.glider"), "") - }); + let (title, desc) = loadout_slot_text( + inventory.equipped(EquipSlot::Glider), + || (i18n.get("hud.bag.glider"), ""), + &self.msm, + ); let glider_q_col = inventory .equipped(EquipSlot::Glider) .map(|item| get_quality_col(item)) @@ -794,6 +809,7 @@ impl<'a> Widget for Bag<'a> { let (title, desc) = loadout_slot_text( inventory.equipped(EquipSlot::Armor(ArmorSlot::Tabard)), || (i18n.get("hud.bag.tabard"), ""), + &self.msm, ); let tabard_q_col = inventory .equipped(EquipSlot::Armor(ArmorSlot::Tabard)) @@ -813,9 +829,11 @@ impl<'a> Widget for Bag<'a> { ) .set(state.ids.tabard_slot, ui); // Mainhand/Left-Slot - let (title, desc) = loadout_slot_text(inventory.equipped(EquipSlot::Mainhand), || { - (i18n.get("hud.bag.mainhand"), "") - }); + let (title, desc) = loadout_slot_text( + inventory.equipped(EquipSlot::Mainhand), + || (i18n.get("hud.bag.mainhand"), ""), + &self.msm, + ); let mainhand_q_col = inventory .equipped(EquipSlot::Mainhand) .map(|item| get_quality_col(item)) @@ -834,9 +852,11 @@ impl<'a> Widget for Bag<'a> { ) .set(state.ids.mainhand_slot, ui); // Offhand/Right-Slot - let (title, desc) = loadout_slot_text(inventory.equipped(EquipSlot::Offhand), || { - (i18n.get("hud.bag.offhand"), "") - }); + let (title, desc) = loadout_slot_text( + inventory.equipped(EquipSlot::Offhand), + || (i18n.get("hud.bag.offhand"), ""), + &self.msm, + ); let offhand_q_col = inventory .equipped(EquipSlot::Offhand) .map(|item| get_quality_col(item)) @@ -859,6 +879,7 @@ impl<'a> Widget for Bag<'a> { let (title, desc) = loadout_slot_text( inventory.equipped(EquipSlot::Armor(ArmorSlot::Bag1)), || (i18n.get("hud.bag.bag"), ""), + &self.msm, ); let bag1_q_col = inventory .equipped(EquipSlot::Armor(ArmorSlot::Bag1)) @@ -885,6 +906,7 @@ impl<'a> Widget for Bag<'a> { let (title, desc) = loadout_slot_text( inventory.equipped(EquipSlot::Armor(ArmorSlot::Bag2)), || (i18n.get("hud.bag.bag"), ""), + &self.msm, ); let bag2_q_col = inventory .equipped(EquipSlot::Armor(ArmorSlot::Bag2)) @@ -907,6 +929,7 @@ impl<'a> Widget for Bag<'a> { let (title, desc) = loadout_slot_text( inventory.equipped(EquipSlot::Armor(ArmorSlot::Bag3)), || (i18n.get("hud.bag.bag"), ""), + &self.msm, ); let bag3_q_col = inventory .equipped(EquipSlot::Armor(ArmorSlot::Bag3)) @@ -929,6 +952,7 @@ impl<'a> Widget for Bag<'a> { let (title, desc) = loadout_slot_text( inventory.equipped(EquipSlot::Armor(ArmorSlot::Bag4)), || (i18n.get("hud.bag.bag"), ""), + &self.msm, ); let bag4_q_col = inventory .equipped(EquipSlot::Armor(ArmorSlot::Bag4)) @@ -1011,7 +1035,7 @@ impl<'a> Widget for Bag<'a> { } if let Some(item) = item { - let (title, desc) = super::util::item_text(item); + let (title, desc) = super::util::item_text(item, &self.msm); let quality_col = get_quality_col(item); let quality_col_img = match item.quality() { Quality::Low => self.imgs.inv_slot_grey, diff --git a/voxygen/src/hud/crafting.rs b/voxygen/src/hud/crafting.rs index f071951e58..97aa47706b 100644 --- a/voxygen/src/hud/crafting.rs +++ b/voxygen/src/hud/crafting.rs @@ -12,7 +12,7 @@ use client::{self, Client}; use common::{ assets::AssetExt, comp::{ - item::{ItemDef, ItemDesc, Quality, TagExampleInfo}, + item::{ItemDef, ItemDesc, MaterialStatManifest, Quality, TagExampleInfo}, Inventory, }, recipe::RecipeInput, @@ -68,6 +68,7 @@ pub struct Crafting<'a> { tooltip_manager: &'a mut TooltipManager, item_imgs: &'a ItemImgs, inventory: &'a Inventory, + msm: &'a MaterialStatManifest, #[conrod(common_builder)] common: widget::CommonBuilder, } @@ -83,6 +84,7 @@ impl<'a> Crafting<'a> { tooltip_manager: &'a mut TooltipManager, item_imgs: &'a ItemImgs, inventory: &'a Inventory, + msm: &'a MaterialStatManifest, ) -> Self { Self { client, @@ -94,6 +96,7 @@ impl<'a> Crafting<'a> { tooltip_manager, item_imgs, inventory, + msm, common: widget::CommonBuilder::default(), } } @@ -297,7 +300,7 @@ impl<'a> Widget for Crafting<'a> { { let output_text = format!("x{}", &recipe.output.1.to_string()); // Output Image - let (title, desc) = super::util::item_text(&*recipe.output.0); + let (title, desc) = super::util::item_text(&*recipe.output.0, self.msm); let quality_col = get_quality_col(&*recipe.output.0); Button::image(animate_by_pulse( &self @@ -488,7 +491,7 @@ impl<'a> Widget for Crafting<'a> { }; frame.set(state.ids.ingredient_frame[i], ui); //Item Image - let (title, desc) = super::util::item_text(&*item_def); + let (title, desc) = super::util::item_text(&*item_def, self.msm); Button::image(animate_by_pulse( &self.item_imgs.img_ids_or_not_found_img((&*item_def).into()), self.pulse, diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 003248e1d3..01f13e6687 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -2143,6 +2143,7 @@ impl Hud { tooltip_manager, &mut self.slot_manager, i18n, + &msm, self.pulse, ) .set(self.ids.trade, ui_widgets) @@ -2207,6 +2208,7 @@ impl Hud { tooltip_manager, &self.item_imgs, &inventory, + &msm, ) .set(self.ids.crafting_window, ui_widgets) { diff --git a/voxygen/src/hud/skillbar.rs b/voxygen/src/hud/skillbar.rs index f11aea24f9..4bfeddf724 100644 --- a/voxygen/src/hud/skillbar.rs +++ b/voxygen/src/hud/skillbar.rs @@ -400,7 +400,13 @@ impl<'a> Widget for Skillbar<'a> { .set(state.ids.stamina_txt, ui); } // Slots - let content_source = (self.hotbar, self.inventory, self.energy, self.ability_map); // TODO: avoid this + let content_source = ( + self.hotbar, + self.inventory, + self.energy, + self.ability_map, + self.msm, + ); // TODO: avoid this let image_source = (self.item_imgs, self.imgs); let mut slot_maker = SlotMaker { // TODO: is a separate image needed for the frame? diff --git a/voxygen/src/hud/slots.rs b/voxygen/src/hud/slots.rs index 7dd43f47b6..bd8e4446d9 100644 --- a/voxygen/src/hud/slots.rs +++ b/voxygen/src/hud/slots.rs @@ -2,13 +2,12 @@ use super::{ hotbar::{self, Slot as HotbarSlot}, img_ids, item_imgs::{ItemImgs, ItemKey}, - util::MATERIAL_STATS_MANIFEST, }; use crate::ui::slot::{self, SlotKey, SumSlot}; use common::comp::{ item::{ tool::{AbilityMap, Hands, ToolKind}, - ItemKind, + ItemKind, MaterialStatManifest, }, slot::InvSlotId, Energy, Inventory, @@ -102,7 +101,13 @@ pub enum HotbarImage { BowJumpBurst, } -type HotbarSource<'a> = (&'a hotbar::State, &'a Inventory, &'a Energy, &'a AbilityMap); +type HotbarSource<'a> = ( + &'a hotbar::State, + &'a Inventory, + &'a Energy, + &'a AbilityMap, + &'a MaterialStatManifest, +); type HotbarImageSource<'a> = (&'a ItemImgs, &'a img_ids::Imgs); impl<'a> SlotKey<HotbarSource<'a>, HotbarImageSource<'a>> for HotbarSlot { @@ -110,7 +115,7 @@ impl<'a> SlotKey<HotbarSource<'a>, HotbarImageSource<'a>> for HotbarSlot { fn image_key( &self, - (hotbar, inventory, energy, ability_map): &HotbarSource<'a>, + (hotbar, inventory, energy, ability_map, msm): &HotbarSource<'a>, ) -> Option<(Self::ImageKey, Option<Color>)> { hotbar.get(*self).and_then(|contents| match contents { hotbar::SlotContents::Inventory(idx) => inventory @@ -131,11 +136,7 @@ impl<'a> SlotKey<HotbarSource<'a>, HotbarImageSource<'a>> for HotbarSlot { ( i, if let Some(skill) = tool - .get_abilities( - &MATERIAL_STATS_MANIFEST, - item.components(), - ability_map, - ) + .get_abilities(&msm, item.components(), ability_map) .abilities .get(0) { @@ -178,11 +179,7 @@ impl<'a> SlotKey<HotbarSource<'a>, HotbarImageSource<'a>> for HotbarSlot { ( i, if let Some(skill) = tool - .get_abilities( - &MATERIAL_STATS_MANIFEST, - item.components(), - ability_map, - ) + .get_abilities(&msm, item.components(), ability_map) .abilities .get(skill_index) { @@ -201,7 +198,7 @@ impl<'a> SlotKey<HotbarSource<'a>, HotbarImageSource<'a>> for HotbarSlot { }) } - fn amount(&self, (hotbar, inventory, _, _): &HotbarSource<'a>) -> Option<u32> { + fn amount(&self, (hotbar, inventory, _, _, _): &HotbarSource<'a>) -> Option<u32> { hotbar .get(*self) .and_then(|content| match content { diff --git a/voxygen/src/hud/trade.rs b/voxygen/src/hud/trade.rs index d5312e3ced..150082e625 100644 --- a/voxygen/src/hud/trade.rs +++ b/voxygen/src/hud/trade.rs @@ -15,7 +15,10 @@ use crate::{ }; use client::Client; use common::{ - comp::{inventory::item::Quality, Inventory}, + comp::{ + inventory::item::{MaterialStatManifest, Quality}, + Inventory, + }, trade::{PendingTrade, TradeAction, TradePhase}, }; use common_net::sync::WorldSyncExt; @@ -61,6 +64,7 @@ pub struct Trade<'a> { common: widget::CommonBuilder, slot_manager: &'a mut SlotManager, localized_strings: &'a Localization, + msm: &'a MaterialStatManifest, pulse: f32, } @@ -74,6 +78,7 @@ impl<'a> Trade<'a> { tooltip_manager: &'a mut TooltipManager, slot_manager: &'a mut SlotManager, localized_strings: &'a Localization, + msm: &'a MaterialStatManifest, pulse: f32, ) -> Self { Self { @@ -84,9 +89,9 @@ impl<'a> Trade<'a> { rot_imgs, tooltip_manager, common: widget::CommonBuilder::default(), - //tooltip_manager, slot_manager, localized_strings, + msm, pulse, } } @@ -295,7 +300,7 @@ impl<'a> Trade<'a> { ); let slot_id = state.ids.inv_slots[i + who * MAX_TRADE_SLOTS]; if let Some(Some(item)) = slot.invslot.and_then(|slotid| inventory.slot(slotid)) { - let (title, desc) = super::util::item_text(item); + let (title, desc) = super::util::item_text(item, self.msm); let quality_col = get_quality_col(item); let quality_col_img = match item.quality() { Quality::Low => self.imgs.inv_slot_grey, diff --git a/voxygen/src/hud/util.rs b/voxygen/src/hud/util.rs index ad6c324874..cde747dfe4 100644 --- a/voxygen/src/hud/util.rs +++ b/voxygen/src/hud/util.rs @@ -1,30 +1,28 @@ use common::comp::item::{ armor::{Armor, ArmorKind, Protection}, - tool::{Hands, StatKind, Tool, ToolKind}, + tool::{Hands, StatKind, Stats, Tool, ToolKind}, Item, ItemDesc, ItemKind, MaterialStatManifest, ModularComponent, }; -use lazy_static::lazy_static; use std::{borrow::Cow, fmt::Write}; -lazy_static! { - // TODO: even more plumbing - pub static ref MATERIAL_STATS_MANIFEST: MaterialStatManifest = MaterialStatManifest::default(); -} - pub fn loadout_slot_text<'a>( item: Option<&'a impl ItemDesc>, mut empty: impl FnMut() -> (&'a str, &'a str), + msm: &'a MaterialStatManifest, ) -> (&'a str, Cow<'a, str>) { item.map_or_else( || { let (title, desc) = empty(); (title, Cow::Borrowed(desc)) }, - item_text, + |item| item_text(item, msm), ) } -pub fn item_text<'a>(item: &'a impl ItemDesc) -> (&'_ str, Cow<'a, str>) { +pub fn item_text<'a>( + item: &'a impl ItemDesc, + msm: &'a MaterialStatManifest, +) -> (&'a str, Cow<'a, str>) { let desc: Cow<str> = match item.kind() { ItemKind::Armor(armor) => { Cow::Owned(armor_desc(armor, item.description(), item.num_slots())) @@ -32,20 +30,24 @@ pub fn item_text<'a>(item: &'a impl ItemDesc) -> (&'_ str, Cow<'a, str>) { ItemKind::Tool(tool) => Cow::Owned(tool_desc( &tool, item.components(), - &MATERIAL_STATS_MANIFEST, + &msm, item.description(), )), ItemKind::ModularComponent(mc) => Cow::Owned(modular_component_desc( mc, item.components(), - &MATERIAL_STATS_MANIFEST, + &msm, item.description(), )), ItemKind::Glider(_glider) => Cow::Owned(glider_desc(item.description())), ItemKind::Consumable { .. } => Cow::Owned(consumable_desc(item.description())), ItemKind::Throwable { .. } => Cow::Owned(throwable_desc(item.description())), ItemKind::Utility { .. } => Cow::Owned(utility_desc(item.description())), - ItemKind::Ingredient { .. } => Cow::Owned(ingredient_desc(item.description())), + ItemKind::Ingredient { .. } => Cow::Owned(ingredient_desc( + item.description(), + item.item_definition_id(), + msm, + )), ItemKind::Lantern { .. } => Cow::Owned(lantern_desc(item.description())), ItemKind::TagExamples { .. } => Cow::Borrowed(item.description()), //_ => Cow::Borrowed(item.description()), @@ -61,13 +63,11 @@ fn modular_component_desc( msm: &MaterialStatManifest, description: &str, ) -> String { - let mut result = format!( - "Modular Component\n\n{:?}\n\n{}", - StatKind::Direct(mc.stats).resolve_stats(msm, components), - description - ); + let stats = StatKind::Direct(mc.stats).resolve_stats(msm, components); + let statblock = statblock_desc(&stats); + let mut result = format!("Modular Component\n\n{}\n\n{}", statblock, description); if !components.is_empty() { - result += "Made from:\n"; + result += "\n\nMade from:\n"; for component in components { result += component.name(); result += "\n" @@ -88,7 +88,14 @@ fn throwable_desc(desc: &str) -> String { fn utility_desc(desc: &str) -> String { format!("{}\n\n<Right-Click to use>", desc) } -fn ingredient_desc(desc: &str) -> String { format!("Crafting Ingredient\n\n{}", desc) } +fn ingredient_desc(desc: &str, item_id: &str, msm: &MaterialStatManifest) -> String { + let mut result = format!("Crafting Ingredient\n\n{}", desc); + if let Some(stats) = msm.0.get(item_id) { + result += "\n\nStat multipliers:\n"; + result += &statblock_desc(stats); + } + result +} fn lantern_desc(desc: &str) -> String { format!("Lantern\n\n{}\n\n<Right-Click to use>", desc) } @@ -153,26 +160,16 @@ fn tool_desc(tool: &Tool, components: &[Item], msm: &MaterialStatManifest, desc: }; // Get tool stats - let power = tool.base_power(msm, components); + let stats = tool.stats.resolve_stats(msm, components).clamp_speed(); + //let poise_strength = tool.base_poise_strength(); let hands = match tool.hands { Hands::One => "One", Hands::Two => "Two", }; - let speed = tool.base_speed(msm, components); - let mut result = format!( - "{}-Handed {}\n\nDPS: {:0.1}\n\nPower: {:0.1}\n\nSpeed: {:0.1}\n\n", - // add back when ready for poise - //"{}\n\nDPS: {:0.1}\n\nPower: {:0.1}\n\nPoise Strength: {:0.1}\n\nSpeed: \ - // {:0.1}\n\n{}\n\n<Right-Click to use>", - hands, - kind, - speed * power * 10.0, // Damage per second - power * 10.0, - //poise_strength * 10.0, - speed - ); + let mut result = format!("{}-Handed {}\n\n", hands, kind); + result += &statblock_desc(&stats); if !components.is_empty() { result += "Made from:\n"; for component in components { @@ -188,6 +185,19 @@ fn tool_desc(tool: &Tool, components: &[Item], msm: &MaterialStatManifest, desc: result } +fn statblock_desc(stats: &Stats) -> String { + format!( + "DPS: {:0.1}\n\nPower: {:0.1}\n\nSpeed: {:0.1}\n\n", + // add back when ready for poise + //"{}\n\nDPS: {:0.1}\n\nPower: {:0.1}\n\nPoise Strength: {:0.1}\n\nSpeed: \ + // {:0.1}\n\n{}\n\n<Right-Click to use>", + stats.speed * stats.power * 10.0, // Damage per second + stats.power * 10.0, + //stats.poise_strength * 10.0, + stats.speed + ) +} + #[cfg(test)] mod tests { use super::*; @@ -234,11 +244,29 @@ mod tests { #[test] fn test_ingredient_desc() { - let item_description = "mushrooms"; + let mut testmsm = MaterialStatManifest(hashbrown::HashMap::new()); + testmsm.0.insert( + "common.items.crafting_ing.bronze_ingot".to_string(), + Stats { + equip_time_millis: 0, + power: 3.0, + poise_strength: 5.0, + speed: 7.0, + }, + ); assert_eq!( "Crafting Ingredient\n\nmushrooms", - ingredient_desc(item_description) + ingredient_desc("mushrooms", "common.items.food.mushroom", &testmsm) + ); + assert_eq!( + "Crafting Ingredient\n\nA bronze ingot.\n\nStat multipliers:\nDPS: 210.0\n\nPower: \ + 30.0\n\nSpeed: 7.0\n\n", + ingredient_desc( + "A bronze ingot.", + "common.items.crafting_ing.bronze_ingot", + &testmsm + ) ); }