Finish plumbing MaterialStatsManifest. Fix issue with speed clamping when recursing through components. Improve statblocks in item tooltips.

This commit is contained in:
Avi Weinstock 2021-02-25 14:04:09 -05:00
parent 78014d7d3b
commit e1484c28c0
10 changed files with 156 additions and 84 deletions

View File

@ -5,5 +5,5 @@ ItemDef(
kind: "TinIngot", kind: "TinIngot",
), ),
quality: Common, quality: Common,
tags: [MetalIngot(0.25)], tags: [MetalIngot],
) )

View File

@ -363,11 +363,11 @@
(Item("common.items.crafting_tools.sewing_set"), 0), (Item("common.items.crafting_tools.sewing_set"), 0),
], ],
), ),
"metal_blade": ( //"metal_blade": (
("common.items.crafting_ing.modular.damage.sword.metal_blade", 1), // ("common.items.crafting_ing.modular.damage.sword.metal_blade", 1),
[ // [
(Tag(MetalIngot), 5), // (Tag(MetalIngot), 5),
(Item("common.items.crafting_tools.craftsman_hammer"), 0), // (Item("common.items.crafting_tools.craftsman_hammer"), 0),
], // ],
), //),
} }

View File

@ -72,6 +72,13 @@ impl Stats {
speed: 0.0, 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 { impl Asset for Stats {
@ -112,7 +119,7 @@ impl DivAssign<usize> for Stats {
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[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 // 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. // Compound impl already does that, so checking for existence here is redundant.
@ -166,16 +173,13 @@ impl StatKind {
average_mult /= multipliers.len(); average_mult /= multipliers.len();
stats *= average_mult; 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 stats
} }
} }
impl From<(&MaterialStatManifest, &[Item], &Tool)> for Stats { impl From<(&MaterialStatManifest, &[Item], &Tool)> for Stats {
fn from((msm, components, tool): (&MaterialStatManifest, &[Item], &Tool)) -> Self { 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 { let (power, speed) = match tool.hands {
Hands::One => (0.67, 1.33), Hands::One => (0.67, 1.33),
// TODO: Restore this when one-handed weapons are made accessible // 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 { 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 { pub fn equip_time(&self, msm: &MaterialStatManifest, components: &[Item]) -> Duration {

View File

@ -509,6 +509,7 @@ impl<'a> Widget for Bag<'a> {
let (title, desc) = loadout_slot_text( let (title, desc) = loadout_slot_text(
inventory.equipped(EquipSlot::Armor(ArmorSlot::Head)), inventory.equipped(EquipSlot::Armor(ArmorSlot::Head)),
|| (i18n.get("hud.bag.head"), ""), || (i18n.get("hud.bag.head"), ""),
&self.msm,
); );
let head_q_col = inventory let head_q_col = inventory
.equipped(EquipSlot::Armor(ArmorSlot::Head)) .equipped(EquipSlot::Armor(ArmorSlot::Head))
@ -531,6 +532,7 @@ impl<'a> Widget for Bag<'a> {
let (title, desc) = loadout_slot_text( let (title, desc) = loadout_slot_text(
inventory.equipped(EquipSlot::Armor(ArmorSlot::Neck)), inventory.equipped(EquipSlot::Armor(ArmorSlot::Neck)),
|| (i18n.get("hud.bag.neck"), ""), || (i18n.get("hud.bag.neck"), ""),
&self.msm,
); );
let neck_q_col = inventory let neck_q_col = inventory
.equipped(EquipSlot::Armor(ArmorSlot::Neck)) .equipped(EquipSlot::Armor(ArmorSlot::Neck))
@ -554,6 +556,7 @@ impl<'a> Widget for Bag<'a> {
let (title, desc) = loadout_slot_text( let (title, desc) = loadout_slot_text(
inventory.equipped(EquipSlot::Armor(ArmorSlot::Chest)), inventory.equipped(EquipSlot::Armor(ArmorSlot::Chest)),
|| (i18n.get("hud.bag.chest"), ""), || (i18n.get("hud.bag.chest"), ""),
&self.msm,
); );
let chest_q_col = inventory let chest_q_col = inventory
.equipped(EquipSlot::Armor(ArmorSlot::Chest)) .equipped(EquipSlot::Armor(ArmorSlot::Chest))
@ -576,6 +579,7 @@ impl<'a> Widget for Bag<'a> {
let (title, desc) = loadout_slot_text( let (title, desc) = loadout_slot_text(
inventory.equipped(EquipSlot::Armor(ArmorSlot::Shoulders)), inventory.equipped(EquipSlot::Armor(ArmorSlot::Shoulders)),
|| (i18n.get("hud.bag.shoulders"), ""), || (i18n.get("hud.bag.shoulders"), ""),
&self.msm,
); );
let shoulder_q_col = inventory let shoulder_q_col = inventory
.equipped(EquipSlot::Armor(ArmorSlot::Shoulders)) .equipped(EquipSlot::Armor(ArmorSlot::Shoulders))
@ -598,6 +602,7 @@ impl<'a> Widget for Bag<'a> {
let (title, desc) = loadout_slot_text( let (title, desc) = loadout_slot_text(
inventory.equipped(EquipSlot::Armor(ArmorSlot::Hands)), inventory.equipped(EquipSlot::Armor(ArmorSlot::Hands)),
|| (i18n.get("hud.bag.hands"), ""), || (i18n.get("hud.bag.hands"), ""),
&self.msm,
); );
let chest_q_col = inventory let chest_q_col = inventory
.equipped(EquipSlot::Armor(ArmorSlot::Hands)) .equipped(EquipSlot::Armor(ArmorSlot::Hands))
@ -620,6 +625,7 @@ impl<'a> Widget for Bag<'a> {
let (title, desc) = loadout_slot_text( let (title, desc) = loadout_slot_text(
inventory.equipped(EquipSlot::Armor(ArmorSlot::Belt)), inventory.equipped(EquipSlot::Armor(ArmorSlot::Belt)),
|| (i18n.get("hud.bag.belt"), ""), || (i18n.get("hud.bag.belt"), ""),
&self.msm,
); );
let belt_q_col = inventory let belt_q_col = inventory
.equipped(EquipSlot::Armor(ArmorSlot::Belt)) .equipped(EquipSlot::Armor(ArmorSlot::Belt))
@ -642,6 +648,7 @@ impl<'a> Widget for Bag<'a> {
let (title, desc) = loadout_slot_text( let (title, desc) = loadout_slot_text(
inventory.equipped(EquipSlot::Armor(ArmorSlot::Legs)), inventory.equipped(EquipSlot::Armor(ArmorSlot::Legs)),
|| (i18n.get("hud.bag.legs"), ""), || (i18n.get("hud.bag.legs"), ""),
&self.msm,
); );
let legs_q_col = inventory let legs_q_col = inventory
.equipped(EquipSlot::Armor(ArmorSlot::Legs)) .equipped(EquipSlot::Armor(ArmorSlot::Legs))
@ -664,6 +671,7 @@ impl<'a> Widget for Bag<'a> {
let (title, desc) = loadout_slot_text( let (title, desc) = loadout_slot_text(
inventory.equipped(EquipSlot::Armor(ArmorSlot::Ring1)), inventory.equipped(EquipSlot::Armor(ArmorSlot::Ring1)),
|| (i18n.get("hud.bag.ring"), ""), || (i18n.get("hud.bag.ring"), ""),
&self.msm,
); );
let ring_q_col = inventory let ring_q_col = inventory
.equipped(EquipSlot::Armor(ArmorSlot::Ring1)) .equipped(EquipSlot::Armor(ArmorSlot::Ring1))
@ -686,6 +694,7 @@ impl<'a> Widget for Bag<'a> {
let (title, desc) = loadout_slot_text( let (title, desc) = loadout_slot_text(
inventory.equipped(EquipSlot::Armor(ArmorSlot::Ring2)), inventory.equipped(EquipSlot::Armor(ArmorSlot::Ring2)),
|| (i18n.get("hud.bag.ring"), ""), || (i18n.get("hud.bag.ring"), ""),
&self.msm,
); );
let ring2_q_col = inventory let ring2_q_col = inventory
.equipped(EquipSlot::Armor(ArmorSlot::Ring2)) .equipped(EquipSlot::Armor(ArmorSlot::Ring2))
@ -708,6 +717,7 @@ impl<'a> Widget for Bag<'a> {
let (title, desc) = loadout_slot_text( let (title, desc) = loadout_slot_text(
inventory.equipped(EquipSlot::Armor(ArmorSlot::Back)), inventory.equipped(EquipSlot::Armor(ArmorSlot::Back)),
|| (i18n.get("hud.bag.back"), ""), || (i18n.get("hud.bag.back"), ""),
&self.msm,
); );
let back_q_col = inventory let back_q_col = inventory
.equipped(EquipSlot::Armor(ArmorSlot::Back)) .equipped(EquipSlot::Armor(ArmorSlot::Back))
@ -730,6 +740,7 @@ impl<'a> Widget for Bag<'a> {
let (title, desc) = loadout_slot_text( let (title, desc) = loadout_slot_text(
inventory.equipped(EquipSlot::Armor(ArmorSlot::Feet)), inventory.equipped(EquipSlot::Armor(ArmorSlot::Feet)),
|| (i18n.get("hud.bag.feet"), ""), || (i18n.get("hud.bag.feet"), ""),
&self.msm,
); );
let foot_q_col = inventory let foot_q_col = inventory
.equipped(EquipSlot::Armor(ArmorSlot::Feet)) .equipped(EquipSlot::Armor(ArmorSlot::Feet))
@ -749,9 +760,11 @@ impl<'a> Widget for Bag<'a> {
) )
.set(state.ids.feet_slot, ui); .set(state.ids.feet_slot, ui);
// Lantern // Lantern
let (title, desc) = loadout_slot_text(inventory.equipped(EquipSlot::Lantern), || { let (title, desc) = loadout_slot_text(
(i18n.get("hud.bag.lantern"), "") inventory.equipped(EquipSlot::Lantern),
}); || (i18n.get("hud.bag.lantern"), ""),
&self.msm,
);
let lantern_q_col = inventory let lantern_q_col = inventory
.equipped(EquipSlot::Lantern) .equipped(EquipSlot::Lantern)
.map(|item| get_quality_col(item)) .map(|item| get_quality_col(item))
@ -770,9 +783,11 @@ impl<'a> Widget for Bag<'a> {
) )
.set(state.ids.lantern_slot, ui); .set(state.ids.lantern_slot, ui);
// Glider // Glider
let (title, desc) = loadout_slot_text(inventory.equipped(EquipSlot::Glider), || { let (title, desc) = loadout_slot_text(
(i18n.get("hud.bag.glider"), "") inventory.equipped(EquipSlot::Glider),
}); || (i18n.get("hud.bag.glider"), ""),
&self.msm,
);
let glider_q_col = inventory let glider_q_col = inventory
.equipped(EquipSlot::Glider) .equipped(EquipSlot::Glider)
.map(|item| get_quality_col(item)) .map(|item| get_quality_col(item))
@ -794,6 +809,7 @@ impl<'a> Widget for Bag<'a> {
let (title, desc) = loadout_slot_text( let (title, desc) = loadout_slot_text(
inventory.equipped(EquipSlot::Armor(ArmorSlot::Tabard)), inventory.equipped(EquipSlot::Armor(ArmorSlot::Tabard)),
|| (i18n.get("hud.bag.tabard"), ""), || (i18n.get("hud.bag.tabard"), ""),
&self.msm,
); );
let tabard_q_col = inventory let tabard_q_col = inventory
.equipped(EquipSlot::Armor(ArmorSlot::Tabard)) .equipped(EquipSlot::Armor(ArmorSlot::Tabard))
@ -813,9 +829,11 @@ impl<'a> Widget for Bag<'a> {
) )
.set(state.ids.tabard_slot, ui); .set(state.ids.tabard_slot, ui);
// Mainhand/Left-Slot // Mainhand/Left-Slot
let (title, desc) = loadout_slot_text(inventory.equipped(EquipSlot::Mainhand), || { let (title, desc) = loadout_slot_text(
(i18n.get("hud.bag.mainhand"), "") inventory.equipped(EquipSlot::Mainhand),
}); || (i18n.get("hud.bag.mainhand"), ""),
&self.msm,
);
let mainhand_q_col = inventory let mainhand_q_col = inventory
.equipped(EquipSlot::Mainhand) .equipped(EquipSlot::Mainhand)
.map(|item| get_quality_col(item)) .map(|item| get_quality_col(item))
@ -834,9 +852,11 @@ impl<'a> Widget for Bag<'a> {
) )
.set(state.ids.mainhand_slot, ui); .set(state.ids.mainhand_slot, ui);
// Offhand/Right-Slot // Offhand/Right-Slot
let (title, desc) = loadout_slot_text(inventory.equipped(EquipSlot::Offhand), || { let (title, desc) = loadout_slot_text(
(i18n.get("hud.bag.offhand"), "") inventory.equipped(EquipSlot::Offhand),
}); || (i18n.get("hud.bag.offhand"), ""),
&self.msm,
);
let offhand_q_col = inventory let offhand_q_col = inventory
.equipped(EquipSlot::Offhand) .equipped(EquipSlot::Offhand)
.map(|item| get_quality_col(item)) .map(|item| get_quality_col(item))
@ -859,6 +879,7 @@ impl<'a> Widget for Bag<'a> {
let (title, desc) = loadout_slot_text( let (title, desc) = loadout_slot_text(
inventory.equipped(EquipSlot::Armor(ArmorSlot::Bag1)), inventory.equipped(EquipSlot::Armor(ArmorSlot::Bag1)),
|| (i18n.get("hud.bag.bag"), ""), || (i18n.get("hud.bag.bag"), ""),
&self.msm,
); );
let bag1_q_col = inventory let bag1_q_col = inventory
.equipped(EquipSlot::Armor(ArmorSlot::Bag1)) .equipped(EquipSlot::Armor(ArmorSlot::Bag1))
@ -885,6 +906,7 @@ impl<'a> Widget for Bag<'a> {
let (title, desc) = loadout_slot_text( let (title, desc) = loadout_slot_text(
inventory.equipped(EquipSlot::Armor(ArmorSlot::Bag2)), inventory.equipped(EquipSlot::Armor(ArmorSlot::Bag2)),
|| (i18n.get("hud.bag.bag"), ""), || (i18n.get("hud.bag.bag"), ""),
&self.msm,
); );
let bag2_q_col = inventory let bag2_q_col = inventory
.equipped(EquipSlot::Armor(ArmorSlot::Bag2)) .equipped(EquipSlot::Armor(ArmorSlot::Bag2))
@ -907,6 +929,7 @@ impl<'a> Widget for Bag<'a> {
let (title, desc) = loadout_slot_text( let (title, desc) = loadout_slot_text(
inventory.equipped(EquipSlot::Armor(ArmorSlot::Bag3)), inventory.equipped(EquipSlot::Armor(ArmorSlot::Bag3)),
|| (i18n.get("hud.bag.bag"), ""), || (i18n.get("hud.bag.bag"), ""),
&self.msm,
); );
let bag3_q_col = inventory let bag3_q_col = inventory
.equipped(EquipSlot::Armor(ArmorSlot::Bag3)) .equipped(EquipSlot::Armor(ArmorSlot::Bag3))
@ -929,6 +952,7 @@ impl<'a> Widget for Bag<'a> {
let (title, desc) = loadout_slot_text( let (title, desc) = loadout_slot_text(
inventory.equipped(EquipSlot::Armor(ArmorSlot::Bag4)), inventory.equipped(EquipSlot::Armor(ArmorSlot::Bag4)),
|| (i18n.get("hud.bag.bag"), ""), || (i18n.get("hud.bag.bag"), ""),
&self.msm,
); );
let bag4_q_col = inventory let bag4_q_col = inventory
.equipped(EquipSlot::Armor(ArmorSlot::Bag4)) .equipped(EquipSlot::Armor(ArmorSlot::Bag4))
@ -1011,7 +1035,7 @@ impl<'a> Widget for Bag<'a> {
} }
if let Some(item) = item { 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 = get_quality_col(item);
let quality_col_img = match item.quality() { let quality_col_img = match item.quality() {
Quality::Low => self.imgs.inv_slot_grey, Quality::Low => self.imgs.inv_slot_grey,

View File

@ -12,7 +12,7 @@ use client::{self, Client};
use common::{ use common::{
assets::AssetExt, assets::AssetExt,
comp::{ comp::{
item::{ItemDef, ItemDesc, Quality, TagExampleInfo}, item::{ItemDef, ItemDesc, MaterialStatManifest, Quality, TagExampleInfo},
Inventory, Inventory,
}, },
recipe::RecipeInput, recipe::RecipeInput,
@ -68,6 +68,7 @@ pub struct Crafting<'a> {
tooltip_manager: &'a mut TooltipManager, tooltip_manager: &'a mut TooltipManager,
item_imgs: &'a ItemImgs, item_imgs: &'a ItemImgs,
inventory: &'a Inventory, inventory: &'a Inventory,
msm: &'a MaterialStatManifest,
#[conrod(common_builder)] #[conrod(common_builder)]
common: widget::CommonBuilder, common: widget::CommonBuilder,
} }
@ -83,6 +84,7 @@ impl<'a> Crafting<'a> {
tooltip_manager: &'a mut TooltipManager, tooltip_manager: &'a mut TooltipManager,
item_imgs: &'a ItemImgs, item_imgs: &'a ItemImgs,
inventory: &'a Inventory, inventory: &'a Inventory,
msm: &'a MaterialStatManifest,
) -> Self { ) -> Self {
Self { Self {
client, client,
@ -94,6 +96,7 @@ impl<'a> Crafting<'a> {
tooltip_manager, tooltip_manager,
item_imgs, item_imgs,
inventory, inventory,
msm,
common: widget::CommonBuilder::default(), common: widget::CommonBuilder::default(),
} }
} }
@ -297,7 +300,7 @@ impl<'a> Widget for Crafting<'a> {
{ {
let output_text = format!("x{}", &recipe.output.1.to_string()); let output_text = format!("x{}", &recipe.output.1.to_string());
// Output Image // 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); let quality_col = get_quality_col(&*recipe.output.0);
Button::image(animate_by_pulse( Button::image(animate_by_pulse(
&self &self
@ -488,7 +491,7 @@ impl<'a> Widget for Crafting<'a> {
}; };
frame.set(state.ids.ingredient_frame[i], ui); frame.set(state.ids.ingredient_frame[i], ui);
//Item Image //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( Button::image(animate_by_pulse(
&self.item_imgs.img_ids_or_not_found_img((&*item_def).into()), &self.item_imgs.img_ids_or_not_found_img((&*item_def).into()),
self.pulse, self.pulse,

View File

@ -2143,6 +2143,7 @@ impl Hud {
tooltip_manager, tooltip_manager,
&mut self.slot_manager, &mut self.slot_manager,
i18n, i18n,
&msm,
self.pulse, self.pulse,
) )
.set(self.ids.trade, ui_widgets) .set(self.ids.trade, ui_widgets)
@ -2207,6 +2208,7 @@ impl Hud {
tooltip_manager, tooltip_manager,
&self.item_imgs, &self.item_imgs,
&inventory, &inventory,
&msm,
) )
.set(self.ids.crafting_window, ui_widgets) .set(self.ids.crafting_window, ui_widgets)
{ {

View File

@ -400,7 +400,13 @@ impl<'a> Widget for Skillbar<'a> {
.set(state.ids.stamina_txt, ui); .set(state.ids.stamina_txt, ui);
} }
// Slots // 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 image_source = (self.item_imgs, self.imgs);
let mut slot_maker = SlotMaker { let mut slot_maker = SlotMaker {
// TODO: is a separate image needed for the frame? // TODO: is a separate image needed for the frame?

View File

@ -2,13 +2,12 @@ use super::{
hotbar::{self, Slot as HotbarSlot}, hotbar::{self, Slot as HotbarSlot},
img_ids, img_ids,
item_imgs::{ItemImgs, ItemKey}, item_imgs::{ItemImgs, ItemKey},
util::MATERIAL_STATS_MANIFEST,
}; };
use crate::ui::slot::{self, SlotKey, SumSlot}; use crate::ui::slot::{self, SlotKey, SumSlot};
use common::comp::{ use common::comp::{
item::{ item::{
tool::{AbilityMap, Hands, ToolKind}, tool::{AbilityMap, Hands, ToolKind},
ItemKind, ItemKind, MaterialStatManifest,
}, },
slot::InvSlotId, slot::InvSlotId,
Energy, Inventory, Energy, Inventory,
@ -102,7 +101,13 @@ pub enum HotbarImage {
BowJumpBurst, 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); type HotbarImageSource<'a> = (&'a ItemImgs, &'a img_ids::Imgs);
impl<'a> SlotKey<HotbarSource<'a>, HotbarImageSource<'a>> for HotbarSlot { 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( fn image_key(
&self, &self,
(hotbar, inventory, energy, ability_map): &HotbarSource<'a>, (hotbar, inventory, energy, ability_map, msm): &HotbarSource<'a>,
) -> Option<(Self::ImageKey, Option<Color>)> { ) -> Option<(Self::ImageKey, Option<Color>)> {
hotbar.get(*self).and_then(|contents| match contents { hotbar.get(*self).and_then(|contents| match contents {
hotbar::SlotContents::Inventory(idx) => inventory hotbar::SlotContents::Inventory(idx) => inventory
@ -131,11 +136,7 @@ impl<'a> SlotKey<HotbarSource<'a>, HotbarImageSource<'a>> for HotbarSlot {
( (
i, i,
if let Some(skill) = tool if let Some(skill) = tool
.get_abilities( .get_abilities(&msm, item.components(), ability_map)
&MATERIAL_STATS_MANIFEST,
item.components(),
ability_map,
)
.abilities .abilities
.get(0) .get(0)
{ {
@ -178,11 +179,7 @@ impl<'a> SlotKey<HotbarSource<'a>, HotbarImageSource<'a>> for HotbarSlot {
( (
i, i,
if let Some(skill) = tool if let Some(skill) = tool
.get_abilities( .get_abilities(&msm, item.components(), ability_map)
&MATERIAL_STATS_MANIFEST,
item.components(),
ability_map,
)
.abilities .abilities
.get(skill_index) .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 hotbar
.get(*self) .get(*self)
.and_then(|content| match content { .and_then(|content| match content {

View File

@ -15,7 +15,10 @@ use crate::{
}; };
use client::Client; use client::Client;
use common::{ use common::{
comp::{inventory::item::Quality, Inventory}, comp::{
inventory::item::{MaterialStatManifest, Quality},
Inventory,
},
trade::{PendingTrade, TradeAction, TradePhase}, trade::{PendingTrade, TradeAction, TradePhase},
}; };
use common_net::sync::WorldSyncExt; use common_net::sync::WorldSyncExt;
@ -61,6 +64,7 @@ pub struct Trade<'a> {
common: widget::CommonBuilder, common: widget::CommonBuilder,
slot_manager: &'a mut SlotManager, slot_manager: &'a mut SlotManager,
localized_strings: &'a Localization, localized_strings: &'a Localization,
msm: &'a MaterialStatManifest,
pulse: f32, pulse: f32,
} }
@ -74,6 +78,7 @@ impl<'a> Trade<'a> {
tooltip_manager: &'a mut TooltipManager, tooltip_manager: &'a mut TooltipManager,
slot_manager: &'a mut SlotManager, slot_manager: &'a mut SlotManager,
localized_strings: &'a Localization, localized_strings: &'a Localization,
msm: &'a MaterialStatManifest,
pulse: f32, pulse: f32,
) -> Self { ) -> Self {
Self { Self {
@ -84,9 +89,9 @@ impl<'a> Trade<'a> {
rot_imgs, rot_imgs,
tooltip_manager, tooltip_manager,
common: widget::CommonBuilder::default(), common: widget::CommonBuilder::default(),
//tooltip_manager,
slot_manager, slot_manager,
localized_strings, localized_strings,
msm,
pulse, pulse,
} }
} }
@ -295,7 +300,7 @@ impl<'a> Trade<'a> {
); );
let slot_id = state.ids.inv_slots[i + who * MAX_TRADE_SLOTS]; 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)) { 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 = get_quality_col(item);
let quality_col_img = match item.quality() { let quality_col_img = match item.quality() {
Quality::Low => self.imgs.inv_slot_grey, Quality::Low => self.imgs.inv_slot_grey,

View File

@ -1,30 +1,28 @@
use common::comp::item::{ use common::comp::item::{
armor::{Armor, ArmorKind, Protection}, armor::{Armor, ArmorKind, Protection},
tool::{Hands, StatKind, Tool, ToolKind}, tool::{Hands, StatKind, Stats, Tool, ToolKind},
Item, ItemDesc, ItemKind, MaterialStatManifest, ModularComponent, Item, ItemDesc, ItemKind, MaterialStatManifest, ModularComponent,
}; };
use lazy_static::lazy_static;
use std::{borrow::Cow, fmt::Write}; 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>( pub fn loadout_slot_text<'a>(
item: Option<&'a impl ItemDesc>, item: Option<&'a impl ItemDesc>,
mut empty: impl FnMut() -> (&'a str, &'a str), mut empty: impl FnMut() -> (&'a str, &'a str),
msm: &'a MaterialStatManifest,
) -> (&'a str, Cow<'a, str>) { ) -> (&'a str, Cow<'a, str>) {
item.map_or_else( item.map_or_else(
|| { || {
let (title, desc) = empty(); let (title, desc) = empty();
(title, Cow::Borrowed(desc)) (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() { let desc: Cow<str> = match item.kind() {
ItemKind::Armor(armor) => { ItemKind::Armor(armor) => {
Cow::Owned(armor_desc(armor, item.description(), item.num_slots())) 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( ItemKind::Tool(tool) => Cow::Owned(tool_desc(
&tool, &tool,
item.components(), item.components(),
&MATERIAL_STATS_MANIFEST, &msm,
item.description(), item.description(),
)), )),
ItemKind::ModularComponent(mc) => Cow::Owned(modular_component_desc( ItemKind::ModularComponent(mc) => Cow::Owned(modular_component_desc(
mc, mc,
item.components(), item.components(),
&MATERIAL_STATS_MANIFEST, &msm,
item.description(), item.description(),
)), )),
ItemKind::Glider(_glider) => Cow::Owned(glider_desc(item.description())), ItemKind::Glider(_glider) => Cow::Owned(glider_desc(item.description())),
ItemKind::Consumable { .. } => Cow::Owned(consumable_desc(item.description())), ItemKind::Consumable { .. } => Cow::Owned(consumable_desc(item.description())),
ItemKind::Throwable { .. } => Cow::Owned(throwable_desc(item.description())), ItemKind::Throwable { .. } => Cow::Owned(throwable_desc(item.description())),
ItemKind::Utility { .. } => Cow::Owned(utility_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::Lantern { .. } => Cow::Owned(lantern_desc(item.description())),
ItemKind::TagExamples { .. } => Cow::Borrowed(item.description()), ItemKind::TagExamples { .. } => Cow::Borrowed(item.description()),
//_ => Cow::Borrowed(item.description()), //_ => Cow::Borrowed(item.description()),
@ -61,13 +63,11 @@ fn modular_component_desc(
msm: &MaterialStatManifest, msm: &MaterialStatManifest,
description: &str, description: &str,
) -> String { ) -> String {
let mut result = format!( let stats = StatKind::Direct(mc.stats).resolve_stats(msm, components);
"Modular Component\n\n{:?}\n\n{}", let statblock = statblock_desc(&stats);
StatKind::Direct(mc.stats).resolve_stats(msm, components), let mut result = format!("Modular Component\n\n{}\n\n{}", statblock, description);
description
);
if !components.is_empty() { if !components.is_empty() {
result += "Made from:\n"; result += "\n\nMade from:\n";
for component in components { for component in components {
result += component.name(); result += component.name();
result += "\n" 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 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) } 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 // 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 poise_strength = tool.base_poise_strength();
let hands = match tool.hands { let hands = match tool.hands {
Hands::One => "One", Hands::One => "One",
Hands::Two => "Two", Hands::Two => "Two",
}; };
let speed = tool.base_speed(msm, components);
let mut result = format!( let mut result = format!("{}-Handed {}\n\n", hands, kind);
"{}-Handed {}\n\nDPS: {:0.1}\n\nPower: {:0.1}\n\nSpeed: {:0.1}\n\n", result += &statblock_desc(&stats);
// 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
);
if !components.is_empty() { if !components.is_empty() {
result += "Made from:\n"; result += "Made from:\n";
for component in components { for component in components {
@ -188,6 +185,19 @@ fn tool_desc(tool: &Tool, components: &[Item], msm: &MaterialStatManifest, desc:
result 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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -234,11 +244,29 @@ mod tests {
#[test] #[test]
fn test_ingredient_desc() { 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!( assert_eq!(
"Crafting Ingredient\n\nmushrooms", "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
)
); );
} }