From df13741be9785df22b0b8f8d2a27d203ab491f37 Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 6 Jun 2022 17:48:39 -0400 Subject: [PATCH 01/28] Durability system in place --- common/src/combat.rs | 20 +++++-- common/src/comp/inventory/item/armor.rs | 14 +++-- common/src/comp/inventory/item/mod.rs | 41 ++++++++++++-- common/src/comp/inventory/item/modular.rs | 29 +++++++--- common/src/comp/inventory/item/tool.rs | 68 +++++++++++++---------- common/src/comp/poise.rs | 4 +- common/src/states/utils.rs | 14 +++-- voxygen/src/hud/diary.rs | 8 ++- voxygen/src/hud/util.rs | 2 +- voxygen/src/ui/widgets/item_tooltip.rs | 12 ++-- 10 files changed, 146 insertions(+), 66 deletions(-) diff --git a/common/src/combat.rs b/common/src/combat.rs index 08f022aeb6..fd9f593ed5 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -1230,7 +1230,7 @@ fn weapon_rating(item: &T, _msm: &MaterialStatManifest) -> f32 { const BUFF_STRENGTH_WEIGHT: f32 = 1.5; let rating = if let ItemKind::Tool(tool) = &*item.kind() { - let stats = tool.stats; + let stats = tool.stats(item.stats_durability_multiplier()); // 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 @@ -1361,7 +1361,9 @@ pub fn compute_crit_mult(inventory: Option<&Inventory>, msm: &MaterialStatManife inv.equipped_items() .filter_map(|item| { if let ItemKind::Armor(armor) = &*item.kind() { - armor.stats(msm).crit_power + armor + .stats(msm, item.stats_durability_multiplier()) + .crit_power } else { None } @@ -1379,7 +1381,9 @@ pub fn compute_energy_reward_mod(inventory: Option<&Inventory>, msm: &MaterialSt inv.equipped_items() .filter_map(|item| { if let ItemKind::Armor(armor) = &*item.kind() { - armor.stats(msm).energy_reward + armor + .stats(msm, item.stats_durability_multiplier()) + .energy_reward } else { None } @@ -1397,7 +1401,9 @@ pub fn compute_max_energy_mod(inventory: Option<&Inventory>, msm: &MaterialStatM inv.equipped_items() .filter_map(|item| { if let ItemKind::Armor(armor) = &*item.kind() { - armor.stats(msm).energy_max + armor + .stats(msm, item.stats_durability_multiplier()) + .energy_max } else { None } @@ -1433,7 +1439,7 @@ pub fn stealth_multiplier_from_items( inv.equipped_items() .filter_map(|item| { if let ItemKind::Armor(armor) = &*item.kind() { - armor.stats(msm).stealth + armor.stats(msm, item.stats_durability_multiplier()).stealth } else { None } @@ -1456,7 +1462,9 @@ pub fn compute_protection( inv.equipped_items() .filter_map(|item| { if let ItemKind::Armor(armor) = &*item.kind() { - armor.stats(msm).protection + armor + .stats(msm, item.stats_durability_multiplier()) + .protection } else { None } diff --git a/common/src/comp/inventory/item/armor.rs b/common/src/comp/inventory/item/armor.rs index 662fa2a9da..1c876e63ce 100644 --- a/common/src/comp/inventory/item/armor.rs +++ b/common/src/comp/inventory/item/armor.rs @@ -1,5 +1,5 @@ use crate::{ - comp::item::{MaterialStatManifest, Rgb}, + comp::item::{DurabilityMultiplier, MaterialStatManifest, Rgb}, terrain::{Block, BlockKind}, }; use serde::{Deserialize, Serialize}; @@ -212,8 +212,12 @@ pub struct Armor { impl Armor { pub fn new(kind: ArmorKind, stats: StatsSource) -> Self { Self { kind, stats } } - pub fn stats(&self, msm: &MaterialStatManifest) -> Stats { - match &self.stats { + pub fn stats( + &self, + msm: &MaterialStatManifest, + durability_multiplier: DurabilityMultiplier, + ) -> Stats { + let base_stats = match &self.stats { StatsSource::Direct(stats) => *stats, StatsSource::FromSet(set) => { let set_stats = msm.armor_stats(set).unwrap_or_else(Stats::none); @@ -237,7 +241,9 @@ impl Armor { set_stats * multiplier }, - } + }; + let DurabilityMultiplier(mult) = durability_multiplier; + base_stats * mult } #[cfg(test)] diff --git a/common/src/comp/inventory/item/mod.rs b/common/src/comp/inventory/item/mod.rs index 10f9879606..c401a08d66 100644 --- a/common/src/comp/inventory/item/mod.rs +++ b/common/src/comp/inventory/item/mod.rs @@ -396,6 +396,10 @@ pub struct Item { slots: Vec, item_config: Option>, hash: u64, + /// Tracks how many deaths occurred while item was equipped, which is + /// converted into the items durability. Only tracked for tools and armor + /// currently. + durability: Option, } use std::hash::{Hash, Hasher}; @@ -609,7 +613,8 @@ impl TryFrom<(&Item, &AbilityMap, &MaterialStatManifest)> for ItemConfig { }; 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) + set.clone() + .modified_by_tool(tool, item.stats_durability_multiplier()) } else { error!( "Custom ability set: {:?} references non-existent set, falling back to \ @@ -619,7 +624,8 @@ 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) + set.clone() + .modified_by_tool(tool, item.stats_durability_multiplier()) } else { error!( "No ability set defined for tool: {:?}, falling back to default ability set.", @@ -785,6 +791,7 @@ impl Item { // These fields are updated immediately below item_config: None, hash: 0, + durability: None, }; item.update_item_state(ability_map, msm); item @@ -1089,7 +1096,7 @@ impl Item { ItemBase::Modular(mod_base) => { // TODO: Try to move further upward let msm = MaterialStatManifest::load().read(); - mod_base.kind(self.components(), &msm) + mod_base.kind(self.components(), &msm, self.stats_durability_multiplier()) }, } } @@ -1188,6 +1195,18 @@ impl Item { } } + pub fn stats_durability_multiplier(&self) -> DurabilityMultiplier { + const MAX_DURABILITY: u32 = 8; + let durability = self.durability.map_or(0, |x| x.clamp(0, MAX_DURABILITY)); + const DURABILITY_THRESHOLD: u32 = 4; + const MIN_FRAC: f32 = 0.25; + let mult = durability.saturating_sub(DURABILITY_THRESHOLD) as f32 + / (MAX_DURABILITY - DURABILITY_THRESHOLD) as f32 + * (1.0 - MIN_FRAC) + + MIN_FRAC; + DurabilityMultiplier(mult) + } + #[cfg(test)] pub fn create_test_item_from_kind(kind: ItemKind) -> Self { let ability_map = &AbilityMap::load().read(); @@ -1211,10 +1230,9 @@ pub trait ItemDesc { fn num_slots(&self) -> u16; fn item_definition_id(&self) -> ItemDefinitionId<'_>; fn tags(&self) -> Vec; - fn is_modular(&self) -> bool; - fn components(&self) -> &[Item]; + fn stats_durability_multiplier(&self) -> DurabilityMultiplier; fn tool_info(&self) -> Option { if let ItemKind::Tool(tool) = &*self.kind() { @@ -1243,6 +1261,10 @@ impl ItemDesc for Item { fn is_modular(&self) -> bool { self.is_modular() } fn components(&self) -> &[Item] { self.components() } + + fn stats_durability_multiplier(&self) -> DurabilityMultiplier { + self.stats_durability_multiplier() + } } impl ItemDesc for ItemDef { @@ -1265,6 +1287,8 @@ impl ItemDesc for ItemDef { fn is_modular(&self) -> bool { false } fn components(&self) -> &[Item] { &[] } + + fn stats_durability_multiplier(&self) -> DurabilityMultiplier { DurabilityMultiplier(1.0) } } impl Component for Item { @@ -1278,6 +1302,9 @@ impl Component for ItemDrop { type Storage = DenseVecStorage; } +#[derive(Copy, Clone, Debug)] +pub struct DurabilityMultiplier(pub f32); + impl<'a, T: ItemDesc + ?Sized> ItemDesc for &'a T { fn description(&self) -> &str { (*self).description() } @@ -1296,6 +1323,10 @@ impl<'a, T: ItemDesc + ?Sized> ItemDesc for &'a T { fn is_modular(&self) -> bool { (*self).is_modular() } fn components(&self) -> &[Item] { (*self).components() } + + fn stats_durability_multiplier(&self) -> DurabilityMultiplier { + (*self).stats_durability_multiplier() + } } /// Returns all item asset specifiers diff --git a/common/src/comp/inventory/item/modular.rs b/common/src/comp/inventory/item/modular.rs index 1d4fcfd40d..da29ede36a 100644 --- a/common/src/comp/inventory/item/modular.rs +++ b/common/src/comp/inventory/item/modular.rs @@ -1,7 +1,8 @@ use super::{ armor, - tool::{self, AbilityMap, AbilitySpec, Hands}, - Item, ItemBase, ItemDef, ItemDesc, ItemKind, ItemTag, Material, Quality, ToolKind, + tool::{self, AbilityMap, AbilitySpec, Hands, Tool}, + DurabilityMultiplier, Item, ItemBase, ItemDef, ItemDesc, ItemKind, ItemTag, Material, Quality, + ToolKind, }; use crate::{ assets::{self, Asset, AssetExt, AssetHandle}, @@ -98,8 +99,17 @@ impl ModularBase { hand_restriction.unwrap_or(Hands::One) } - pub fn kind(&self, components: &[Item], msm: &MaterialStatManifest) -> Cow { - fn resolve_stats(components: &[Item], msm: &MaterialStatManifest) -> tool::Stats { + pub(super) fn kind( + &self, + components: &[Item], + msm: &MaterialStatManifest, + durability_multiplier: DurabilityMultiplier, + ) -> Cow { + fn resolve_stats( + components: &[Item], + msm: &MaterialStatManifest, + durability_multiplier: DurabilityMultiplier, + ) -> tool::Stats { components .iter() .filter_map(|comp| { @@ -110,6 +120,7 @@ impl ModularBase { } }) .fold(tool::Stats::one(), |a, b| a * b) + * durability_multiplier } let toolkind = components @@ -124,11 +135,11 @@ impl ModularBase { .unwrap_or(ToolKind::Empty); match self { - ModularBase::Tool => Cow::Owned(ItemKind::Tool(tool::Tool { - kind: toolkind, - hands: Self::resolve_hands(components), - stats: resolve_stats(components, msm), - })), + ModularBase::Tool => Cow::Owned(ItemKind::Tool(Tool::new( + toolkind, + Self::resolve_hands(components), + resolve_stats(components, msm, durability_multiplier), + ))), } } diff --git a/common/src/comp/inventory/item/tool.rs b/common/src/comp/inventory/item/tool.rs index f1fe2f7ef3..3309725771 100644 --- a/common/src/comp/inventory/item/tool.rs +++ b/common/src/comp/inventory/item/tool.rs @@ -3,14 +3,13 @@ use crate::{ assets::{self, Asset, AssetExt, AssetHandle}, - comp::{ability::Stance, skills::Skill, CharacterAbility, SkillSet}, + comp::{ + ability::Stance, item::DurabilityMultiplier, skills::Skill, CharacterAbility, SkillSet, + }, }; use hashbrown::HashMap; use serde::{Deserialize, Serialize}; -use std::{ - ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub}, - time::Duration, -}; +use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Sub}; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Ord, PartialOrd)] pub enum ToolKind { @@ -171,9 +170,11 @@ impl Add for Stats { } } } + impl AddAssign for Stats { fn add_assign(&mut self, other: Stats) { *self = *self + other; } } + impl Sub for Stats { type Output = Self; @@ -190,6 +191,7 @@ impl Sub for Stats { } } } + impl Mul for Stats { type Output = Self; @@ -206,9 +208,29 @@ impl Mul for Stats { } } } + impl MulAssign for Stats { fn mul_assign(&mut self, other: Stats) { *self = *self * other; } } + +impl Mul for Stats { + type Output = Self; + + fn mul(self, value: DurabilityMultiplier) -> Self { + let DurabilityMultiplier(value) = value; + Self { + equip_time_secs: self.equip_time_secs / value.max(0.01), + power: self.power * value, + effect_power: self.effect_power * value, + speed: self.speed * value, + crit_chance: self.crit_chance * value, + range: self.range * value, + energy_efficiency: self.energy_efficiency * value, + buff_strength: self.buff_strength * value, + } + } +} + impl Div for Stats { type Output = Self; @@ -225,15 +247,12 @@ impl Div for Stats { } } } -impl DivAssign for Stats { - fn div_assign(&mut self, scalar: usize) { *self = *self / (scalar as f32); } -} #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Tool { pub kind: ToolKind, pub hands: Hands, - pub stats: Stats, + stats: Stats, // TODO: item specific abilities } @@ -259,24 +278,9 @@ impl Tool { } } - // Keep power between 0.5 and 2.00 - pub fn base_power(&self) -> f32 { self.stats.power } - - pub fn base_effect_power(&self) -> f32 { self.stats.effect_power } - - /// Has floor to prevent infinite durations being created later down due to - /// a divide by zero - pub fn base_speed(&self) -> f32 { self.stats.speed.max(0.1) } - - pub fn base_crit_chance(&self) -> f32 { self.stats.crit_chance } - - pub fn base_range(&self) -> f32 { self.stats.range } - - pub fn base_energy_efficiency(&self) -> f32 { self.stats.energy_efficiency } - - pub fn base_buff_strength(&self) -> f32 { self.stats.buff_strength } - - pub fn equip_time(&self) -> Duration { Duration::from_secs_f32(self.stats.equip_time_secs) } + pub fn stats(&self, durability_multiplier: DurabilityMultiplier) -> Stats { + self.stats * durability_multiplier + } } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -376,10 +380,16 @@ impl AbilityContext { impl AbilitySet { #[must_use] - pub fn modified_by_tool(self, tool: &Tool) -> Self { + pub fn modified_by_tool( + self, + tool: &Tool, + durability_multiplier: DurabilityMultiplier, + ) -> Self { self.map(|a| AbilityItem { id: a.id, - ability: a.ability.adjusted_by_stats(tool.stats), + ability: a + .ability + .adjusted_by_stats(tool.stats(durability_multiplier)), }) } } diff --git a/common/src/comp/poise.rs b/common/src/comp/poise.rs index 2b74f5b63d..4fb69b8932 100644 --- a/common/src/comp/poise.rs +++ b/common/src/comp/poise.rs @@ -270,7 +270,9 @@ impl Poise { inv.equipped_items() .filter_map(|item| { if let ItemKind::Armor(armor) = &*item.kind() { - armor.stats(msm).poise_resilience + armor + .stats(msm, item.stats_durability_multiplier()) + .poise_resilience } else { None } diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 441df70c35..4457f0df51 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -335,7 +335,10 @@ pub fn handle_skating(data: &JoinData, update: &mut StateUpdate) { footwear = data.inventory.and_then(|inv| { inv.equipped(EquipSlot::Armor(ArmorSlot::Feet)) .map(|armor| match armor.kind().as_ref() { - ItemKind::Armor(a) => a.stats(data.msm).ground_contact, + ItemKind::Armor(a) => { + a.stats(data.msm, armor.stats_durability_multiplier()) + .ground_contact + }, _ => Friction::Normal, }) }); @@ -719,7 +722,10 @@ pub fn attempt_wield(data: &JoinData<'_>, update: &mut StateUpdate) { data.inventory .and_then(|inv| inv.equipped(equip_slot)) .and_then(|item| match &*item.kind() { - ItemKind::Tool(tool) => Some(tool.equip_time()), + ItemKind::Tool(tool) => Some(Duration::from_secs_f32( + tool.stats(item.stats_durability_multiplier()) + .equip_time_secs, + )), _ => None, }) }; @@ -1261,7 +1267,7 @@ 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()) + Some(tool.stats(item.stats_durability_multiplier()).crit_chance) } else { None } @@ -1282,7 +1288,7 @@ pub fn get_tool_stats(data: &JoinData<'_>, ai: AbilityInfo) -> tool::Stats { .and_then(|slot| data.inventory.and_then(|inv| inv.equipped(slot))) .and_then(|item| { if let ItemKind::Tool(tool) = &*item.kind() { - Some(tool.stats) + Some(tool.stats(item.stats_durability_multiplier())) } else { None } diff --git a/voxygen/src/hud/diary.rs b/voxygen/src/hud/diary.rs index 814f0633f7..6d690bd3f7 100644 --- a/voxygen/src/hud/diary.rs +++ b/voxygen/src/hud/diary.rs @@ -1211,7 +1211,9 @@ impl<'a> Widget for Diary<'a> { .inventory .equipped(EquipSlot::ActiveMainhand) .and_then(|item| match &*item.kind() { - ItemKind::Tool(tool) => Some(tool.stats), + ItemKind::Tool(tool) => { + Some(tool.stats(item.stats_durability_multiplier())) + }, _ => None, }); @@ -1219,7 +1221,9 @@ impl<'a> Widget for Diary<'a> { .inventory .equipped(EquipSlot::ActiveOffhand) .and_then(|item| match &*item.kind() { - ItemKind::Tool(tool) => Some(tool.stats), + ItemKind::Tool(tool) => { + Some(tool.stats(item.stats_durability_multiplier())) + }, _ => None, }); diff --git a/voxygen/src/hud/util.rs b/voxygen/src/hud/util.rs index c85853aa1c..53215b1820 100644 --- a/voxygen/src/hud/util.rs +++ b/voxygen/src/hud/util.rs @@ -102,7 +102,7 @@ pub fn material_kind_text<'a>(kind: &MaterialKind, i18n: &'a Localization) -> Co pub fn stats_count(item: &dyn ItemDesc, msm: &MaterialStatManifest) -> usize { match &*item.kind() { ItemKind::Armor(armor) => { - let armor_stats = armor.stats(msm); + let armor_stats = armor.stats(msm, item.stats_durability_multiplier()); armor_stats.energy_reward.is_some() as usize + armor_stats.energy_max.is_some() as usize + armor_stats.stealth.is_some() as usize diff --git a/voxygen/src/ui/widgets/item_tooltip.rs b/voxygen/src/ui/widgets/item_tooltip.rs index 7333c32ad8..451f64d0c6 100644 --- a/voxygen/src/ui/widgets/item_tooltip.rs +++ b/voxygen/src/ui/widgets/item_tooltip.rs @@ -583,7 +583,7 @@ impl<'a> Widget for ItemTooltip<'a> { // Stats match &*item.kind() { ItemKind::Tool(tool) => { - let stats = tool.stats; + let stats = tool.stats(item.stats_durability_multiplier()); // Power widget::Text::new(&format!( @@ -671,8 +671,9 @@ impl<'a> Widget for ItemTooltip<'a> { if let Some(equipped_item) = equipped_item { if let ItemKind::Tool(equipped_tool) = &*equipped_item.kind() { - let tool_stats = tool.stats; - let equipped_tool_stats = equipped_tool.stats; + let tool_stats = tool.stats(item.stats_durability_multiplier()); + let equipped_tool_stats = + equipped_tool.stats(equipped_item.stats_durability_multiplier()); let diff = tool_stats - equipped_tool_stats; let power_diff = util::comparison(tool_stats.power, equipped_tool_stats.power); @@ -756,7 +757,7 @@ impl<'a> Widget for ItemTooltip<'a> { } }, ItemKind::Armor(armor) => { - let armor_stats = armor.stats(self.msm); + let armor_stats = armor.stats(self.msm, item.stats_durability_multiplier()); let mut stat_text = |text: String, i: usize| { widget::Text::new(&text) @@ -877,7 +878,8 @@ impl<'a> Widget for ItemTooltip<'a> { if let Some(equipped_item) = equipped_item { if let ItemKind::Armor(equipped_armor) = &*equipped_item.kind() { - let equipped_stats = equipped_armor.stats(self.msm); + let equipped_stats = equipped_armor + .stats(self.msm, equipped_item.stats_durability_multiplier()); let diff = armor_stats - equipped_stats; let protection_diff = util::option_comparison( &armor_stats.protection, From a07e042fa2eea938d282bea466d610693ca0da0d Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 6 Jun 2022 18:45:50 -0400 Subject: [PATCH 02/28] Items now lose durability on death --- common/src/comp/inventory/item/armor.rs | 20 ++++++++++++++++++ common/src/comp/inventory/item/mod.rs | 26 +++++++++++++++++++----- common/src/comp/inventory/loadout.rs | 19 +++++++++++++++++ common/src/comp/inventory/mod.rs | 9 ++++++++ server/src/events/entity_manipulation.rs | 9 +++++++- 5 files changed, 77 insertions(+), 6 deletions(-) diff --git a/common/src/comp/inventory/item/armor.rs b/common/src/comp/inventory/item/armor.rs index 1c876e63ce..1c8efe8458 100644 --- a/common/src/comp/inventory/item/armor.rs +++ b/common/src/comp/inventory/item/armor.rs @@ -25,6 +25,26 @@ pub enum ArmorKind { Bag, } +impl ArmorKind { + pub fn has_durability(self) -> bool { + use ArmorKind::*; + match self { + Shoulder => true, + Chest => true, + Belt => true, + Hand => true, + Pants => true, + Foot => true, + Back => true, + Ring => false, + Neck => false, + Head => false, + Tabard => false, + Bag => false, + } + } +} + impl Armor { /// Determines whether two pieces of armour are superficially equivalent to /// one another (i.e: one may be substituted for the other in crafting diff --git a/common/src/comp/inventory/item/mod.rs b/common/src/comp/inventory/item/mod.rs index c401a08d66..9ab56961c9 100644 --- a/common/src/comp/inventory/item/mod.rs +++ b/common/src/comp/inventory/item/mod.rs @@ -5,11 +5,11 @@ pub mod tool; // Reexports pub use modular::{MaterialStatManifest, ModularBase, ModularComponent}; -pub use tool::{AbilitySet, AbilitySpec, Hands, Tool, ToolKind}; +pub use tool::{AbilityMap, AbilitySet, AbilitySpec, Hands, Tool, ToolKind}; use crate::{ assets::{self, AssetExt, BoxedError, Error}, - comp::inventory::{item::tool::AbilityMap, InvSlot}, + comp::inventory::InvSlot, effect::Effect, recipe::RecipeInput, terrain::Block, @@ -793,6 +793,7 @@ impl Item { hash: 0, durability: None, }; + item.durability = item.has_durability().then_some(0); item.update_item_state(ability_map, msm); item } @@ -1199,14 +1200,29 @@ impl Item { const MAX_DURABILITY: u32 = 8; let durability = self.durability.map_or(0, |x| x.clamp(0, MAX_DURABILITY)); const DURABILITY_THRESHOLD: u32 = 4; - const MIN_FRAC: f32 = 0.25; - let mult = durability.saturating_sub(DURABILITY_THRESHOLD) as f32 - / (MAX_DURABILITY - DURABILITY_THRESHOLD) as f32 + const MIN_FRAC: f32 = 0.2; + let mult = (1.0 + - durability.saturating_sub(DURABILITY_THRESHOLD) as f32 + / (MAX_DURABILITY - DURABILITY_THRESHOLD) as f32) * (1.0 - MIN_FRAC) + MIN_FRAC; DurabilityMultiplier(mult) } + pub fn has_durability(&self) -> bool { + match &*self.kind() { + ItemKind::Tool(_) => true, + ItemKind::Armor(armor) => armor.kind.has_durability(), + _ => false, + } + } + + pub fn apply_durability(&mut self) { + if let Some(durability) = &mut self.durability { + *durability += 1; + } + } + #[cfg(test)] pub fn create_test_item_from_kind(kind: ItemKind) -> Self { let ability_map = &AbilityMap::load().read(); diff --git a/common/src/comp/inventory/loadout.rs b/common/src/comp/inventory/loadout.rs index dd2eee1bc8..f52826668a 100644 --- a/common/src/comp/inventory/loadout.rs +++ b/common/src/comp/inventory/loadout.rs @@ -416,6 +416,25 @@ impl Loadout { } }); } + + /// Increments durability by 1 of all valid items + pub(super) fn apply_durability( + &mut self, + ability_map: &item::tool::AbilityMap, + msm: &item::MaterialStatManifest, + ) { + self.slots + .iter_mut() + .filter(|slot| slot.slot.as_ref().map_or(false, |i| i.has_durability())) + .for_each(|slot| { + if let Some(item) = &mut slot.slot { + item.apply_durability(); + // Update item state after applying durability because stats have potential to + // change from different durability + item.update_item_state(ability_map, msm); + } + }) + } } #[cfg(test)] diff --git a/common/src/comp/inventory/mod.rs b/common/src/comp/inventory/mod.rs index 4eff57e20c..26241b6323 100644 --- a/common/src/comp/inventory/mod.rs +++ b/common/src/comp/inventory/mod.rs @@ -878,6 +878,15 @@ impl Inventory { } }); } + + /// Increments durability of all valid items equipped in loaodut by 1 + pub fn apply_durability( + &mut self, + ability_map: &item::tool::AbilityMap, + msm: &item::MaterialStatManifest, + ) { + self.loadout.apply_durability(ability_map, msm) + } } impl Component for Inventory { diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 7efa635355..55976ca2f5 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -18,7 +18,7 @@ use common::{ comp::{ self, aura, buff, chat::{KillSource, KillType}, - inventory::item::MaterialStatManifest, + inventory::item::{AbilityMap, MaterialStatManifest}, loot_owner::LootOwnerKind, Alignment, Auras, Body, CharacterState, Energy, Group, Health, HealthChange, Inventory, Player, Poise, Pos, SkillSet, Stats, @@ -511,6 +511,13 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, last_change: Healt true }; + // Modify durability on all equipped items + if let Some(mut inventory) = state.ecs().write_storage::().get_mut(entity) { + let ability_map = state.ecs().read_resource::(); + let msm = state.ecs().read_resource::(); + inventory.apply_durability(&ability_map, &msm); + } + if should_delete { if let Some(rtsim_entity) = state .ecs() From c586db8feb73a5363199eb01c322ce3ec4ac6201 Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 6 Jun 2022 21:52:27 -0400 Subject: [PATCH 03/28] Item durability is now persisted --- common/src/comp/inventory/item/mod.rs | 16 ++++++++++ .../src/migrations/V50__item_durability.sql | 1 + server/src/persistence/character.rs | 31 +++++++++++++------ .../src/persistence/character/conversions.rs | 5 ++- server/src/persistence/models.rs | 1 + 5 files changed, 44 insertions(+), 10 deletions(-) create mode 100644 server/src/migrations/V50__item_durability.sql diff --git a/common/src/comp/inventory/item/mod.rs b/common/src/comp/inventory/item/mod.rs index 9ab56961c9..f821bbfc02 100644 --- a/common/src/comp/inventory/item/mod.rs +++ b/common/src/comp/inventory/item/mod.rs @@ -1223,6 +1223,22 @@ impl Item { } } + pub fn persistence_durability(&self) -> Option { + self.durability.map(|x| x.min(i32::MAX as u32) as i32) + } + + pub fn persistence_set_durability(&mut self, value: Option) { + // If changes have been made so that item no longer needs to track durability, + // set to None + if !self.has_durability() { + self.durability = None; + } else { + // Set durability to persisted value, and if item previously had no durability, + // set to Some(0) so that durability will be tracked + self.durability = Some(value.map_or(0, |x| x.max(0) as u32)); + } + } + #[cfg(test)] pub fn create_test_item_from_kind(kind: ItemKind) -> Self { let ability_map = &AbilityMap::load().read(); diff --git a/server/src/migrations/V50__item_durability.sql b/server/src/migrations/V50__item_durability.sql new file mode 100644 index 0000000000..482a175793 --- /dev/null +++ b/server/src/migrations/V50__item_durability.sql @@ -0,0 +1 @@ +ALTER TABLE item ADD durability INTEGER; \ No newline at end of file diff --git a/server/src/persistence/character.rs b/server/src/persistence/character.rs index 71851ec019..e7069b8b69 100644 --- a/server/src/persistence/character.rs +++ b/server/src/persistence/character.rs @@ -70,13 +70,15 @@ pub fn load_items(connection: &Connection, root: i64) -> Result, Persi parent_container_item_id, item_definition_id, stack_size, - position + position, + durability ) AS ( SELECT item_id, parent_container_item_id, item_definition_id, stack_size, - position + position, + durability FROM item WHERE parent_container_item_id = ?1 UNION ALL @@ -84,7 +86,8 @@ pub fn load_items(connection: &Connection, root: i64) -> Result, Persi item.parent_container_item_id, item.item_definition_id, item.stack_size, - item.position + item.position, + item.durability FROM item, items_tree WHERE item.parent_container_item_id = items_tree.item_id ) @@ -100,6 +103,7 @@ pub fn load_items(connection: &Connection, root: i64) -> Result, Persi item_definition_id: row.get(2)?, stack_size: row.get(3)?, position: row.get(4)?, + durability: row.get(5)?, }) })? .filter_map(Result::ok) @@ -390,6 +394,7 @@ pub fn create_character( parent_container_item_id: WORLD_PSEUDO_CONTAINER_ID, item_definition_id: CHARACTER_PSEUDO_CONTAINER_DEF_ID.to_owned(), position: character_id.to_string(), + durability: None, }, Item { stack_size: 1, @@ -397,6 +402,7 @@ pub fn create_character( parent_container_item_id: character_id, item_definition_id: INVENTORY_PSEUDO_CONTAINER_DEF_ID.to_owned(), position: INVENTORY_PSEUDO_CONTAINER_POSITION.to_owned(), + durability: None, }, Item { stack_size: 1, @@ -404,6 +410,7 @@ pub fn create_character( parent_container_item_id: character_id, item_definition_id: LOADOUT_PSEUDO_CONTAINER_DEF_ID.to_owned(), position: LOADOUT_PSEUDO_CONTAINER_POSITION.to_owned(), + durability: None, }, ]; @@ -413,8 +420,9 @@ pub fn create_character( parent_container_item_id, item_definition_id, stack_size, - position) - VALUES (?1, ?2, ?3, ?4, ?5)", + position, + durability) + VALUES (?1, ?2, ?3, ?4, ?5, ?6)", )?; for pseudo_container in pseudo_containers { @@ -424,6 +432,7 @@ pub fn create_character( &pseudo_container.item_definition_id, &pseudo_container.stack_size, &pseudo_container.position, + &pseudo_container.durability, ])?; } drop(stmt); @@ -521,8 +530,9 @@ pub fn create_character( parent_container_item_id, item_definition_id, stack_size, - position) - VALUES (?1, ?2, ?3, ?4, ?5)", + position, + durability) + VALUES (?1, ?2, ?3, ?4, ?5, ?6)", )?; for item in inserts { @@ -532,6 +542,7 @@ pub fn create_character( &item.model.item_definition_id, &item.model.stack_size, &item.model.position, + &item.model.durability, ])?; } drop(stmt); @@ -1048,8 +1059,9 @@ pub fn update( parent_container_item_id, item_definition_id, stack_size, - position) - VALUES (?1, ?2, ?3, ?4, ?5)", + position, + durability) + VALUES (?1, ?2, ?3, ?4, ?5, ?6)", )?; for item in upserted_items.iter() { @@ -1059,6 +1071,7 @@ pub fn update( &item.item_definition_id, &item.stack_size, &item.position, + &item.durability, ])?; } } diff --git a/server/src/persistence/character/conversions.rs b/server/src/persistence/character/conversions.rs index 93bc4055a7..012c70b420 100644 --- a/server/src/persistence/character/conversions.rs +++ b/server/src/persistence/character/conversions.rs @@ -176,6 +176,7 @@ pub fn convert_items_to_database_items( } else { 1 }, + durability: item.persistence_durability(), }, // Continue to remember the atomic, in case we detect an error later and want // to roll back to preserve liveness. @@ -359,6 +360,7 @@ pub fn convert_inventory_from_database_items( item_indices.insert(db_item.item_id, i); let mut item = get_item_from_asset(db_item.item_definition_id.as_str())?; + item.persistence_set_durability(db_item.durability); // NOTE: Since this is freshly loaded, the atomic is *unique.* let comp = item.get_item_id_for_database(); @@ -459,7 +461,8 @@ pub fn convert_loadout_from_database_items( for (i, db_item) in database_items.iter().enumerate() { item_indices.insert(db_item.item_id, i); - let item = get_item_from_asset(db_item.item_definition_id.as_str())?; + let mut item = get_item_from_asset(db_item.item_definition_id.as_str())?; + item.persistence_set_durability(db_item.durability); // NOTE: item id is currently *unique*, so we can store the ID safely. let comp = item.get_item_id_for_database(); diff --git a/server/src/persistence/models.rs b/server/src/persistence/models.rs index 1f339316fb..1a82963cda 100644 --- a/server/src/persistence/models.rs +++ b/server/src/persistence/models.rs @@ -12,6 +12,7 @@ pub struct Item { pub item_definition_id: String, pub stack_size: i32, pub position: String, + pub durability: Option, } pub struct Body { From a555e08d0be4510b0fa64ec4618faaf9bc065784 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 7 Jun 2022 16:05:56 -0400 Subject: [PATCH 04/28] Added durability to item tooltips --- assets/voxygen/i18n/en/common.ftl | 1 + common/src/comp/inventory/item/mod.rs | 25 +++++++++++++++++--- voxygen/src/hud/util.rs | 6 ++++- voxygen/src/ui/widgets/item_tooltip.rs | 32 ++++++++++++++++++++++++++ 4 files changed, 60 insertions(+), 4 deletions(-) diff --git a/assets/voxygen/i18n/en/common.ftl b/assets/voxygen/i18n/en/common.ftl index 4aba5f2d76..6f599dca3e 100644 --- a/assets/voxygen/i18n/en/common.ftl +++ b/assets/voxygen/i18n/en/common.ftl @@ -103,6 +103,7 @@ common-stats-energy_reward = Energy Reward common-stats-crit_power = Crit Power common-stats-stealth = Stealth common-stats-slots = Slots +common-stats-durability = Durability common-material-metal = Metal common-material-wood = Wood common-material-stone = Stone diff --git a/common/src/comp/inventory/item/mod.rs b/common/src/comp/inventory/item/mod.rs index f821bbfc02..e6e88b647d 100644 --- a/common/src/comp/inventory/item/mod.rs +++ b/common/src/comp/inventory/item/mod.rs @@ -772,6 +772,8 @@ impl assets::Asset for RawItemDef { pub struct OperationFailure; impl Item { + pub const MAX_DURABILITY: u32 = 8; + // TODO: consider alternatives such as default abilities that can be added to a // loadout when no weapon is present pub fn empty() -> Self { Item::new_from_asset_expect("common.items.weapons.empty.empty") } @@ -1196,14 +1198,17 @@ impl Item { } } + pub fn durability(&self) -> Option { self.durability.map(|x| x.min(Self::MAX_DURABILITY)) } + pub fn stats_durability_multiplier(&self) -> DurabilityMultiplier { - const MAX_DURABILITY: u32 = 8; - let durability = self.durability.map_or(0, |x| x.clamp(0, MAX_DURABILITY)); + let durability = self + .durability + .map_or(0, |x| x.clamp(0, Self::MAX_DURABILITY)); const DURABILITY_THRESHOLD: u32 = 4; const MIN_FRAC: f32 = 0.2; let mult = (1.0 - durability.saturating_sub(DURABILITY_THRESHOLD) as f32 - / (MAX_DURABILITY - DURABILITY_THRESHOLD) as f32) + / (Self::MAX_DURABILITY - DURABILITY_THRESHOLD) as f32) * (1.0 - MIN_FRAC) + MIN_FRAC; DurabilityMultiplier(mult) @@ -1264,6 +1269,8 @@ pub trait ItemDesc { fn tags(&self) -> Vec; fn is_modular(&self) -> bool; fn components(&self) -> &[Item]; + fn has_durability(&self) -> bool; + fn durability(&self) -> Option; fn stats_durability_multiplier(&self) -> DurabilityMultiplier; fn tool_info(&self) -> Option { @@ -1294,6 +1301,10 @@ impl ItemDesc for Item { fn components(&self) -> &[Item] { self.components() } + fn has_durability(&self) -> bool { self.has_durability() } + + fn durability(&self) -> Option { self.durability() } + fn stats_durability_multiplier(&self) -> DurabilityMultiplier { self.stats_durability_multiplier() } @@ -1320,6 +1331,10 @@ impl ItemDesc for ItemDef { fn components(&self) -> &[Item] { &[] } + fn has_durability(&self) -> bool { false } + + fn durability(&self) -> Option { None } + fn stats_durability_multiplier(&self) -> DurabilityMultiplier { DurabilityMultiplier(1.0) } } @@ -1356,6 +1371,10 @@ impl<'a, T: ItemDesc + ?Sized> ItemDesc for &'a T { fn components(&self) -> &[Item] { (*self).components() } + fn has_durability(&self) -> bool { (*self).has_durability() } + + fn durability(&self) -> Option { (*self).durability() } + fn stats_durability_multiplier(&self) -> DurabilityMultiplier { (*self).stats_durability_multiplier() } diff --git a/voxygen/src/hud/util.rs b/voxygen/src/hud/util.rs index 53215b1820..0dd1abebc1 100644 --- a/voxygen/src/hud/util.rs +++ b/voxygen/src/hud/util.rs @@ -100,7 +100,7 @@ pub fn material_kind_text<'a>(kind: &MaterialKind, i18n: &'a Localization) -> Co } pub fn stats_count(item: &dyn ItemDesc, msm: &MaterialStatManifest) -> usize { - match &*item.kind() { + let mut count = match &*item.kind() { ItemKind::Armor(armor) => { let armor_stats = armor.stats(msm, item.stats_durability_multiplier()); armor_stats.energy_reward.is_some() as usize @@ -118,7 +118,11 @@ pub fn stats_count(item: &dyn ItemDesc, msm: &MaterialStatManifest) -> usize { }, ItemKind::ModularComponent { .. } => 7, _ => 0, + }; + if item.has_durability() { + count += 1; } + count } pub fn line_count(item: &dyn ItemDesc, msm: &MaterialStatManifest, i18n: &Localization) -> usize { diff --git a/voxygen/src/ui/widgets/item_tooltip.rs b/voxygen/src/ui/widgets/item_tooltip.rs index 451f64d0c6..d3d50c664e 100644 --- a/voxygen/src/ui/widgets/item_tooltip.rs +++ b/voxygen/src/ui/widgets/item_tooltip.rs @@ -669,6 +669,24 @@ impl<'a> Widget for ItemTooltip<'a> { 6, ); + // Durability + if let Some(durability) = item.durability() { + const MAX_DURABILITY: u32 = 8; + let durability = MAX_DURABILITY - durability.min(MAX_DURABILITY); + widget::Text::new(&format!( + "{} : {}/{}", + i18n.get("common.stats.durability"), + durability, + MAX_DURABILITY + )) + .graphics_for(id) + .parent(id) + .with_style(self.style.desc) + .color(text_color) + .down_from(state.ids.stats[6], V_PAD_STATS) + .set(state.ids.stats[7], ui); + } + if let Some(equipped_item) = equipped_item { if let ItemKind::Tool(equipped_tool) = &*equipped_item.kind() { let tool_stats = tool.stats(item.stats_durability_multiplier()); @@ -874,6 +892,20 @@ impl<'a> Widget for ItemTooltip<'a> { ), index, ); + index += 1; + } + + if item.has_durability() { + let durability = Item::MAX_DURABILITY - item.durability().unwrap_or(0); + stat_text( + format!( + "{} : {}/{}", + i18n.get("common.stats.durability"), + durability, + Item::MAX_DURABILITY + ), + index, + ); } if let Some(equipped_item) = equipped_item { From c3f5bc13f1071ca04e1983aec1304bc376ee8bd2 Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 18 Jun 2022 17:38:46 -0400 Subject: [PATCH 05/28] Equipment can now be repaired at sprites in town. --- assets/voxygen/i18n/en/hud/crafting.ftl | 9 +- client/src/lib.rs | 28 +++ common/src/comp/controller.rs | 1 + common/src/comp/inventory/item/mod.rs | 2 + common/src/comp/inventory/loadout.rs | 12 ++ common/src/comp/inventory/mod.rs | 14 ++ common/src/terrain/block.rs | 1 + common/src/terrain/sprite.rs | 3 + server/src/events/inventory_manip.rs | 7 + voxygen/src/hud/crafting.rs | 175 +++++++++++------- voxygen/src/hud/mod.rs | 32 +++- voxygen/src/scene/terrain/watcher.rs | 3 + voxygen/src/session/mod.rs | 3 + voxygen/src/ui/widgets/item_tooltip.rs | 4 +- .../settlement/building/archetype/house.rs | 3 +- world/src/site2/plot/workshop.rs | 1 + 16 files changed, 226 insertions(+), 72 deletions(-) diff --git a/assets/voxygen/i18n/en/hud/crafting.ftl b/assets/voxygen/i18n/en/hud/crafting.ftl index a549ae11b3..c65852f348 100644 --- a/assets/voxygen/i18n/en/hud/crafting.ftl +++ b/assets/voxygen/i18n/en/hud/crafting.ftl @@ -26,12 +26,19 @@ hud-crafting-tabs-utility = Utility hud-crafting-tabs-weapon = Weapons hud-crafting-tabs-bag = Bags hud-crafting-tabs-processed_material = Materials +hud-crafting-tabs-repair = Repair hud-crafting-dismantle_title = Dismantling hud-crafting-dismantle_explanation = Hover items in your bag to see what you can salvage. - + Double-Click them to start dismantling. +hud-crafting-repair_title = Repair Items +hud-crafting-repair_explanation = + Repair your damaged and + broken equipment here. + + Double-Click the item to repair it. hud-crafting-modular_desc = Drag Item-Parts here to craft a weapon hud-crafting-mod_weap_prim_slot_title = Primary Weapon Component hud-crafting-mod_weap_prim_slot_desc = Place a primary weapon component here (e.g. a sword blade, axe head, or bow limbs). diff --git a/client/src/lib.rs b/client/src/lib.rs index 3922e419ce..e170b3ee4c 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -1289,6 +1289,34 @@ impl Client { ))); } + /// Repairs the item in the given inventory slot. `sprite_pos` should be + /// the location of a relevant crafting station within range of the player. + pub fn repair_item(&mut self, slot: Slot, sprite_pos: Vec3) -> bool { + let is_repairable = { + let inventories = self.inventories(); + let inventory = inventories.get(self.entity()); + inventory.map_or(false, |inv| { + if let Some(item) = match slot { + Slot::Equip(equip_slot) => inv.equipped(equip_slot), + Slot::Inventory(invslot) => inv.get(invslot), + } { + item.has_durability() + } else { + false + } + }) + }; + if is_repairable { + self.send_msg(ClientGeneral::ControlEvent(ControlEvent::InventoryEvent( + InventoryEvent::CraftRecipe { + craft_event: CraftEvent::Repair(slot), + craft_sprite: Some(sprite_pos), + }, + ))); + } + is_repairable + } + fn update_available_recipes(&mut self) { self.available_recipes = self .recipe_book diff --git a/common/src/comp/controller.rs b/common/src/comp/controller.rs index 29bed25ef4..704f0fa7d5 100644 --- a/common/src/comp/controller.rs +++ b/common/src/comp/controller.rs @@ -105,6 +105,7 @@ pub enum CraftEvent { modifier: Option, slots: Vec<(u32, InvSlotId)>, }, + Repair(Slot), } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] diff --git a/common/src/comp/inventory/item/mod.rs b/common/src/comp/inventory/item/mod.rs index e6e88b647d..990c696ee7 100644 --- a/common/src/comp/inventory/item/mod.rs +++ b/common/src/comp/inventory/item/mod.rs @@ -1244,6 +1244,8 @@ impl Item { } } + pub fn reset_durability(&mut self) { self.durability = self.durability.map(|_| 0); } + #[cfg(test)] pub fn create_test_item_from_kind(kind: ItemKind) -> Self { let ability_map = &AbilityMap::load().read(); diff --git a/common/src/comp/inventory/loadout.rs b/common/src/comp/inventory/loadout.rs index f52826668a..25d45716f3 100644 --- a/common/src/comp/inventory/loadout.rs +++ b/common/src/comp/inventory/loadout.rs @@ -435,6 +435,18 @@ impl Loadout { } }) } + + /// Resets durability of item in specified slot + pub(super) fn repair_item_at_slot(&mut self, equip_slot: EquipSlot) { + if let Some(item) = self + .slots + .iter_mut() + .find(|slot| slot.equip_slot == equip_slot) + .and_then(|slot| slot.slot.as_mut()) + { + item.reset_durability(); + } + } } #[cfg(test)] diff --git a/common/src/comp/inventory/mod.rs b/common/src/comp/inventory/mod.rs index 26241b6323..f10ac9f4c4 100644 --- a/common/src/comp/inventory/mod.rs +++ b/common/src/comp/inventory/mod.rs @@ -887,6 +887,20 @@ impl Inventory { ) { self.loadout.apply_durability(ability_map, msm) } + + /// Resets durability of item in specified slot + pub fn repair_item_at_slot(&mut self, slot: Slot) { + match slot { + Slot::Inventory(invslot) => { + if let Some(Some(item)) = self.slot_mut(invslot).map(Option::as_mut) { + item.reset_durability(); + } + }, + Slot::Equip(equip_slot) => { + self.loadout.repair_item_at_slot(equip_slot); + }, + } + } } impl Component for Inventory { diff --git a/common/src/terrain/block.rs b/common/src/terrain/block.rs index 398720c079..6e8669586b 100644 --- a/common/src/terrain/block.rs +++ b/common/src/terrain/block.rs @@ -291,6 +291,7 @@ impl Block { | SpriteKind::Loom | SpriteKind::SpinningWheel | SpriteKind::DismantlingBench + | SpriteKind::RepairBench | SpriteKind::TanningRack | SpriteKind::Chest | SpriteKind::DungeonChest0 diff --git a/common/src/terrain/sprite.rs b/common/src/terrain/sprite.rs index 74a8f6d205..e15cbb1439 100644 --- a/common/src/terrain/sprite.rs +++ b/common/src/terrain/sprite.rs @@ -240,6 +240,7 @@ make_case_elim!( Keyhole = 0xD7, KeyDoor = 0xD8, CommonLockedChest = 0xD9, + RepairBench = 0xDA, } ); @@ -308,6 +309,7 @@ impl SpriteKind { SpriteKind::CookingPot => 1.36, SpriteKind::DismantlingBench => 1.18, SpriteKind::IceSpike => 1.0, + SpriteKind::RepairBench => 1.2, // TODO: Find suitable heights. SpriteKind::BarrelCactus | SpriteKind::RoundCactus @@ -581,6 +583,7 @@ impl SpriteKind { | SpriteKind::TanningRack | SpriteKind::Loom | SpriteKind::DismantlingBench + | SpriteKind::RepairBench | SpriteKind::ChristmasOrnament | SpriteKind::ChristmasWreath | SpriteKind::WindowArabic diff --git a/server/src/events/inventory_manip.rs b/server/src/events/inventory_manip.rs index 5df2c61dfc..558fe863fb 100644 --- a/server/src/events/inventory_manip.rs +++ b/server/src/events/inventory_manip.rs @@ -856,6 +856,13 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv None } }, + CraftEvent::Repair(slot) => { + let sprite = get_craft_sprite(state, craft_sprite); + if matches!(sprite, Some(SpriteKind::RepairBench)) { + inventory.repair_item_at_slot(slot); + } + None + }, }; // Attempt to insert items into inventory, dropping them if there is not enough diff --git a/voxygen/src/hud/crafting.rs b/voxygen/src/hud/crafting.rs index 8c59e8c715..c344e4514b 100644 --- a/voxygen/src/hud/crafting.rs +++ b/voxygen/src/hud/crafting.rs @@ -86,7 +86,9 @@ widget_ids! { dismantle_title, dismantle_img, dismantle_txt, - dismantle_highlight_txt, + repair_title, + repair_img, + repair_txt, modular_inputs[], modular_art, modular_desc_txt, @@ -122,6 +124,7 @@ pub struct CraftingShow { pub crafting_search_key: Option, pub craft_sprite: Option<(Vec3, SpriteKind)>, pub salvage: bool, + pub repair: bool, // TODO: Maybe try to do something that doesn't need to allocate? pub recipe_inputs: HashMap, } @@ -133,6 +136,7 @@ impl Default for CraftingShow { crafting_search_key: None, craft_sprite: None, salvage: false, + repair: false, recipe_inputs: HashMap::new(), } } @@ -207,7 +211,8 @@ pub enum CraftingTab { Bag, Utility, Glider, - Dismantle, // Needs to be the last one or widget alignment will be messed up + Dismantle, + Repair, } impl CraftingTab { @@ -224,6 +229,7 @@ impl CraftingTab { CraftingTab::Bag => "hud-crafting-tabs-bag", CraftingTab::ProcessedMaterial => "hud-crafting-tabs-processed_material", CraftingTab::Dismantle => "hud-crafting-tabs-dismantle", + CraftingTab::Repair => "hud-crafting-tabs-repair", } } @@ -239,14 +245,16 @@ impl CraftingTab { CraftingTab::Weapon => imgs.icon_weapon, CraftingTab::Bag => imgs.icon_bag, CraftingTab::ProcessedMaterial => imgs.icon_processed_material, - CraftingTab::Dismantle => imgs.icon_dismantle, + // These tabs are never shown, so using not found is fine + CraftingTab::Dismantle => imgs.not_found, + CraftingTab::Repair => imgs.not_found, } } fn satisfies(self, recipe: &Recipe) -> bool { let (item, _count) = &recipe.output; match self { - CraftingTab::All | CraftingTab::Dismantle => true, + CraftingTab::All | CraftingTab::Dismantle | CraftingTab::Repair => true, CraftingTab::Food => item.tags().contains(&ItemTag::Food), CraftingTab::Armor => match &*item.kind() { ItemKind::Armor(_) => !item.tags().contains(&ItemTag::Bag), @@ -271,6 +279,10 @@ impl CraftingTab { }, } } + + // Tells UI whether tab is an adhoc tab that should only sometimes be present + // depending on what station is accessed + fn is_adhoc(self) -> bool { matches!(self, CraftingTab::Dismantle | CraftingTab::Repair) } } pub struct State { @@ -434,56 +446,57 @@ impl<'a> Widget for Crafting<'a> { }) }; let sel_crafting_tab = &self.show.crafting_fields.crafting_tab; - for (i, crafting_tab) in CraftingTab::iter().enumerate() { - if crafting_tab != CraftingTab::Dismantle { - let tab_img = crafting_tab.img_id(self.imgs); - // Button Background - let mut bg = Image::new(self.imgs.pixel) - .w_h(40.0, 30.0) - .color(Some(UI_MAIN)); - if i == 0 { - bg = bg.top_left_with_margins_on(state.ids.window_frame, 50.0, -40.0) - } else { - bg = bg.down_from(state.ids.category_bgs[i - 1], 0.0) - }; - bg.set(state.ids.category_bgs[i], ui); - // Category Button - if Button::image(if crafting_tab == *sel_crafting_tab { - self.imgs.wpn_icon_border_pressed - } else { - self.imgs.wpn_icon_border - }) - .wh_of(state.ids.category_bgs[i]) - .middle_of(state.ids.category_bgs[i]) - .hover_image(if crafting_tab == *sel_crafting_tab { - self.imgs.wpn_icon_border_pressed - } else { - self.imgs.wpn_icon_border_mo - }) - .press_image(if crafting_tab == *sel_crafting_tab { - self.imgs.wpn_icon_border_pressed - } else { - self.imgs.wpn_icon_border_press - }) - .with_tooltip( - self.tooltip_manager, - &self.localized_strings.get_msg(crafting_tab.name_key()), - "", - &tabs_tooltip, - TEXT_COLOR, - ) - .set(state.ids.category_tabs[i], ui) - .was_clicked() - { - events.push(Event::ChangeCraftingTab(crafting_tab)) - }; - // Tab images - Image::new(tab_img) - .middle_of(state.ids.category_tabs[i]) - .w_h(20.0, 20.0) - .graphics_for(state.ids.category_tabs[i]) - .set(state.ids.category_imgs[i], ui); - } + for (i, crafting_tab) in CraftingTab::iter() + .filter(|tab| !tab.is_adhoc()) + .enumerate() + { + let tab_img = crafting_tab.img_id(self.imgs); + // Button Background + let mut bg = Image::new(self.imgs.pixel) + .w_h(40.0, 30.0) + .color(Some(UI_MAIN)); + if i == 0 { + bg = bg.top_left_with_margins_on(state.ids.window_frame, 50.0, -40.0) + } else { + bg = bg.down_from(state.ids.category_bgs[i - 1], 0.0) + }; + bg.set(state.ids.category_bgs[i], ui); + // Category Button + if Button::image(if crafting_tab == *sel_crafting_tab { + self.imgs.wpn_icon_border_pressed + } else { + self.imgs.wpn_icon_border + }) + .wh_of(state.ids.category_bgs[i]) + .middle_of(state.ids.category_bgs[i]) + .hover_image(if crafting_tab == *sel_crafting_tab { + self.imgs.wpn_icon_border_pressed + } else { + self.imgs.wpn_icon_border_mo + }) + .press_image(if crafting_tab == *sel_crafting_tab { + self.imgs.wpn_icon_border_pressed + } else { + self.imgs.wpn_icon_border_press + }) + .with_tooltip( + self.tooltip_manager, + &self.localized_strings.get_msg(crafting_tab.name_key()), + "", + &tabs_tooltip, + TEXT_COLOR, + ) + .set(state.ids.category_tabs[i], ui) + .was_clicked() + { + events.push(Event::ChangeCraftingTab(crafting_tab)) + }; + // Tab images + Image::new(tab_img) + .middle_of(state.ids.category_tabs[i]) + .w_h(20.0, 20.0) + .graphics_for(state.ids.category_tabs[i]) + .set(state.ids.category_imgs[i], ui); } // TODO: Consider UX for filtering searches, maybe a checkbox or a dropdown if @@ -735,12 +748,9 @@ impl<'a> Widget for Crafting<'a> { if state.selected_recipe.as_ref() == Some(name) { state.update(|s| s.selected_recipe = None); } else { - if matches!( - self.show.crafting_fields.crafting_tab, - CraftingTab::Dismantle - ) { - // If current tab is dismantle, and recipe is selected, change to general - // tab, as in dismantle tab recipe gets deselected + if self.show.crafting_fields.crafting_tab.is_adhoc() { + // If current tab is an adhoc tab, and recipe is selected, change to general + // tab events.push(Event::ChangeCraftingTab(CraftingTab::All)); } state.update(|s| s.selected_recipe = Some(name.clone())); @@ -802,12 +812,9 @@ impl<'a> Widget for Crafting<'a> { } } - // Deselect recipe if current tab is dismantle, elsewhere if recipe selected - // while dismantling, tab is changed to general - if matches!( - self.show.crafting_fields.crafting_tab, - CraftingTab::Dismantle - ) { + // Deselect recipe if current tab is an adhoc tab, elsewhere if recipe selected + // while in an adhoc tab, tab is changed to general + if self.show.crafting_fields.crafting_tab.is_adhoc() { state.update(|s| s.selected_recipe = None); } @@ -1854,6 +1861,42 @@ impl<'a> Widget for Crafting<'a> { .color(TEXT_COLOR) .parent(state.ids.window) .set(state.ids.dismantle_txt, ui); + } else if *sel_crafting_tab == CraftingTab::Repair { + // Title + Text::new(&self.localized_strings.get_msg("hud-crafting-repair_title")) + .mid_top_with_margin_on(state.ids.align_ing, 0.0) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(24)) + .color(TEXT_COLOR) + .parent(state.ids.window) + .set(state.ids.repair_title, ui); + + // Bench Icon + let size = 140.0; + Image::new(animate_by_pulse( + &self + .item_imgs + .img_ids_or_not_found_img(ItemKey::Simple("RepairBench".to_string())), + self.pulse, + )) + .wh([size; 2]) + .mid_top_with_margin_on(state.ids.align_ing, 50.0) + .parent(state.ids.align_ing) + .set(state.ids.repair_img, ui); + + // Explanation + + Text::new( + &self + .localized_strings + .get_msg("hud-crafting-repair_explanation"), + ) + .mid_bottom_with_margin_on(state.ids.repair_img, -60.0) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(14)) + .color(TEXT_COLOR) + .parent(state.ids.window) + .set(state.ids.repair_txt, ui); } // Search / Title Recipes diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 375a8f0a73..669fe0ca68 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -87,7 +87,11 @@ use common::{ self, ability::{AuxiliaryAbility, Stance}, fluid_dynamics, - inventory::{slot::InvSlotId, trade_pricing::TradePricing, CollectFailedReason}, + inventory::{ + slot::{InvSlotId, Slot}, + trade_pricing::TradePricing, + CollectFailedReason, + }, item::{ tool::{AbilityContext, ToolKind}, ItemDesc, MaterialStatManifest, Quality, @@ -721,6 +725,10 @@ pub enum Event { modifier: Option, craft_sprite: Option>, }, + RepairItem { + slot: Slot, + sprite_pos: Vec3, + }, InviteMember(Uid), AcceptInvite, DeclineInvite, @@ -914,6 +922,8 @@ impl Show { self.bag = open; self.map = false; self.crafting_fields.salvage = false; + self.crafting_fields.repair = false; + if !open { self.crafting = false; } @@ -937,6 +947,7 @@ impl Show { self.bag = false; self.crafting = false; self.crafting_fields.salvage = false; + self.crafting_fields.repair = false; self.social = false; self.quest = false; self.diary = false; @@ -973,6 +984,7 @@ impl Show { } self.crafting = open; self.crafting_fields.salvage = false; + self.crafting_fields.repair = false; self.crafting_fields.recipe_inputs = HashMap::new(); self.bag = open; self.map = false; @@ -992,6 +1004,10 @@ impl Show { self.crafting_fields.craft_sprite, Some((_, SpriteKind::DismantlingBench)) ) && matches!(tab, CraftingTab::Dismantle); + self.crafting_fields.repair = matches!( + self.crafting_fields.craft_sprite, + Some((_, SpriteKind::RepairBench)) + ) && matches!(tab, CraftingTab::Repair); } fn diary(&mut self, open: bool) { @@ -1000,6 +1016,7 @@ impl Show { self.quest = false; self.crafting = false; self.crafting_fields.salvage = false; + self.crafting_fields.repair = false; self.bag = false; self.map = false; self.diary_fields = diary::DiaryShow::default(); @@ -1020,6 +1037,7 @@ impl Show { self.quest = false; self.crafting = false; self.crafting_fields.salvage = false; + self.crafting_fields.repair = false; self.diary = false; self.want_grab = !self.any_window_requires_cursor(); } @@ -3688,7 +3706,6 @@ impl Hud { // Maintain slot manager 'slot_events: for event in self.slot_manager.maintain(ui_widgets) { - use comp::slot::Slot; use slots::{AbilitySlot, InventorySlot, SlotKind::*}; let to_slot = |slot_kind| match slot_kind { Inventory(InventorySlot { @@ -3920,6 +3937,17 @@ impl Hud { { events.push(Event::SalvageItem { slot, salvage_pos }) } + } else if self.show.crafting_fields.repair + && matches!(self.show.crafting_fields.crafting_tab, CraftingTab::Repair) + { + if let Some((sprite_pos, _sprite_kind)) = + self.show.crafting_fields.craft_sprite + { + events.push(Event::RepairItem { + slot: from, + sprite_pos, + }) + } } else { events.push(Event::UseSlot { slot: from, diff --git a/voxygen/src/scene/terrain/watcher.rs b/voxygen/src/scene/terrain/watcher.rs index b783cb07cc..d73ee86a95 100644 --- a/voxygen/src/scene/terrain/watcher.rs +++ b/voxygen/src/scene/terrain/watcher.rs @@ -165,6 +165,9 @@ impl BlocksOfInterest { fires.push(pos); interactables.push((pos, Interaction::Craft(CraftingTab::Dismantle))) }, + Some(SpriteKind::RepairBench) => { + interactables.push((pos, Interaction::Craft(CraftingTab::Repair))) + }, _ => {}, }, } diff --git a/voxygen/src/session/mod.rs b/voxygen/src/session/mod.rs index 4aef117d82..7c366d4fdf 100644 --- a/voxygen/src/session/mod.rs +++ b/voxygen/src/session/mod.rs @@ -1777,6 +1777,9 @@ impl PlayState for SessionState { HudEvent::SalvageItem { slot, salvage_pos } => { self.client.borrow_mut().salvage_item(slot, salvage_pos); }, + HudEvent::RepairItem { slot, sprite_pos } => { + self.client.borrow_mut().repair_item(slot, sprite_pos); + }, HudEvent::InviteMember(uid) => { self.client.borrow_mut().send_invite(uid, InviteKind::Group); }, diff --git a/voxygen/src/ui/widgets/item_tooltip.rs b/voxygen/src/ui/widgets/item_tooltip.rs index d3d50c664e..c45868afa7 100644 --- a/voxygen/src/ui/widgets/item_tooltip.rs +++ b/voxygen/src/ui/widgets/item_tooltip.rs @@ -675,7 +675,7 @@ impl<'a> Widget for ItemTooltip<'a> { let durability = MAX_DURABILITY - durability.min(MAX_DURABILITY); widget::Text::new(&format!( "{} : {}/{}", - i18n.get("common.stats.durability"), + i18n.get_msg("common-stats-durability"), durability, MAX_DURABILITY )) @@ -900,7 +900,7 @@ impl<'a> Widget for ItemTooltip<'a> { stat_text( format!( "{} : {}/{}", - i18n.get("common.stats.durability"), + i18n.get_msg("common-stats-durability"), durability, Item::MAX_DURABILITY ), diff --git a/world/src/site/settlement/building/archetype/house.rs b/world/src/site/settlement/building/archetype/house.rs index 074b442343..6904f5093b 100644 --- a/world/src/site/settlement/building/archetype/house.rs +++ b/world/src/site/settlement/building/archetype/house.rs @@ -536,7 +536,7 @@ impl Archetype for House { center_offset.x, center_offset.y, z + 100, - )) % 13 + )) % 14 { 0..=1 => SpriteKind::Crate, 2 => SpriteKind::Bench, @@ -550,6 +550,7 @@ impl Archetype for House { 10 => SpriteKind::SpinningWheel, 11 => SpriteKind::TanningRack, 12 => SpriteKind::DismantlingBench, + 13 => SpriteKind::RepairBench, _ => unreachable!(), }; diff --git a/world/src/site2/plot/workshop.rs b/world/src/site2/plot/workshop.rs index 9ac64acb1f..dc06b9601d 100644 --- a/world/src/site2/plot/workshop.rs +++ b/world/src/site2/plot/workshop.rs @@ -128,6 +128,7 @@ impl Structure for Workshop { SpriteKind::Loom, SpriteKind::Anvil, SpriteKind::DismantlingBench, + SpriteKind::RepairBench, ]; 'outer: for d in 0..3 { for dir in CARDINALS { From 0966753699195bbbd489200d421f27926538bde4 Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 17 Jul 2022 15:33:14 -0400 Subject: [PATCH 06/28] Added repair recipes --- assets/common/repair_recipe_book.ron | 79 ++++++++ client/src/lib.rs | 20 +- common/net/src/msg/server.rs | 3 +- common/src/comp/controller.rs | 5 +- common/src/recipe.rs | 293 ++++++++++++++++++++++----- server/src/events/inventory_manip.rs | 15 +- server/src/sys/msg/register.rs | 3 +- voxygen/src/hud/mod.rs | 4 +- voxygen/src/session/mod.rs | 23 ++- 9 files changed, 383 insertions(+), 62 deletions(-) create mode 100644 assets/common/repair_recipe_book.ron diff --git a/assets/common/repair_recipe_book.ron b/assets/common/repair_recipe_book.ron new file mode 100644 index 0000000000..789e4b56cd --- /dev/null +++ b/assets/common/repair_recipe_book.ron @@ -0,0 +1,79 @@ +( +recipes: { + // Weapons + ItemDefId("common.items.weapons.sword.starter"): ( inputs: [], ), + ItemDefId("common.items.weapons.axe.starter_axe"): ( inputs: [], ), + ItemDefId("common.items.weapons.hammer.starter_hammer"): ( inputs: [], ), + ItemDefId("common.items.weapons.bow.starter"): ( inputs: [], ), + ItemDefId("common.items.weapons.staff.starter_staff"): ( inputs: [], ), + ItemDefId("common.items.weapons.sceptre.starter_sceptre"): ( inputs: [], ), + ItemDefId("common.items.weapons.sword_1h.starter"): ( inputs: [], ), + ModularWeapon(material: "common.items.mineral.ingot.bronze"): ( + inputs: [ + (Item("common.items.mineral.ingot.bronze"), 1), + ], + ), + ModularWeapon(material: "common.items.mineral.ingot.iron"): ( + inputs: [ + (Item("common.items.mineral.ingot.iron"), 1), + ], + ), + ModularWeapon(material: "common.items.mineral.ingot.steel"): ( + inputs: [ + (Item("common.items.mineral.ingot.steel"), 1), + ], + ), + ModularWeapon(material: "common.items.mineral.ingot.cobalt"): ( + inputs: [ + (Item("common.items.mineral.ingot.cobalt"), 1), + ], + ), + ModularWeapon(material: "common.items.mineral.ingot.bloodsteel"): ( + inputs: [ + (Item("common.items.mineral.ingot.bloodsteel"), 1), + ], + ), + ModularWeapon(material: "common.items.mineral.ingot.orichalcum"): ( + inputs: [ + (Item("common.items.mineral.ingot.orichalcum"), 1), + ], + ), + ModularWeapon(material: "common.items.log.wood"): ( + inputs: [ + (Item("common.items.log.wood"), 1), + ], + ), + ModularWeapon(material: "common.items.log.bamboo"): ( + inputs: [ + (Item("common.items.log.bamboo"), 1), + ], + ), + ModularWeapon(material: "common.items.log.hardwood"): ( + inputs: [ + (Item("common.items.log.hardwood"), 1), + ], + ), + ModularWeapon(material: "common.items.log.ironwood"): ( + inputs: [ + (Item("common.items.log.ironwood"), 1), + ], + ), + ModularWeapon(material: "common.items.log.frostwood"): ( + inputs: [ + (Item("common.items.log.frostwood"), 1), + ], + ), + ModularWeapon(material: "common.items.log.eldwood"): ( + inputs: [ + (Item("common.items.log.eldwood"), 1), + ], + ), + // Armor + +}, +fallback: ( + inputs: [ + (Item("common.items.utility.coins"), 1000), + ], +), +) \ No newline at end of file diff --git a/client/src/lib.rs b/client/src/lib.rs index e170b3ee4c..f7c527bb36 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -38,7 +38,7 @@ use common::{ lod, mounting::Rider, outcome::Outcome, - recipe::{ComponentRecipeBook, RecipeBook}, + recipe::{ComponentRecipeBook, RecipeBook, RepairRecipeBook}, resources::{GameMode, PlayerEntity, Time, TimeOfDay}, shared_server_config::ServerConstants, spiral::Spiral2d, @@ -222,6 +222,7 @@ pub struct Client { pub chat_mode: ChatMode, recipe_book: RecipeBook, component_recipe_book: ComponentRecipeBook, + repair_recipe_book: RepairRecipeBook, available_recipes: HashMap>, lod_zones: HashMap, lod::Zone>, lod_last_requested: Option, @@ -362,6 +363,7 @@ impl Client { material_stats, ability_map, server_constants, + repair_recipe_book, } = loop { tokio::select! { // Spawn in a blocking thread (leaving the network thread free). This is mostly @@ -655,6 +657,7 @@ impl Client { world_map.pois, recipe_book, component_recipe_book, + repair_recipe_book, max_group_size, client_timeout, )) @@ -670,6 +673,7 @@ impl Client { pois, recipe_book, component_recipe_book, + repair_recipe_book, max_group_size, client_timeout, ) = loop { @@ -708,6 +712,7 @@ impl Client { pois, recipe_book, component_recipe_book, + repair_recipe_book, available_recipes: HashMap::default(), chat_mode: ChatMode::default(), @@ -1146,6 +1151,8 @@ impl Client { pub fn component_recipe_book(&self) -> &ComponentRecipeBook { &self.component_recipe_book } + pub fn repair_recipe_book(&self) -> &RepairRecipeBook { &self.repair_recipe_book } + pub fn available_recipes(&self) -> &HashMap> { &self.available_recipes } @@ -1291,12 +1298,17 @@ impl Client { /// Repairs the item in the given inventory slot. `sprite_pos` should be /// the location of a relevant crafting station within range of the player. - pub fn repair_item(&mut self, slot: Slot, sprite_pos: Vec3) -> bool { + pub fn repair_item( + &mut self, + item: Slot, + slots: Vec<(u32, InvSlotId)>, + sprite_pos: Vec3, + ) -> bool { let is_repairable = { let inventories = self.inventories(); let inventory = inventories.get(self.entity()); inventory.map_or(false, |inv| { - if let Some(item) = match slot { + if let Some(item) = match item { Slot::Equip(equip_slot) => inv.equipped(equip_slot), Slot::Inventory(invslot) => inv.get(invslot), } { @@ -1309,7 +1321,7 @@ impl Client { if is_repairable { self.send_msg(ClientGeneral::ControlEvent(ControlEvent::InventoryEvent( InventoryEvent::CraftRecipe { - craft_event: CraftEvent::Repair(slot), + craft_event: CraftEvent::Repair { item, slots }, craft_sprite: Some(sprite_pos), }, ))); diff --git a/common/net/src/msg/server.rs b/common/net/src/msg/server.rs index db6c3655b5..4d158d5345 100644 --- a/common/net/src/msg/server.rs +++ b/common/net/src/msg/server.rs @@ -10,7 +10,7 @@ use common::{ event::UpdateCharacterMetadata, lod, outcome::Outcome, - recipe::{ComponentRecipeBook, RecipeBook}, + recipe::{ComponentRecipeBook, RecipeBook, RepairRecipeBook}, resources::{Time, TimeOfDay}, shared_server_config::ServerConstants, terrain::{Block, TerrainChunk, TerrainChunkMeta, TerrainChunkSize}, @@ -65,6 +65,7 @@ pub enum ServerInit { world_map: crate::msg::world_msg::WorldMapMsg, recipe_book: RecipeBook, component_recipe_book: ComponentRecipeBook, + repair_recipe_book: RepairRecipeBook, material_stats: MaterialStatManifest, ability_map: comp::item::tool::AbilityMap, server_constants: ServerConstants, diff --git a/common/src/comp/controller.rs b/common/src/comp/controller.rs index 704f0fa7d5..bdde986394 100644 --- a/common/src/comp/controller.rs +++ b/common/src/comp/controller.rs @@ -105,7 +105,10 @@ pub enum CraftEvent { modifier: Option, slots: Vec<(u32, InvSlotId)>, }, - Repair(Slot), + Repair { + item: Slot, + slots: Vec<(u32, InvSlotId)>, + }, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] diff --git a/common/src/recipe.rs b/common/src/recipe.rs index 9d6fa5a8a6..0f6dbe1079 100644 --- a/common/src/recipe.rs +++ b/common/src/recipe.rs @@ -1,11 +1,12 @@ use crate::{ assets::{self, AssetExt, AssetHandle}, comp::{ - inventory::slot::InvSlotId, + inventory::slot::{InvSlotId, Slot}, item::{ modular, tool::{AbilityMap, ToolKind}, - ItemBase, ItemDef, ItemDefinitionIdOwned, ItemKind, ItemTag, MaterialStatManifest, + ItemBase, ItemDef, ItemDefinitionId, ItemDefinitionIdOwned, ItemKind, ItemTag, + MaterialStatManifest, }, Inventory, Item, }, @@ -39,6 +40,45 @@ pub enum RecipeInput { ListSameItem(Vec>), } +impl RecipeInput { + fn handle_requirement<'a, I: Iterator>( + &'a self, + amount: u32, + slot_claims: &mut HashMap, + unsatisfied_requirements: &mut Vec<(&'a RecipeInput, u32)>, + inv: &Inventory, + input_slots: I, + ) { + let mut required = amount; + // contains_any check used for recipes that have an input that is not consumed, + // e.g. craftsman hammer + // Goes through each slot and marks some amount from each slot as claimed + let contains_any = input_slots.into_iter().all(|slot| { + // Checks that the item in the slot can be used for the input + if let Some(item) = inv + .get(slot) + .filter(|item| item.matches_recipe_input(self, amount)) + { + // Gets the number of items claimed from the slot, or sets to 0 if slot has + // not been claimed by another input yet + let claimed = slot_claims.entry(slot).or_insert(0); + let available = item.amount().saturating_sub(*claimed); + let provided = available.min(required); + required -= provided; + *claimed += provided; + true + } else { + false + } + }); + // If there were not sufficient items to cover requirement between all provided + // slots, or if non-consumed item was not present, mark input as not satisfied + if required > 0 || !contains_any { + unsatisfied_requirements.push((self, required)); + } + } +} + #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Recipe { pub output: (Arc, u32), @@ -594,65 +634,26 @@ impl ComponentRecipe { let mut slot_claims = HashMap::new(); let mut unsatisfied_requirements = Vec::new(); - fn handle_requirement<'a, I: Iterator>( - slot_claims: &mut HashMap, - unsatisfied_requirements: &mut Vec<(&'a RecipeInput, u32)>, - inv: &Inventory, - input: &'a RecipeInput, - amount: u32, - input_slots: I, - ) { - let mut required = amount; - // contains_any check used for recipes that have an input that is not consumed, - // e.g. craftsman hammer - // Goes through each slot and marks some amount from each slot as claimed - let contains_any = input_slots.into_iter().all(|slot| { - // Checks that the item in the slot can be used for the input - if let Some(item) = inv - .get(slot) - .filter(|item| item.matches_recipe_input(input, amount)) - { - // Gets the number of items claimed from the slot, or sets to 0 if slot has - // not been claimed by another input yet - let claimed = slot_claims.entry(slot).or_insert(0); - let available = item.amount().saturating_sub(*claimed); - let provided = available.min(required); - required -= provided; - *claimed += provided; - true - } else { - false - } - }); - // If there were not sufficient items to cover requirement between all provided - // slots, or if non-consumed item was not present, mark input as not satisfied - if required > 0 || !contains_any { - unsatisfied_requirements.push((input, required)); - } - } - // Checks each input against slots in the inventory. If the slots contain an // item that fulfills the need of the input, marks some of the item as claimed // up to quantity needed for the crafting input. If the item either // cannot be used, or there is insufficient quantity, adds input and // number of materials needed to unsatisfied requirements. - handle_requirement( + self.material.0.handle_requirement( + self.material.1, &mut slot_claims, &mut unsatisfied_requirements, inv, - &self.material.0, - self.material.1, core::iter::once(material_slot), ); if let Some((modifier_input, modifier_amount)) = &self.modifier { // TODO: Better way to get slot to use that ensures this requirement fails if no // slot provided? - handle_requirement( + modifier_input.handle_requirement( + *modifier_amount, &mut slot_claims, &mut unsatisfied_requirements, inv, - modifier_input, - *modifier_amount, core::iter::once(modifier_slot.unwrap_or(InvSlotId::new(0, 0))), ); } @@ -666,12 +667,11 @@ impl ComponentRecipe { .filter_map(|(j, slot)| if i as u32 == *j { Some(slot) } else { None }) .copied(); // Checks if requirement is met, and if not marks it as unsatisfied - handle_requirement( + input.handle_requirement( + *amount, &mut slot_claims, &mut unsatisfied_requirements, inv, - input, - *amount, input_slots, ); }); @@ -900,6 +900,199 @@ impl assets::Compound for ComponentRecipeBook { } } +#[derive(Serialize, Deserialize, Hash, PartialEq, Eq, Clone, Debug)] +enum RepairKey { + ItemDefId(String), + ModularWeapon { material: String }, +} + +impl RepairKey { + fn from_item(item: &Item) -> Option { + match item.item_definition_id() { + ItemDefinitionId::Simple(item_id) => Some(Self::ItemDefId(String::from(item_id))), + ItemDefinitionId::Compound { .. } => None, + ItemDefinitionId::Modular { pseudo_base, .. } => match pseudo_base { + "veloren.core.pseudo_items.modular.tool" => { + if let Some(ItemDefinitionId::Simple(material)) = item + .components() + .iter() + .find(|comp| { + matches!( + &*comp.kind(), + ItemKind::ModularComponent( + modular::ModularComponent::ToolPrimaryComponent { .. } + ) + ) + }) + .and_then(|comp| { + comp.components() + .iter() + .next() + .map(|comp| comp.item_definition_id()) + }) + { + let material = String::from(material); + Some(Self::ModularWeapon { material }) + } else { + None + } + }, + _ => None, + }, + } + } +} + +#[derive(Serialize, Deserialize, Clone)] +struct RawRepairRecipe { + inputs: Vec<(RawRecipeInput, u32)>, +} + +#[derive(Serialize, Deserialize, Clone)] +struct RawRepairRecipeBook { + recipes: HashMap, + fallback: RawRepairRecipe, +} + +impl assets::Asset for RawRepairRecipeBook { + type Loader = assets::RonLoader; + + const EXTENSION: &'static str = "ron"; +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct RepairRecipe { + inputs: Vec<(RecipeInput, u32)>, +} + +impl RepairRecipe { + /// Determine whether the inventory contains the ingredients for a repair. + /// If it does, return a vec of inventory slots that contain the + /// ingredients needed, whose positions correspond to particular repair + /// inputs. If items are missing, return the missing items, and how many + /// are missing. + pub fn inventory_contains_ingredients( + &self, + inv: &Inventory, + ) -> Result, Vec<(&RecipeInput, u32)>> { + inventory_contains_ingredients(self.inputs(), inv, 1) + } + + pub fn inputs(&self) -> impl ExactSizeIterator { + self.inputs.iter().map(|(input, amount)| (input, *amount)) + } +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct RepairRecipeBook { + recipes: HashMap, + fallback: RepairRecipe, +} + +impl RepairRecipeBook { + pub fn repair_recipe(&self, item: &Item) -> Option<&RepairRecipe> { + RepairKey::from_item(item) + .as_ref() + .and_then(|key| self.recipes.get(key)) + .or_else(|| item.has_durability().then_some(&self.fallback)) + } + + pub fn repair_item( + &self, + inv: &mut Inventory, + item: Slot, + slots: Vec<(u32, InvSlotId)>, + ability_map: &AbilityMap, + msm: &MaterialStatManifest, + ) -> Result<(), Vec<(&RecipeInput, u32)>> { + let mut slot_claims = HashMap::new(); + let mut unsatisfied_requirements = Vec::new(); + + if let Some(item) = match item { + Slot::Equip(slot) => inv.equipped(slot), + Slot::Inventory(slot) => inv.get(slot), + } { + if let Some(repair_recipe) = self.repair_recipe(item) { + repair_recipe + .inputs + .iter() + .enumerate() + .for_each(|(i, (input, amount))| { + // Gets all slots provided for this input by the frontend + let input_slots = slots + .iter() + .filter_map(|(j, slot)| if i as u32 == *j { Some(slot) } else { None }) + .copied(); + // Checks if requirement is met, and if not marks it as unsatisfied + input.handle_requirement( + *amount, + &mut slot_claims, + &mut unsatisfied_requirements, + inv, + input_slots, + ); + }) + } + } + + if unsatisfied_requirements.is_empty() { + for (slot, to_remove) in slot_claims.iter() { + for _ in 0..*to_remove { + let _ = inv + .take(*slot, ability_map, msm) + .expect("Expected item to exist in the inventory"); + } + } + + inv.repair_item_at_slot(item); + + Ok(()) + } else { + Err(unsatisfied_requirements) + } + } +} + +impl assets::Compound for RepairRecipeBook { + fn load( + cache: assets::AnyCache, + specifier: &assets::SharedString, + ) -> Result { + #[inline] + fn load_recipe_input( + (input, amount): &(RawRecipeInput, u32), + ) -> Result<(RecipeInput, u32), assets::Error> { + let input = input.load_recipe_input()?; + Ok((input, *amount)) + } + + let raw = cache.load::(specifier)?.cloned(); + + let recipes = raw + .recipes + .iter() + .map(|(key, RawRepairRecipe { inputs })| { + let inputs = inputs + .iter() + .map(load_recipe_input) + .collect::, _>>()?; + Ok((key.clone(), RepairRecipe { inputs })) + }) + .collect::>()?; + + let fallback = RepairRecipe { + inputs: raw + .fallback + .inputs + .iter() + .map(load_recipe_input) + .collect::, _>>()?, + }; + + Ok(RepairRecipeBook { recipes, fallback }) + } +} + pub fn default_recipe_book() -> AssetHandle { RecipeBook::load_expect("common.recipe_book") } @@ -908,6 +1101,10 @@ pub fn default_component_recipe_book() -> AssetHandle { ComponentRecipeBook::load_expect("common.component_recipe_book") } +pub fn default_repair_recipe_book() -> AssetHandle { + RepairRecipeBook::load_expect("common.repair_recipe_book") +} + impl assets::Compound for ReverseComponentRecipeBook { fn load( cache: assets::AnyCache, diff --git a/server/src/events/inventory_manip.rs b/server/src/events/inventory_manip.rs index 558fe863fb..6a71cbff4b 100644 --- a/server/src/events/inventory_manip.rs +++ b/server/src/events/inventory_manip.rs @@ -12,7 +12,9 @@ use common::{ slot::{self, Slot}, }, consts::MAX_PICKUP_RANGE, - recipe::{self, default_component_recipe_book, default_recipe_book}, + recipe::{ + self, default_component_recipe_book, default_recipe_book, default_repair_recipe_book, + }, terrain::{Block, SpriteKind}, trade::Trades, uid::Uid, @@ -856,10 +858,17 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv None } }, - CraftEvent::Repair(slot) => { + CraftEvent::Repair { item, slots } => { + let repair_recipes = default_repair_recipe_book().read(); let sprite = get_craft_sprite(state, craft_sprite); if matches!(sprite, Some(SpriteKind::RepairBench)) { - inventory.repair_item_at_slot(slot); + let _ = repair_recipes.repair_item( + &mut inventory, + item, + slots, + &state.ecs().read_resource::(), + &state.ecs().read_resource::(), + ); } None }, diff --git a/server/src/sys/msg/register.rs b/server/src/sys/msg/register.rs index d457cab9b4..cabf24a025 100644 --- a/server/src/sys/msg/register.rs +++ b/server/src/sys/msg/register.rs @@ -8,7 +8,7 @@ use crate::{ use common::{ comp::{self, Admin, Player, Stats}, event::{EventBus, ServerEvent}, - recipe::{default_component_recipe_book, default_recipe_book}, + recipe::{default_component_recipe_book, default_recipe_book, default_repair_recipe_book}, resources::TimeOfDay, shared_server_config::ServerConstants, uid::{Uid, UidAllocator}, @@ -348,6 +348,7 @@ impl<'a> System<'a> for Sys { world_map: (*read_data.map).clone(), recipe_book: default_recipe_book().cloned(), component_recipe_book: default_component_recipe_book().cloned(), + repair_recipe_book: default_repair_recipe_book().cloned(), material_stats: (*read_data.material_stats).clone(), ability_map: (*read_data.ability_map).clone(), server_constants: ServerConstants { diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 669fe0ca68..03d0129229 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -726,7 +726,7 @@ pub enum Event { craft_sprite: Option>, }, RepairItem { - slot: Slot, + item: Slot, sprite_pos: Vec3, }, InviteMember(Uid), @@ -3944,7 +3944,7 @@ impl Hud { self.show.crafting_fields.craft_sprite { events.push(Event::RepairItem { - slot: from, + item: from, sprite_pos, }) } diff --git a/voxygen/src/session/mod.rs b/voxygen/src/session/mod.rs index 7c366d4fdf..8ef2af6e6e 100644 --- a/voxygen/src/session/mod.rs +++ b/voxygen/src/session/mod.rs @@ -1777,8 +1777,27 @@ impl PlayState for SessionState { HudEvent::SalvageItem { slot, salvage_pos } => { self.client.borrow_mut().salvage_item(slot, salvage_pos); }, - HudEvent::RepairItem { slot, sprite_pos } => { - self.client.borrow_mut().repair_item(slot, sprite_pos); + HudEvent::RepairItem { item, sprite_pos } => { + let slots = { + let client = self.client.borrow(); + let slots = (|| { + if let Some(inventory) = client.inventories().get(client.entity()) { + let item = match item { + Slot::Equip(slot) => inventory.equipped(slot), + Slot::Inventory(slot) => inventory.get(slot), + }?; + let repair_recipe = + client.repair_recipe_book().repair_recipe(item)?; + repair_recipe.inventory_contains_ingredients(inventory).ok() + } else { + None + } + })(); + slots.unwrap_or_default() + }; + self.client + .borrow_mut() + .repair_item(item, slots, sprite_pos); }, HudEvent::InviteMember(uid) => { self.client.borrow_mut().send_invite(uid, InviteKind::Group); From 011b6c3feb48729dd02d767972c485bff0da3b61 Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 18 Jul 2022 22:08:03 -0400 Subject: [PATCH 07/28] Changed how durability was persisted so that a more general system can be used for future item properties. --- .../sprite/repair_bench/repair_bench-0.vox | 3 +++ assets/voxygen/voxel/sprite_manifest.ron | 11 ++++++++ common/src/comp/inventory/item/mod.rs | 8 ++---- .../src/migrations/V50__item_durability.sql | 2 +- server/src/persistence/character.rs | 26 +++++++++---------- .../src/persistence/character/conversions.rs | 20 +++++++++++--- server/src/persistence/json_models.rs | 17 ++++++++++++ server/src/persistence/models.rs | 2 +- 8 files changed, 64 insertions(+), 25 deletions(-) create mode 100644 assets/voxygen/voxel/sprite/repair_bench/repair_bench-0.vox diff --git a/assets/voxygen/voxel/sprite/repair_bench/repair_bench-0.vox b/assets/voxygen/voxel/sprite/repair_bench/repair_bench-0.vox new file mode 100644 index 0000000000..265b9eabb4 --- /dev/null +++ b/assets/voxygen/voxel/sprite/repair_bench/repair_bench-0.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ae8a36234e9ed125f1369d0fa396e35885f57c256d14d94149c383090678d326 +size 4692 diff --git a/assets/voxygen/voxel/sprite_manifest.ron b/assets/voxygen/voxel/sprite_manifest.ron index 6639b1bae9..e9f29976c3 100644 --- a/assets/voxygen/voxel/sprite_manifest.ron +++ b/assets/voxygen/voxel/sprite_manifest.ron @@ -3788,6 +3788,17 @@ CookingPot: Some(( ], wind_sway: 0.0, )), +// Repair Bench +RepairBench: Some(( + variations: [ + ( + model: "voxygen.voxel.sprite.repair_bench.repair_bench-0", + offset: (-7.0, -8.0, 0.0), + lod_axes: (0.0, 0.0, 0.0), + ), + ], + wind_sway: 0.0, +)), // Ensnaring Vines EnsnaringVines: Some(( variations: [ diff --git a/common/src/comp/inventory/item/mod.rs b/common/src/comp/inventory/item/mod.rs index 990c696ee7..271d705b96 100644 --- a/common/src/comp/inventory/item/mod.rs +++ b/common/src/comp/inventory/item/mod.rs @@ -1228,11 +1228,7 @@ impl Item { } } - pub fn persistence_durability(&self) -> Option { - self.durability.map(|x| x.min(i32::MAX as u32) as i32) - } - - pub fn persistence_set_durability(&mut self, value: Option) { + pub fn persistence_set_durability(&mut self, value: Option) { // If changes have been made so that item no longer needs to track durability, // set to None if !self.has_durability() { @@ -1240,7 +1236,7 @@ impl Item { } else { // Set durability to persisted value, and if item previously had no durability, // set to Some(0) so that durability will be tracked - self.durability = Some(value.map_or(0, |x| x.max(0) as u32)); + self.durability = Some(value.unwrap_or(0)); } } diff --git a/server/src/migrations/V50__item_durability.sql b/server/src/migrations/V50__item_durability.sql index 482a175793..09fe315461 100644 --- a/server/src/migrations/V50__item_durability.sql +++ b/server/src/migrations/V50__item_durability.sql @@ -1 +1 @@ -ALTER TABLE item ADD durability INTEGER; \ No newline at end of file +ALTER TABLE item ADD properties TEXT NOT NULL DEFAULT '{}'; \ No newline at end of file diff --git a/server/src/persistence/character.rs b/server/src/persistence/character.rs index e7069b8b69..5a1b99bc1a 100644 --- a/server/src/persistence/character.rs +++ b/server/src/persistence/character.rs @@ -71,14 +71,14 @@ pub fn load_items(connection: &Connection, root: i64) -> Result, Persi item_definition_id, stack_size, position, - durability + properties ) AS ( SELECT item_id, parent_container_item_id, item_definition_id, stack_size, position, - durability + properties FROM item WHERE parent_container_item_id = ?1 UNION ALL @@ -87,7 +87,7 @@ pub fn load_items(connection: &Connection, root: i64) -> Result, Persi item.item_definition_id, item.stack_size, item.position, - item.durability + item.properties FROM item, items_tree WHERE item.parent_container_item_id = items_tree.item_id ) @@ -103,7 +103,7 @@ pub fn load_items(connection: &Connection, root: i64) -> Result, Persi item_definition_id: row.get(2)?, stack_size: row.get(3)?, position: row.get(4)?, - durability: row.get(5)?, + properties: row.get(5)?, }) })? .filter_map(Result::ok) @@ -394,7 +394,7 @@ pub fn create_character( parent_container_item_id: WORLD_PSEUDO_CONTAINER_ID, item_definition_id: CHARACTER_PSEUDO_CONTAINER_DEF_ID.to_owned(), position: character_id.to_string(), - durability: None, + properties: String::new(), }, Item { stack_size: 1, @@ -402,7 +402,7 @@ pub fn create_character( parent_container_item_id: character_id, item_definition_id: INVENTORY_PSEUDO_CONTAINER_DEF_ID.to_owned(), position: INVENTORY_PSEUDO_CONTAINER_POSITION.to_owned(), - durability: None, + properties: String::new(), }, Item { stack_size: 1, @@ -410,7 +410,7 @@ pub fn create_character( parent_container_item_id: character_id, item_definition_id: LOADOUT_PSEUDO_CONTAINER_DEF_ID.to_owned(), position: LOADOUT_PSEUDO_CONTAINER_POSITION.to_owned(), - durability: None, + properties: String::new(), }, ]; @@ -421,7 +421,7 @@ pub fn create_character( item_definition_id, stack_size, position, - durability) + properties) VALUES (?1, ?2, ?3, ?4, ?5, ?6)", )?; @@ -432,7 +432,7 @@ pub fn create_character( &pseudo_container.item_definition_id, &pseudo_container.stack_size, &pseudo_container.position, - &pseudo_container.durability, + &pseudo_container.properties, ])?; } drop(stmt); @@ -531,7 +531,7 @@ pub fn create_character( item_definition_id, stack_size, position, - durability) + properties) VALUES (?1, ?2, ?3, ?4, ?5, ?6)", )?; @@ -542,7 +542,7 @@ pub fn create_character( &item.model.item_definition_id, &item.model.stack_size, &item.model.position, - &item.model.durability, + &item.model.properties, ])?; } drop(stmt); @@ -1060,7 +1060,7 @@ pub fn update( item_definition_id, stack_size, position, - durability) + properties) VALUES (?1, ?2, ?3, ?4, ?5, ?6)", )?; @@ -1071,7 +1071,7 @@ pub fn update( &item.item_definition_id, &item.stack_size, &item.position, - &item.durability, + &item.properties, ])?; } } diff --git a/server/src/persistence/character/conversions.rs b/server/src/persistence/character/conversions.rs index 012c70b420..dbeb0eab61 100644 --- a/server/src/persistence/character/conversions.rs +++ b/server/src/persistence/character/conversions.rs @@ -5,7 +5,10 @@ use crate::persistence::{ use crate::persistence::{ error::PersistenceError, - json_models::{self, CharacterPosition, DatabaseAbilitySet, GenericBody, HumanoidBody}, + json_models::{ + self, CharacterPosition, DatabaseAbilitySet, DatabaseItemProperties, GenericBody, + HumanoidBody, + }, }; use common::{ character::CharacterId, @@ -165,6 +168,8 @@ pub fn convert_items_to_database_items( bfs_queue.push_back((format!("component_{}", i), Some(component), item_id)); } + let item_properties = json_models::item_properties_to_db_model(item); + let upsert = ItemModelPair { model: Item { item_definition_id: String::from(item.persistence_item_id()), @@ -176,7 +181,8 @@ pub fn convert_items_to_database_items( } else { 1 }, - durability: item.persistence_durability(), + properties: serde_json::to_string(&item_properties) + .expect("We probably want to crash if this gets triggered?"), }, // Continue to remember the atomic, in case we detect an error later and want // to roll back to preserve liveness. @@ -360,7 +366,10 @@ pub fn convert_inventory_from_database_items( item_indices.insert(db_item.item_id, i); let mut item = get_item_from_asset(db_item.item_definition_id.as_str())?; - item.persistence_set_durability(db_item.durability); + let item_properties = + serde_json::de::from_str::(&db_item.properties) + .expect("We probably want to crash if this gets triggered?"); + json_models::apply_db_item_properties(&mut item, &item_properties); // NOTE: Since this is freshly loaded, the atomic is *unique.* let comp = item.get_item_id_for_database(); @@ -462,7 +471,10 @@ pub fn convert_loadout_from_database_items( item_indices.insert(db_item.item_id, i); let mut item = get_item_from_asset(db_item.item_definition_id.as_str())?; - item.persistence_set_durability(db_item.durability); + let item_properties = + serde_json::de::from_str::(&db_item.properties) + .expect("We probably want to crash if this gets triggered?"); + json_models::apply_db_item_properties(&mut item, &item_properties); // NOTE: item id is currently *unique*, so we can store the ID safely. let comp = item.get_item_id_for_database(); diff --git a/server/src/persistence/json_models.rs b/server/src/persistence/json_models.rs index 5d0ba9b3ef..ac25843172 100644 --- a/server/src/persistence/json_models.rs +++ b/server/src/persistence/json_models.rs @@ -285,3 +285,20 @@ pub fn active_abilities_from_db_model( .collect::>(); comp::ability::ActiveAbilities::new(ability_sets) } + +#[derive(Serialize, Deserialize)] +pub struct DatabaseItemProperties { + #[serde(skip_serializing_if = "Option::is_none")] + durability: Option, +} + +pub fn item_properties_to_db_model(item: &comp::Item) -> DatabaseItemProperties { + DatabaseItemProperties { + durability: item.durability(), + } +} + +pub fn apply_db_item_properties(item: &mut comp::Item, properties: &DatabaseItemProperties) { + let DatabaseItemProperties { durability } = properties; + item.persistence_set_durability(*durability); +} diff --git a/server/src/persistence/models.rs b/server/src/persistence/models.rs index 1a82963cda..df1487422b 100644 --- a/server/src/persistence/models.rs +++ b/server/src/persistence/models.rs @@ -12,7 +12,7 @@ pub struct Item { pub item_definition_id: String, pub stack_size: i32, pub position: String, - pub durability: Option, + pub properties: String, } pub struct Body { From 52a62420db4f782ba21f69117542ecc2125778ce Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 19 Jul 2022 21:17:00 -0400 Subject: [PATCH 08/28] Changed durability persistence to a NonZeroU32 --- common/src/comp/inventory/item/mod.rs | 8 ++++++-- common/src/comp/inventory/trade_pricing.rs | 11 +++++------ server/src/persistence/json_models.rs | 22 +++++++++++++++++++--- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/common/src/comp/inventory/item/mod.rs b/common/src/comp/inventory/item/mod.rs index 271d705b96..beb642bf50 100644 --- a/common/src/comp/inventory/item/mod.rs +++ b/common/src/comp/inventory/item/mod.rs @@ -1228,7 +1228,11 @@ impl Item { } } - pub fn persistence_set_durability(&mut self, value: Option) { + pub fn persistence_durability(&self) -> Option { + self.durability.and_then(NonZeroU32::new) + } + + pub fn persistence_set_durability(&mut self, value: Option) { // If changes have been made so that item no longer needs to track durability, // set to None if !self.has_durability() { @@ -1236,7 +1240,7 @@ impl Item { } else { // Set durability to persisted value, and if item previously had no durability, // set to Some(0) so that durability will be tracked - self.durability = Some(value.unwrap_or(0)); + self.durability = Some(value.map_or(0, NonZeroU32::get)); } } diff --git a/common/src/comp/inventory/trade_pricing.rs b/common/src/comp/inventory/trade_pricing.rs index 64414abeb3..a46a40bcdb 100644 --- a/common/src/comp/inventory/trade_pricing.rs +++ b/common/src/comp/inventory/trade_pricing.rs @@ -1022,16 +1022,17 @@ impl TradePricing { #[cfg(test)] fn print_sorted(&self) { - use crate::comp::item::armor; //, ItemKind, MaterialStatManifest}; + use crate::comp::item::{armor, DurabilityMultiplier}; //, ItemKind, MaterialStatManifest}; println!("Item, ForSale, Amount, Good, Quality, Deal, Unit,"); fn more_information(i: &Item, p: f32) -> (String, &'static str) { let msm = &MaterialStatManifest::load().read(); + let durability_multiplier = DurabilityMultiplier(1.0); if let ItemKind::Armor(a) = &*i.kind() { ( - match a.stats(msm).protection { + match a.stats(msm, durability_multiplier).protection { Some(armor::Protection::Invincible) => "Invincible".into(), Some(armor::Protection::Normal(x)) => format!("{:.4}", x * p), None => "0.0".into(), @@ -1039,10 +1040,8 @@ impl TradePricing { "prot/val", ) } else if let ItemKind::Tool(t) = &*i.kind() { - ( - format!("{:.4}", t.stats.power * t.stats.speed * p), - "dps/val", - ) + let stats = t.stats(durability_multiplier); + (format!("{:.4}", stats.power * stats.speed * p), "dps/val") } else if let ItemKind::Consumable { kind: _, effects } = &*i.kind() { ( effects diff --git a/server/src/persistence/json_models.rs b/server/src/persistence/json_models.rs index ac25843172..f8c4e1d147 100644 --- a/server/src/persistence/json_models.rs +++ b/server/src/persistence/json_models.rs @@ -2,7 +2,7 @@ use common::comp; use common_base::dev_panic; use hashbrown::HashMap; use serde::{Deserialize, Serialize}; -use std::string::ToString; +use std::{num::NonZeroU32, string::ToString}; use vek::{Vec2, Vec3}; #[derive(Serialize, Deserialize)] @@ -286,15 +286,18 @@ pub fn active_abilities_from_db_model( comp::ability::ActiveAbilities::new(ability_sets) } +/// Struct containing item properties in the format that they get persisted to +/// the database. Adding new fields is generally safe as long as they are +/// optional. Renaming or removing old fields will require a migration. #[derive(Serialize, Deserialize)] pub struct DatabaseItemProperties { #[serde(skip_serializing_if = "Option::is_none")] - durability: Option, + durability: Option, } pub fn item_properties_to_db_model(item: &comp::Item) -> DatabaseItemProperties { DatabaseItemProperties { - durability: item.durability(), + durability: item.persistence_durability(), } } @@ -302,3 +305,16 @@ pub fn apply_db_item_properties(item: &mut comp::Item, properties: &DatabaseItem let DatabaseItemProperties { durability } = properties; item.persistence_set_durability(*durability); } + +#[cfg(test)] +pub mod tests { + #[test] + fn test_default_item_properties() { + use super::DatabaseItemProperties; + const DEFAULT_ITEM_PROPERTIES: &str = "{}"; + let _ = serde_json::de::from_str::(DEFAULT_ITEM_PROPERTIES).expect( + "Default value should always load to ensure that changes to item properties is always \ + forward compatible with migration V50.", + ); + } +} From 85ceae335443eb4c92a936adcce1090285059af6 Mon Sep 17 00:00:00 2001 From: UncomfySilence Date: Wed, 20 Jul 2022 17:04:35 +0200 Subject: [PATCH 09/28] repair recipes for armor pieces --- assets/common/recipe_book.ron | 2 +- assets/common/repair_recipe_book.ron | 790 ++++++++++++++++++++++++++- 2 files changed, 789 insertions(+), 3 deletions(-) diff --git a/assets/common/recipe_book.ron b/assets/common/recipe_book.ron index bda7282cbb..defa3f5ce3 100644 --- a/assets/common/recipe_book.ron +++ b/assets/common/recipe_book.ron @@ -25,7 +25,7 @@ craft_sprite: Some(CraftingBench), ), "velorite_frag": ( - output: ("common.items.mineral.ore.veloritefrag", 2), + output: ("common.items.mineral.ore.veloritefrag", 3), inputs: [ (Item("common.items.mineral.ore.velorite"), 1, false), (Item("common.items.tool.craftsman_hammer"), 0, false), diff --git a/assets/common/repair_recipe_book.ron b/assets/common/repair_recipe_book.ron index 789e4b56cd..53baab7b96 100644 --- a/assets/common/repair_recipe_book.ron +++ b/assets/common/repair_recipe_book.ron @@ -68,8 +68,794 @@ recipes: { (Item("common.items.log.eldwood"), 1), ], ), - // Armor - + // ARMOR/HIDE/RAWHIDE + ItemDefId("common.items.armor.hide.rawhide.back"): ( + inputs: [ + (Item("common.items.crafting_ing.leather.simple_leather"), 1), + ], + ), + ItemDefId("common.items.armor.hide.rawhide.belt"): ( + inputs: [ + (Item("common.items.crafting_ing.leather.simple_leather"), 1), + ], + ), + ItemDefId("common.items.armor.hide.rawhide.chest"): ( + inputs: [ + (Item("common.items.crafting_ing.leather.simple_leather"), 4), + ], + ), + ItemDefId("common.items.armorhide.rawhide.foot"): ( + inputs: [ + (Item("common.items.crafting_ing.leather.simple_leather"), 1), + ], + ), + ItemDefId("common.items.armor.hide.rawhide.hand"): ( + inputs: [ + (Item("common.items.crafting_ing.leather.simple_leather"), 1), + ], + ), + ItemDefId("common.items.armor.hide.rawhide.pants"): ( + inputs: [ + (Item("common.items.crafting_ing.leather.simple_leather"), 3), + ], + ), + ItemDefId("common.items.armor.hide.rawhide.shoulder"): ( + inputs: [ + (Item("common.items.crafting_ing.leather.simple_leather"), 3), + ], + ), + // ARMOR/HIDE/LEATHER + ItemDefId("common.items.armor.hide.leather.back"): ( + inputs: [ + (Item("common.items.crafting_ing.leather.thick_leather"), 1), + ], + ), + ItemDefId("common.items.armor.hide.leather.belt"): ( + inputs: [ + (Item("common.items.crafting_ing.leather.thick_leather"), 1), + ], + ), + ItemDefId("common.items.armor.hide.leather.chest"): ( + inputs: [ + (Item("common.items.crafting_ing.leather.thick_leather"), 4), + ], + ), + ItemDefId("common.items.armorhide.leather.foot"): ( + inputs: [ + (Item("common.items.crafting_ing.leather.thick_leather"), 1), + ], + ), + ItemDefId("common.items.armor.hide.leather.hand"): ( + inputs: [ + (Item("common.items.crafting_ing.leather.thick_leather"), 1), + ], + ), + ItemDefId("common.items.armor.hide.leather.pants"): ( + inputs: [ + (Item("common.items.crafting_ing.leather.thick_leather"), 3), + ], + ), + ItemDefId("common.items.armor.hide.leather.shoulder"): ( + inputs: [ + (Item("common.items.crafting_ing.leather.thick_leather"), 3), + ], + ), + // ARMOR/HIDE/SCALE + ItemDefId("common.items.armor.hide.scale.back"): ( + inputs: [ + (Item("common.items.crafting_ing.hide.scales"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 1), + ], + ), + ItemDefId("common.items.armor.hide.scale.belt"): ( + inputs: [ + (Item("common.items.crafting_ing.hide.scales"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 1), + ], + ), + ItemDefId("common.items.armor.hide.scale.chest"): ( + inputs: [ + (Item("common.items.crafting_ing.hide.scales"), 4), + (Item("common.items.mineral.ore.veloritefrag"), 7), + ], + ), + ItemDefId("common.items.armorhide.scale.foot"): ( + inputs: [ + (Item("common.items.crafting_ing.hide.scales"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 1), + ], + ), + ItemDefId("common.items.armor.hide.scale.hand"): ( + inputs: [ + (Item("common.items.crafting_ing.hide.scales"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 1), + ], + ), + ItemDefId("common.items.armor.hide.scale.pants"): ( + inputs: [ + (Item("common.items.crafting_ing.hide.scales"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 5), + ], + ), + ItemDefId("common.items.armor.hide.scale.shoulder"): ( + inputs: [ + (Item("common.items.crafting_ing.hide.scales"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 5), + ], + ), + // ARMOR/HIDE/CARAPACE + ItemDefId("common.items.armor.hide.carapace.back"): ( + inputs: [ + (Item("common.items.crafting_ing.lhide.carapace"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 2), + ], + ), + ItemDefId("common.items.armor.hide.carapace.belt"): ( + inputs: [ + (Item("common.items.crafting_ing.hide.carapace"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 2), + ], + ), + ItemDefId("common.items.armor.hide.carapace.chest"): ( + inputs: [ + (Item("common.items.crafting_ing.hide.carapace"), 4), + (Item("common.items.mineral.ore.veloritefrag"), 10), + ], + ), + ItemDefId("common.items.armorhide.carapace.foot"): ( + inputs: [ + (Item("common.items.crafting_ing.hide.carapace"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 2), + ], + ), + ItemDefId("common.items.armor.hide.carapace.hand"): ( + inputs: [ + (Item("common.items.crafting_ing.hide.carapace"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 2), + ], + ), + ItemDefId("common.items.armor.hide.carapace.pants"): ( + inputs: [ + (Item("common.items.crafting_ing.hide.carapace"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 7), + ], + ), + ItemDefId("common.items.armor.hide.carapace.shoulder"): ( + inputs: [ + (Item("common.items.crafting_ing.hide.carapace"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 7), + ], + ), + // ARMOR/HIDE/PRIMAL + ItemDefId("common.items.armor.hide.primal.back"): ( + inputs: [ + (Item("common.items.crafting_ing.hide.plate"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 3), + ], + ), + ItemDefId("common.items.armor.hide.primal.belt"): ( + inputs: [ + (Item("common.items.crafting_ing.hide.plate"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 3), + ], + ), + ItemDefId("common.items.armor.hide.primal.chest"): ( + inputs: [ + (Item("common.items.crafting_ing.hide.plate"), 4), + (Item("common.items.mineral.ore.veloritefrag"), 14), + ], + ), + ItemDefId("common.items.armorhide.primal.foot"): ( + inputs: [ + (Item("common.items.crafting_ing.hide.plate"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 3), + ], + ), + ItemDefId("common.items.armor.hide.primal.hand"): ( + inputs: [ + (Item("common.items.crafting_ing.hide.plate"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 3), + ], + ), + ItemDefId("common.items.armor.hide.primal.pants"): ( + inputs: [ + (Item("common.items.crafting_ing.hide.plate"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 10), + ], + ), + ItemDefId("common.items.armor.hide.primal.shoulder"): ( + inputs: [ + (Item("common.items.crafting_ing.hide.plate"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 10), + ], + ), + // ARMOR/HIDE/DRAGONSCALE + ItemDefId("common.items.armor.hide.dragonscale.back"): ( + inputs: [ + (Item("common.items.crafting_ing.hide.dragon_scale"), 1), + (Item("common.items.mineral.ore.velorite"), 1), + ], + ), + ItemDefId("common.items.armor.hide.dragonscale.belt"): ( + inputs: [ + (Item("common.items.crafting_ing.hide.dragon_scale"), 1), + (Item("common.items.mineral.ore.velorite"), 1), + ], + ), + ItemDefId("common.items.armor.hide.dragonscale.chest"): ( + inputs: [ + (Item("common.items.crafting_ing.hide.dragon_scale"), 4), + (Item("common.items.mineral.ore.velorite"), 3), + ], + ), + ItemDefId("common.items.armorhide.dragonscale.foot"): ( + inputs: [ + (Item("common.items.crafting_ing.hide.dragon_scale"), 1), + (Item("common.items.mineral.ore.velorite"), 2), + ], + ), + ItemDefId("common.items.armor.hide.dragonscale.hand"): ( + inputs: [ + (Item("common.items.crafting_ing.hide.dragon_scale"), 1), + (Item("common.items.mineral.ore.velorite"), 2), + ], + ), + ItemDefId("common.items.armor.hide.dragonscale.pants"): ( + inputs: [ + (Item("common.items.crafting_ing.hide.dragon_scale"), 3), + (Item("common.items.mineral.ore.velorite"), 3), + ], + ), + ItemDefId("common.items.armor.hide.dragonscale.shoulder"): ( + inputs: [ + (Item("common.items.crafting_ing.hide.dragon_scale"), 3), + (Item("common.items.mineral.ore.velorite"), 3), + ], + ), + // ARMOR/CLOTH/LINEN + ItemDefId("common.items.armor.cloth.linen.back"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.linen"), 1), + ], + ), + ItemDefId("common.items.armor.cloth.linen.belt"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.linen"), 1), + ], + ), + ItemDefId("common.items.armor.cloth.linen.chest"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.linen"), 4), + ], + ), + ItemDefId("common.items.armor.cloth.linen.foot"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.linen"), 1), + ], + ), + ItemDefId("common.items.armor.cloth.linen.hand"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.linen"), 1), + ], + ), + ItemDefId("common.items.armor.cloth.linen.pants"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.linen"), 3), + ], + ), + ItemDefId("common.items.armor.cloth.linen.shoulder"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.linen"), 3), + ], + ), + // ARMOR/CLOTH/WOOL + ItemDefId("common.items.armor.cloth.woolen.back"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.wool"), 1), + ], + ), + ItemDefId("common.items.armor.cloth.woolen.belt"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.wool"), 1), + ], + ), + ItemDefId("common.items.armor.cloth.woolen.chest"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.wool"), 4), + ], + ), + ItemDefId("common.items.armor.cloth.woolen.foot"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.wool"), 1), + ], + ), + ItemDefId("common.items.armor.cloth.woolen.hand"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.wool"), 1), + ], + ), + ItemDefId("common.items.armor.cloth.woolen.pants"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.wool"), 3), + ], + ), + ItemDefId("common.items.armor.cloth.woolen.shoulder"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.wool"), 3), + ], + ), + // ARMOR/CLOTH/SILK + ItemDefId("common.items.armor.cloth.silken.back"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.silk"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 1), + ], + ), + ItemDefId("common.items.armor.cloth.silken.belt"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.silk"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 1), + ], + ), + ItemDefId("common.items.armor.cloth.silken.chest"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.silk"), 4), + (Item("common.items.mineral.ore.veloritefrag"), 7), + ], + ), + ItemDefId("common.items.armor.cloth.silken.foot"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.silk"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 1), + ], + ), + ItemDefId("common.items.armor.cloth.silken.hand"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.silk"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 1), + ], + ), + ItemDefId("common.items.armor.cloth.silken.pants"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.silk"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 5), + ], + ), + ItemDefId("common.items.armor.cloth.silken.shoulder"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.silk"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 5), + ], + ), + // ARMOR/CLOTH/DRUID + ItemDefId("common.items.armor.cloth.druid.back"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.lifecloth"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 2), + ], + ), + ItemDefId("common.items.armor.cloth.druid.belt"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.lifecloth"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 2), + ], + ), + ItemDefId("common.items.armor.cloth.druid.chest"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.lifecloth"), 4), + (Item("common.items.mineral.ore.veloritefrag"), 10), + ], + ), + ItemDefId("common.items.armor.cloth.druid.foot"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.lifecloth"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 2), + ], + ), + ItemDefId("common.items.armor.cloth.druid.hand"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.lifecloth"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 2), + ], + ), + ItemDefId("common.items.armor.cloth.druid.pants"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.lifecloth"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 7), + ], + ), + ItemDefId("common.items.armor.cloth.druid.shoulder"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.lifecloth"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 7), + ], + ), + // ARMOR/CLOTH/MOONWEAVE + ItemDefId("common.items.armor.cloth.moonweave.back"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.moonweave"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 3), + ], + ), + ItemDefId("common.items.armor.cloth.moonweave.belt"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.moonweave"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 3), + ], + ), + ItemDefId("common.items.armor.cloth.moonweave.chest"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.moonweave"), 4), + (Item("common.items.mineral.ore.veloritefrag"), 14), + ], + ), + ItemDefId("common.items.armor.cloth.moonweave.foot"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.moonweave"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 3), + ], + ), + ItemDefId("common.items.armor.cloth.moonweave.hand"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.moonweave"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 3), + ], + ), + ItemDefId("common.items.armor.cloth.moonweave.pants"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.moonweave"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 10), + ], + ), + ItemDefId("common.items.armor.cloth.moonweave.shoulder"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.moonweave"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 10), + ], + ), + // ARMOR/CLOTH/SUNSILK + ItemDefId("common.items.armor.cloth.sunsilk.back"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.sunsilk"), 1), + (Item("common.items.mineral.ore.velorite"), 1), + ], + ), + ItemDefId("common.items.armor.cloth.sunsilk.belt"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.sunsilk"), 1), + (Item("common.items.mineral.ore.velorite"), 1), + ], + ), + ItemDefId("common.items.armor.cloth.sunsilk.chest"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.sunsilk"), 4), + (Item("common.items.mineral.ore.velorite"), 3), + ], + ), + ItemDefId("common.items.armor.cloth.sunsilk.foot"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.sunsilk"), 1), + (Item("common.items.mineral.ore.velorite"), 2), + ], + ), + ItemDefId("common.items.armor.cloth.sunsilk.hand"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.sunsilk"), 1), + (Item("common.items.mineral.ore.velorite"), 2), + ], + ), + ItemDefId("common.items.armor.cloth.sunsilk.pants"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.sunsilk"), 3), + (Item("common.items.mineral.ore.velorite"), 3), + ], + ), + ItemDefId("common.items.armor.cloth.sunsilk.shoulder"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.sunsilk"), 3), + (Item("common.items.mineral.ore.velorite"), 3), + ], + ), + // ARMOR/MAIL/BRONZE + ItemDefId("common.items.armor.mail.bronze.back"): ( + inputs: [ + (Item("common.items.mineral.ingot.bronze"), 1), + ], + ), + ItemDefId("common.items.armor.mail.bronze.belt"): ( + inputs: [ + (Item("common.items.mineral.ingot.bronze"), 1), + ], + ), + ItemDefId("common.items.armor.mail.bronze.chest"): ( + inputs: [ + (Item("common.items.mineral.ingot.bronze"), 4), + ], + ), + ItemDefId("common.items.armor.mail.bronze.foot"): ( + inputs: [ + (Item("common.items.mineral.ingot.bronze"), 1), + ], + ), + ItemDefId("common.items.armor.mail.bronze.hand"): ( + inputs: [ + (Item("common.items.mineral.ingot.bronze"), 1), + ], + ), + ItemDefId("common.items.armor.mail.bronze.pants"): ( + inputs: [ + (Item("common.items.mineral.ingot.bronze"), 3), + ], + ), + ItemDefId("common.items.armor.mail.bronze.shoulder"): ( + inputs: [ + (Item("common.items.mineral.ingot.bronze"), 3), + ], + ), + // ARMOR/MAIL/IRON + ItemDefId("common.items.armor.mail.iron.back"): ( + inputs: [ + (Item("common.items.mineral.ingot.iron"), 1), + ], + ), + ItemDefId("common.items.armor.mail.iron.belt"): ( + inputs: [ + (Item("common.items.mineral.ingot.iron"), 1), + ], + ), + ItemDefId("common.items.armor.mail.iron.chest"): ( + inputs: [ + (Item("common.items.mineral.ingot.iron"), 4), + ], + ), + ItemDefId("common.items.armor.mail.iron.foot"): ( + inputs: [ + (Item("common.items.mineral.ingot.iron"), 1), + ], + ), + ItemDefId("common.items.armor.mail.iron.hand"): ( + inputs: [ + (Item("common.items.mineral.ingot.iron"), 1), + ], + ), + ItemDefId("common.items.armor.mail.iron.pants"): ( + inputs: [ + (Item("common.items.mineral.ingot.iron"), 3), + ], + ), + ItemDefId("common.items.armor.mail.iron.shoulder"): ( + inputs: [ + (Item("common.items.mineral.ingot.iron"), 3), + ], + ), + // ARMOR/MAIL/STEEL + ItemDefId("common.items.armor.mail.steel.back"): ( + inputs: [ + (Item("common.items.mineral.ingot.steel"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 1), + ], + ), + ItemDefId("common.items.armor.mail.steel.belt"): ( + inputs: [ + (Item("common.items.mineral.ingot.steel"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 1), + ], + ), + ItemDefId("common.items.armor.mail.steel.chest"): ( + inputs: [ + (Item("common.items.mineral.ingot.steel"), 4), + (Item("common.items.mineral.ore.veloritefrag"), 7), + ], + ), + ItemDefId("common.items.armor.mail.steel.foot"): ( + inputs: [ + (Item("common.items.mineral.ingot.steel"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 1), + ], + ), + ItemDefId("common.items.armor.mail.steel.hand"): ( + inputs: [ + (Item("common.items.mineral.ingot.steel"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 1), + ], + ), + ItemDefId("common.items.armor.mail.steel.pants"): ( + inputs: [ + (Item("common.items.mineral.ingot.steel"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 5), + ], + ), + ItemDefId("common.items.armor.mail.steel.shoulder"): ( + inputs: [ + (Item("common.items.mineral.ingot.steel"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 5), + ], + ), + // ARMOR/MAIL/COBALT + ItemDefId("common.items.armor.mail.bloodsteel.back"): ( + inputs: [ + (Item("common.items.mineral.ingot.cobalt"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 2), + ], + ), + ItemDefId("common.items.armor.mail.cobalt.belt"): ( + inputs: [ + (Item("common.items.mineral.ingot.cobalt"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 2), + ], + ), + ItemDefId("common.items.armor.mail.cobalt.chest"): ( + inputs: [ + (Item("common.items.mineral.ingot.cobalt"), 4), + (Item("common.items.mineral.ore.veloritefrag"), 10), + ], + ), + ItemDefId("common.items.armor.mail.cobalt.foot"): ( + inputs: [ + (Item("common.items.mineral.ingot.cobalt"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 2), + ], + ), + ItemDefId("common.items.armor.mail.cobalt.hand"): ( + inputs: [ + (Item("common.items.mineral.ingot.cobalt"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 2), + ], + ), + ItemDefId("common.items.armor.mail.cobalt.pants"): ( + inputs: [ + (Item("common.items.mineral.ingot.cobalt"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 7), + ], + ), + ItemDefId("common.items.armor.mail.cobalt.shoulder"): ( + inputs: [ + (Item("common.items.mineral.ingot.cobalt"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 7), + ], + ), + // ARMOR/MAIL/BLOODSTEEL + ItemDefId("common.items.armor.mail.bloodsteel.back"): ( + inputs: [ + (Item("common.items.mineral.ingot.bloodsteel"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 3), + ], + ), + ItemDefId("common.items.armor.mail.bloodsteel.belt"): ( + inputs: [ + (Item("common.items.mineral.ingot.bloodsteel"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 3), + ], + ), + ItemDefId("common.items.armor.mail.bloodsteel.chest"): ( + inputs: [ + (Item("common.items.mineral.ingot.bloodsteel"), 4), + (Item("common.items.mineral.ore.veloritefrag"), 14), + ], + ), + ItemDefId("common.items.armor.mail.bloodsteel.foot"): ( + inputs: [ + (Item("common.items.mineral.ingot.bloodsteel"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 3), + ], + ), + ItemDefId("common.items.armor.mail.bloodsteel.hand"): ( + inputs: [ + (Item("common.items.mineral.ingot.bloodsteel"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 3), + ], + ), + ItemDefId("common.items.armor.mail.bloodsteel.pants"): ( + inputs: [ + (Item("common.items.mineral.ingot.bloodsteel"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 10), + ], + ), + ItemDefId("common.items.armor.mail.bloodsteel.shoulder"): ( + inputs: [ + (Item("common.items.mineral.ingot.bloodsteel"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 10), + ], + ), + // ARMOR/MAIL/ORICHALCUM + ItemDefId("common.items.armor.mail.orichalcum.back"): ( + inputs: [ + (Item("common.items.mineral.ingot.orichalcum"), 1), + (Item("common.items.mineral.ore.velorite"), 1), + ], + ), + ItemDefId("common.items.armor.mail.orichalcum.belt"): ( + inputs: [ + (Item("common.items.mineral.ingot.orichalcum"), 1), + (Item("common.items.mineral.ore.velorite"), 1), + ], + ), + ItemDefId("common.items.armor.mail.orichalcum.chest"): ( + inputs: [ + (Item("common.items.mineral.ingot.orichalcum"), 4), + (Item("common.items.mineral.ore.velorite"), 3), + ], + ), + ItemDefId("common.items.armor.mail.orichalcum.foot"): ( + inputs: [ + (Item("common.items.mineral.ingot.orichalcum"), 1), + (Item("common.items.mineral.ore.velorite"), 2), + ], + ), + ItemDefId("common.items.armor.mail.orichalcum.hand"): ( + inputs: [ + (Item("common.items.mineral.ingot.orichalcum"), 1), + (Item("common.items.mineral.ore.velorite"), 2), + ], + ), + ItemDefId("common.items.armor.mail.orichalcum.pants"): ( + inputs: [ + (Item("common.items.mineral.ingot.orichalcum"), 3), + (Item("common.items.mineral.ore.velorite"), 3), + ], + ), + ItemDefId("common.items.armor.mail.orichalcum.shoulder"): ( + inputs: [ + (Item("common.items.mineral.ingot.orichalcum"), 3), + (Item("common.items.mineral.ore.velorite"), 3), + ], + ), + // MISC/HEAD + ItemDefId("common.items.armor.misc.head.hood"): ( + inputs: [ + (Item("common.items.crafting_ing.leather.thick_leather"), 1), + ], + ), + ItemDefId("common.items.armor.misc.head.dark_hood"): ( + inputs: [ + (Item("common.items.crafting_ing.leather.thick_leather"), 1), + ], + ), + ItemDefId("common.items.armor.misc.head.mitre"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.linen_red"), 2), + (Item("common.items.mineral.ore.veloritefrag"), 3), + ], + ), + ItemDefId("common.items.armor.misc.head.spikeguard"): ( + inputs: [ + (Item("common.items.mineral.ingot.cobalt"), 2), + (Item("common.items.mineral.ore.veloritefrag"), 3), + ], + ), + ItemDefId("common.items.armor.misc.head.winged_coronet"): ( + inputs: [ + (Item("common.items.mineral.ingot.gold"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 2), + ], + ), + ItemDefId("common.items.armor.misc.head.crown"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.linen_red"), 3), + (item("common.items.mineral.ingot.gold"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 3), + ], + ), + ItemDefId("common.items.armor.pirate.hat"): ( + inputs: [ + (Item("common.items.crafting_ing.hide.rigid_leather"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 3), + ], + ), + ItemDefId("common.items.armor.witch.hat"): ( + inputs: [ + (Item("common.items.crafting_ing.hide.rigid_leather"), 1), + (Item("common.items.crafting_ing.animal_misc.grim_eyeball"), 1) + (Item("common.items.mineral.ore.veloritefrag"), 3), + ], + ), + ItemDefId("common.items.armor.merchant.hat"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.linen_red"), 8), + (Item("common.items.mineral.gem.diamond"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 3), + ], + ), }, fallback: ( inputs: [ From 536d88a2c72d8c04cecceeac517f5f442261a55e Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 31 Jul 2022 12:29:38 -0400 Subject: [PATCH 10/28] Repair costs scale based on how damaged the item is. --- assets/common/repair_recipe_book.ron | 12 ++++++------ common/src/lib.rs | 3 ++- common/src/recipe.rs | 18 ++++++++++------- voxygen/src/session/mod.rs | 4 +++- voxygen/src/ui/widgets/item_tooltip.rs | 27 +++++++++++--------------- 5 files changed, 33 insertions(+), 31 deletions(-) diff --git a/assets/common/repair_recipe_book.ron b/assets/common/repair_recipe_book.ron index 53baab7b96..640498f2af 100644 --- a/assets/common/repair_recipe_book.ron +++ b/assets/common/repair_recipe_book.ron @@ -186,7 +186,7 @@ recipes: { // ARMOR/HIDE/CARAPACE ItemDefId("common.items.armor.hide.carapace.back"): ( inputs: [ - (Item("common.items.crafting_ing.lhide.carapace"), 1), + (Item("common.items.crafting_ing.hide.carapace"), 1), (Item("common.items.mineral.ore.veloritefrag"), 2), ], ), @@ -832,20 +832,20 @@ recipes: { ItemDefId("common.items.armor.misc.head.crown"): ( inputs: [ (Item("common.items.crafting_ing.cloth.linen_red"), 3), - (item("common.items.mineral.ingot.gold"), 1), + (Item("common.items.mineral.ingot.gold"), 1), (Item("common.items.mineral.ore.veloritefrag"), 3), ], ), ItemDefId("common.items.armor.pirate.hat"): ( inputs: [ - (Item("common.items.crafting_ing.hide.rigid_leather"), 1), + (Item("common.items.crafting_ing.leather.rigid_leather"), 1), (Item("common.items.mineral.ore.veloritefrag"), 3), ], ), ItemDefId("common.items.armor.witch.hat"): ( inputs: [ - (Item("common.items.crafting_ing.hide.rigid_leather"), 1), - (Item("common.items.crafting_ing.animal_misc.grim_eyeball"), 1) + (Item("common.items.crafting_ing.leather.rigid_leather"), 1), + (Item("common.items.crafting_ing.animal_misc.grim_eyeball"), 1), (Item("common.items.mineral.ore.veloritefrag"), 3), ], ), @@ -859,7 +859,7 @@ recipes: { }, fallback: ( inputs: [ - (Item("common.items.utility.coins"), 1000), + (Item("common.items.mineral.ore.veloritefrag"), 25), ], ), ) \ No newline at end of file diff --git a/common/src/lib.rs b/common/src/lib.rs index a2f7dc1c10..215ab90f8b 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -11,7 +11,8 @@ trait_alias, type_alias_impl_trait, extend_one, - arbitrary_self_types + arbitrary_self_types, + int_roundings )] #![feature(hash_drain_filter)] diff --git a/common/src/recipe.rs b/common/src/recipe.rs index 0f6dbe1079..060d257067 100644 --- a/common/src/recipe.rs +++ b/common/src/recipe.rs @@ -14,7 +14,7 @@ use crate::{ }; use hashbrown::HashMap; use serde::{Deserialize, Serialize}; -use std::{borrow::Cow, sync::Arc}; +use std::{borrow::Cow, ops::Mul, sync::Arc}; #[derive(Clone, Debug, Serialize, Deserialize)] pub enum RecipeInput { @@ -973,13 +973,18 @@ impl RepairRecipe { /// are missing. pub fn inventory_contains_ingredients( &self, + item: &Item, inv: &Inventory, ) -> Result, Vec<(&RecipeInput, u32)>> { - inventory_contains_ingredients(self.inputs(), inv, 1) + inventory_contains_ingredients(self.inputs(item), inv, 1) } - pub fn inputs(&self) -> impl ExactSizeIterator { - self.inputs.iter().map(|(input, amount)| (input, *amount)) + pub fn inputs(&self, item: &Item) -> impl ExactSizeIterator { + let item_durability = item.durability().unwrap_or(0); + self.inputs.iter().map(move |(input, amount)| { + let amount = amount.mul(item_durability).div_ceil(Item::MAX_DURABILITY); + (input, amount) + }) } } @@ -1014,8 +1019,7 @@ impl RepairRecipeBook { } { if let Some(repair_recipe) = self.repair_recipe(item) { repair_recipe - .inputs - .iter() + .inputs(item) .enumerate() .for_each(|(i, (input, amount))| { // Gets all slots provided for this input by the frontend @@ -1025,7 +1029,7 @@ impl RepairRecipeBook { .copied(); // Checks if requirement is met, and if not marks it as unsatisfied input.handle_requirement( - *amount, + amount, &mut slot_claims, &mut unsatisfied_requirements, inv, diff --git a/voxygen/src/session/mod.rs b/voxygen/src/session/mod.rs index 8ef2af6e6e..e18a7ec0cb 100644 --- a/voxygen/src/session/mod.rs +++ b/voxygen/src/session/mod.rs @@ -1788,7 +1788,9 @@ impl PlayState for SessionState { }?; let repair_recipe = client.repair_recipe_book().repair_recipe(item)?; - repair_recipe.inventory_contains_ingredients(inventory).ok() + repair_recipe + .inventory_contains_ingredients(item, inventory) + .ok() } else { None } diff --git a/voxygen/src/ui/widgets/item_tooltip.rs b/voxygen/src/ui/widgets/item_tooltip.rs index c45868afa7..e6db678eb8 100644 --- a/voxygen/src/ui/widgets/item_tooltip.rs +++ b/voxygen/src/ui/widgets/item_tooltip.rs @@ -669,22 +669,17 @@ impl<'a> Widget for ItemTooltip<'a> { 6, ); - // Durability - if let Some(durability) = item.durability() { - const MAX_DURABILITY: u32 = 8; - let durability = MAX_DURABILITY - durability.min(MAX_DURABILITY); - widget::Text::new(&format!( - "{} : {}/{}", - i18n.get_msg("common-stats-durability"), - durability, - MAX_DURABILITY - )) - .graphics_for(id) - .parent(id) - .with_style(self.style.desc) - .color(text_color) - .down_from(state.ids.stats[6], V_PAD_STATS) - .set(state.ids.stats[7], ui); + if item.has_durability() { + let durability = Item::MAX_DURABILITY - item.durability().unwrap_or(0); + stat_text( + format!( + "{} : {}/{}", + i18n.get_msg("common-stats-durability"), + durability, + Item::MAX_DURABILITY + ), + 7, + ) } if let Some(equipped_item) = equipped_item { From 658de93c593d4a99f7f9fdbe0bba87b1b661c76b Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 2 Aug 2022 22:29:52 -0400 Subject: [PATCH 11/28] Repairing UI --- assets/voxygen/i18n/en/hud/crafting.ftl | 8 +- assets/voxygen/item_image_manifest.ron | 4 + voxygen/src/hud/crafting.rs | 420 ++++++++++++++---------- voxygen/src/hud/mod.rs | 40 ++- voxygen/src/hud/slots.rs | 27 +- 5 files changed, 292 insertions(+), 207 deletions(-) diff --git a/assets/voxygen/i18n/en/hud/crafting.ftl b/assets/voxygen/i18n/en/hud/crafting.ftl index c65852f348..7c356bf048 100644 --- a/assets/voxygen/i18n/en/hud/crafting.ftl +++ b/assets/voxygen/i18n/en/hud/crafting.ftl @@ -14,6 +14,7 @@ hud-crafting-loom = Loom hud-crafting-spinning_wheel = Spinning Wheel hud-crafting-tanning_rack = Tanning Rack hud-crafting-salvaging_station = Salvaging Bench +hud-crafting-repair_bench = Repair Bench hud-crafting-campfire = Campfire hud-crafting-tabs-all = All hud-crafting-tabs-armor = Armor @@ -34,11 +35,8 @@ hud-crafting-dismantle_explanation = Double-Click them to start dismantling. hud-crafting-repair_title = Repair Items -hud-crafting-repair_explanation = - Repair your damaged and - broken equipment here. - - Double-Click the item to repair it. +hud-crafting-repair_slot_title = Damaged Item +hud-crafting-repair_slot_desc = Place a damaged item here to repair it. hud-crafting-modular_desc = Drag Item-Parts here to craft a weapon hud-crafting-mod_weap_prim_slot_title = Primary Weapon Component hud-crafting-mod_weap_prim_slot_desc = Place a primary weapon component here (e.g. a sword blade, axe head, or bow limbs). diff --git a/assets/voxygen/item_image_manifest.ron b/assets/voxygen/item_image_manifest.ron index 2a4039a30e..df1d68f37c 100644 --- a/assets/voxygen/item_image_manifest.ron +++ b/assets/voxygen/item_image_manifest.ron @@ -39,6 +39,10 @@ "voxel.sprite.salvaging_station.salvaging_station-0", (0.0, 0.0, 0.0), (-50.0, 40.0, 30.0), 0.9, ), + Simple("RepairBench"): VoxTrans( + "voxel.sprite.repair_bench.repair_bench-0", + (0.0, 0.0, 0.0), (-50.0, 40.0, 30.0), 0.9, + ), // Weapons // Diary Example Images Simple("example_utility"): VoxTrans( diff --git a/voxygen/src/hud/crafting.rs b/voxygen/src/hud/crafting.rs index c344e4514b..15eb730e56 100644 --- a/voxygen/src/hud/crafting.rs +++ b/voxygen/src/hud/crafting.rs @@ -22,7 +22,7 @@ use common::{ Item, ItemBase, ItemDef, ItemDesc, ItemKind, ItemTag, MaterialStatManifest, Quality, TagExampleInfo, }, - slot::InvSlotId, + slot::{InvSlotId, Slot}, Inventory, }, recipe::{ComponentKey, Recipe, RecipeInput}, @@ -86,10 +86,7 @@ widget_ids! { dismantle_title, dismantle_img, dismantle_txt, - repair_title, - repair_img, - repair_txt, - modular_inputs[], + craft_slots[], modular_art, modular_desc_txt, modular_wep_empty_bg, @@ -117,6 +114,9 @@ pub enum Event { Focus(widget::Id), SearchRecipe(Option), ClearRecipeInputs, + RepairItem { + slot: Slot, + }, } pub struct CraftingShow { @@ -126,7 +126,7 @@ pub struct CraftingShow { pub salvage: bool, pub repair: bool, // TODO: Maybe try to do something that doesn't need to allocate? - pub recipe_inputs: HashMap, + pub recipe_inputs: HashMap, } impl Default for CraftingShow { @@ -282,7 +282,7 @@ impl CraftingTab { // Tells UI whether tab is an adhoc tab that should only sometimes be present // depending on what station is accessed - fn is_adhoc(self) -> bool { matches!(self, CraftingTab::Dismantle | CraftingTab::Repair) } + fn is_adhoc(self) -> bool { matches!(self, CraftingTab::Dismantle) } } pub struct State { @@ -534,40 +534,45 @@ impl<'a> Widget for Crafting<'a> { let weapon_recipe = make_pseudo_recipe(SpriteKind::CraftingBench); let metal_comp_recipe = make_pseudo_recipe(SpriteKind::Anvil); let wood_comp_recipe = make_pseudo_recipe(SpriteKind::CraftingBench); - let modular_entries = { + let repair_recipe = make_pseudo_recipe(SpriteKind::RepairBench); + let pseudo_entries = { // A BTreeMap is used over a HashMap as when a HashMap is used, the UI shuffles // the positions of these every tick, so a BTreeMap is necessary to keep it // ordered. - let mut modular_entries = BTreeMap::new(); - modular_entries.insert( + let mut pseudo_entries = BTreeMap::new(); + pseudo_entries.insert( String::from("veloren.core.pseudo_recipe.modular_weapon"), - (&weapon_recipe, "Modular Weapon"), + (&weapon_recipe, "Modular Weapon", CraftingTab::Weapon), ); - modular_entries.insert( + pseudo_entries.insert( String::from("veloren.core.pseudo_recipe.modular_weapon_component.sword"), - (&metal_comp_recipe, "Sword Blade"), + (&metal_comp_recipe, "Sword Blade", CraftingTab::Weapon), ); - modular_entries.insert( + pseudo_entries.insert( String::from("veloren.core.pseudo_recipe.modular_weapon_component.axe"), - (&metal_comp_recipe, "Axe Head"), + (&metal_comp_recipe, "Axe Head", CraftingTab::Weapon), ); - modular_entries.insert( + pseudo_entries.insert( String::from("veloren.core.pseudo_recipe.modular_weapon_component.hammer"), - (&metal_comp_recipe, "Hammer Head"), + (&metal_comp_recipe, "Hammer Head", CraftingTab::Weapon), ); - modular_entries.insert( + pseudo_entries.insert( String::from("veloren.core.pseudo_recipe.modular_weapon_component.bow"), - (&wood_comp_recipe, "Bow Limbs"), + (&wood_comp_recipe, "Bow Limbs", CraftingTab::Weapon), ); - modular_entries.insert( + pseudo_entries.insert( String::from("veloren.core.pseudo_recipe.modular_weapon_component.staff"), - (&wood_comp_recipe, "Staff Shaft"), + (&wood_comp_recipe, "Staff Shaft", CraftingTab::Weapon), ); - modular_entries.insert( + pseudo_entries.insert( String::from("veloren.core.pseudo_recipe.modular_weapon_component.sceptre"), - (&wood_comp_recipe, "Sceptre Shaft"), + (&wood_comp_recipe, "Sceptre Shaft", CraftingTab::Weapon), ); - modular_entries + pseudo_entries.insert( + String::from("veloren.core.pseudo_recipe.repair"), + (&repair_recipe, "Repair Equipment", CraftingTab::Repair), + ); + pseudo_entries }; // First available recipes, then ones with available materials, @@ -618,36 +623,34 @@ impl<'a> Widget for Crafting<'a> { (name, recipe, is_craftable, has_materials) }) .chain( - matches!(sel_crafting_tab, CraftingTab::Weapon | CraftingTab::All) - .then_some( - modular_entries - .iter() - .filter(|(_, (_, output_name))| { - match search_filter { - SearchFilter::None => { - let output_name = output_name.to_lowercase(); - search_keys - .iter() - .all(|&substring| output_name.contains(substring)) - }, - // TODO: Get input filtering to work here, probably requires - // checking component recipe book? - SearchFilter::Input => false, - SearchFilter::Nonexistent => false, - } - }) - .map(|(recipe_name, (recipe, _))| { - ( - recipe_name, - *recipe, - self.show.crafting_fields.craft_sprite.map(|(_, s)| s) - == recipe.craft_sprite, - true, - ) - }), - ) - .into_iter() - .flatten(), + pseudo_entries + .iter() + // Filter by selected tab + .filter(|(_, (_, _, tab))| *sel_crafting_tab == CraftingTab::All || sel_crafting_tab == tab) + // Filter by search filter + .filter(|(_, (_, output_name, _))| { + match search_filter { + SearchFilter::None => { + let output_name = output_name.to_lowercase(); + search_keys + .iter() + .all(|&substring| output_name.contains(substring)) + }, + // TODO: Get input filtering to work here, probably requires + // checking component recipe book? + SearchFilter::Input => false, + SearchFilter::Nonexistent => false, + } + }) + .map(|(recipe_name, (recipe, _, _))| { + ( + recipe_name, + *recipe, + self.show.crafting_fields.craft_sprite.map(|(_, s)| s) + == recipe.craft_sprite, + true, + ) + }), ) .collect(); ordered_recipes.sort_by_key(|(_, recipe, is_craftable, has_materials)| { @@ -660,7 +663,7 @@ impl<'a> Widget for Crafting<'a> { }); // Recipe list - let recipe_list_length = self.client.recipe_book().iter().len() + modular_entries.len(); + let recipe_list_length = self.client.recipe_book().iter().len() + pseudo_entries.len(); if state.ids.recipe_list_btns.len() < recipe_list_length { state.update(|state| { state @@ -715,11 +718,12 @@ impl<'a> Widget for Crafting<'a> { .press_image(self.imgs.selection_press) .image_color(color::rgba(1.0, 0.82, 0.27, 1.0)); - let recipe_name = if let Some((_recipe, modular_name)) = modular_entries.get(name) { - *modular_name - } else { - &recipe.output.0.name - }; + let recipe_name = + if let Some((_recipe, pseudo_name, _filter_tab)) = pseudo_entries.get(name) { + *pseudo_name + } else { + &recipe.output.0.name + }; let text = Text::new(recipe_name) .color(if is_craftable { @@ -821,7 +825,8 @@ impl<'a> Widget for Crafting<'a> { // Selected Recipe if let Some((recipe_name, recipe)) = match state.selected_recipe.as_deref() { Some(selected_recipe) => { - if let Some((modular_recipe, _modular_name)) = modular_entries.get(selected_recipe) + if let Some((modular_recipe, _pseudo_name, _filter_tab)) = + pseudo_entries.get(selected_recipe) { Some((selected_recipe, *modular_recipe)) } else { @@ -834,8 +839,10 @@ impl<'a> Widget for Crafting<'a> { None => None, } { let recipe_name = String::from(recipe_name); - let title = if let Some((_recipe, modular_name)) = modular_entries.get(&recipe_name) { - *modular_name + let title = if let Some((_recipe, pseudo_name, _filter_tab)) = + pseudo_entries.get(&recipe_name) + { + *pseudo_name } else { &recipe.output.0.name }; @@ -853,6 +860,7 @@ impl<'a> Widget for Crafting<'a> { ModularWeapon, Component(ToolKind), Simple, + Repair, } let recipe_kind = match recipe_name.as_str() { @@ -875,37 +883,36 @@ impl<'a> Widget for Crafting<'a> { "veloren.core.pseudo_recipe.modular_weapon_component.sceptre" => { RecipeKind::Component(ToolKind::Sceptre) }, + "veloren.core.pseudo_recipe.repair" => RecipeKind::Repair, _ => RecipeKind::Simple, }; - // Output slot, tags, and modular input slots - let (modular_primary_slot, modular_secondary_slot, can_perform) = match recipe_kind { - RecipeKind::ModularWeapon | RecipeKind::Component(_) => { - let mut slot_maker = SlotMaker { - empty_slot: self.imgs.inv_slot, - filled_slot: self.imgs.inv_slot, - selected_slot: self.imgs.inv_slot_sel, - background_color: Some(UI_MAIN), - content_size: ContentSize { - width_height_ratio: 1.0, - max_fraction: 0.75, - }, - selected_content_scale: 1.067, - amount_font: self.fonts.cyri.conrod_id, - amount_margins: Vec2::new(-4.0, 0.0), - amount_font_size: self.fonts.cyri.scale(12), - amount_text_color: TEXT_COLOR, - content_source: self.inventory, - image_source: self.item_imgs, - slot_manager: Some(self.slot_manager), - pulse: self.pulse, - }; + let mut slot_maker = SlotMaker { + empty_slot: self.imgs.inv_slot, + filled_slot: self.imgs.inv_slot, + selected_slot: self.imgs.inv_slot_sel, + background_color: Some(UI_MAIN), + content_size: ContentSize { + width_height_ratio: 1.0, + max_fraction: 0.75, + }, + selected_content_scale: 1.067, + amount_font: self.fonts.cyri.conrod_id, + amount_margins: Vec2::new(-4.0, 0.0), + amount_font_size: self.fonts.cyri.scale(12), + amount_text_color: TEXT_COLOR, + content_source: self.inventory, + image_source: self.item_imgs, + slot_manager: Some(self.slot_manager), + pulse: self.pulse, + }; - if state.ids.modular_inputs.len() < 2 { + // Output slot, tags, and modular input slots + let (craft_slot_1, craft_slot_2, can_perform) = match recipe_kind { + RecipeKind::ModularWeapon | RecipeKind::Component(_) => { + if state.ids.craft_slots.len() < 2 { state.update(|s| { - s.ids - .modular_inputs - .resize(2, &mut ui.widget_id_generator()); + s.ids.craft_slots.resize(2, &mut ui.widget_id_generator()); }); } // Modular Weapon Crafting BG-Art @@ -916,7 +923,7 @@ impl<'a> Widget for Crafting<'a> { let primary_slot = CraftSlot { index: 0, - invslot: self.show.crafting_fields.recipe_inputs.get(&0).copied(), + slot: self.show.crafting_fields.recipe_inputs.get(&0).copied(), requirement: match recipe_kind { RecipeKind::ModularWeapon => |item, _, _| { matches!( @@ -939,11 +946,13 @@ impl<'a> Widget for Crafting<'a> { false } }, - RecipeKind::Simple => |_, _, _| unreachable!(), + RecipeKind::Simple | RecipeKind::Repair => |_, _, _| unreachable!(), }, info: match recipe_kind { RecipeKind::Component(toolkind) => Some(CraftSlotInfo::Tool(toolkind)), - RecipeKind::ModularWeapon | RecipeKind::Simple => None, + RecipeKind::ModularWeapon | RecipeKind::Simple | RecipeKind::Repair => { + None + }, }, }; @@ -952,10 +961,7 @@ impl<'a> Widget for Crafting<'a> { .top_left_with_margins_on(state.ids.modular_art, 4.0, 4.0) .parent(state.ids.align_ing); - if let Some(item) = primary_slot - .invslot - .and_then(|slot| self.inventory.get(slot)) - { + if let Some(item) = primary_slot.item(self.inventory) { primary_slot_widget .with_item_tooltip( self.item_tooltip_manager, @@ -963,7 +969,7 @@ impl<'a> Widget for Crafting<'a> { &None, &item_tooltip, ) - .set(state.ids.modular_inputs[0], ui); + .set(state.ids.craft_slots[0], ui); } else { let (tooltip_title, tooltip_desc) = match recipe_kind { RecipeKind::ModularWeapon => ( @@ -988,7 +994,7 @@ impl<'a> Widget for Crafting<'a> { self.localized_strings .get_msg("hud-crafting-mod_comp_wood_prim_slot_desc"), ), - RecipeKind::Component(_) | RecipeKind::Simple => { + RecipeKind::Component(_) | RecipeKind::Simple | RecipeKind::Repair => { (Cow::Borrowed(""), Cow::Borrowed("")) }, }; @@ -1000,12 +1006,12 @@ impl<'a> Widget for Crafting<'a> { &tabs_tooltip, TEXT_COLOR, ) - .set(state.ids.modular_inputs[0], ui); + .set(state.ids.craft_slots[0], ui); } let secondary_slot = CraftSlot { index: 1, - invslot: self.show.crafting_fields.recipe_inputs.get(&1).copied(), + slot: self.show.crafting_fields.recipe_inputs.get(&1).copied(), requirement: match recipe_kind { RecipeKind::ModularWeapon => |item, _, _| { matches!( @@ -1028,11 +1034,13 @@ impl<'a> Widget for Crafting<'a> { false } }, - RecipeKind::Simple => |_, _, _| unreachable!(), + RecipeKind::Simple | RecipeKind::Repair => |_, _, _| unreachable!(), }, info: match recipe_kind { RecipeKind::Component(toolkind) => Some(CraftSlotInfo::Tool(toolkind)), - RecipeKind::ModularWeapon | RecipeKind::Simple => None, + RecipeKind::ModularWeapon | RecipeKind::Simple | RecipeKind::Repair => { + None + }, }, }; @@ -1041,10 +1049,7 @@ impl<'a> Widget for Crafting<'a> { .top_right_with_margins_on(state.ids.modular_art, 4.0, 4.0) .parent(state.ids.align_ing); - if let Some(item) = secondary_slot - .invslot - .and_then(|slot| self.inventory.get(slot)) - { + if let Some(item) = secondary_slot.item(self.inventory) { secondary_slot_widget .with_item_tooltip( self.item_tooltip_manager, @@ -1052,7 +1057,7 @@ impl<'a> Widget for Crafting<'a> { &None, &item_tooltip, ) - .set(state.ids.modular_inputs[1], ui); + .set(state.ids.craft_slots[1], ui); } else { let (tooltip_title, tooltip_desc) = match recipe_kind { RecipeKind::ModularWeapon => ( @@ -1067,7 +1072,9 @@ impl<'a> Widget for Crafting<'a> { self.localized_strings .get_msg("hud-crafting-mod_comp_sec_slot_desc"), ), - RecipeKind::Simple => (Cow::Borrowed(""), Cow::Borrowed("")), + RecipeKind::Simple | RecipeKind::Repair => { + (Cow::Borrowed(""), Cow::Borrowed("")) + }, }; secondary_slot_widget .with_tooltip( @@ -1077,11 +1084,11 @@ impl<'a> Widget for Crafting<'a> { &tabs_tooltip, TEXT_COLOR, ) - .set(state.ids.modular_inputs[1], ui); + .set(state.ids.craft_slots[1], ui); } - let prim_item_placed = primary_slot.invslot.is_some(); - let sec_item_placed = secondary_slot.invslot.is_some(); + let prim_item_placed = primary_slot.slot.is_some(); + let sec_item_placed = secondary_slot.slot.is_some(); let prim_icon = match recipe_kind { RecipeKind::ModularWeapon => self.imgs.icon_primary_comp, @@ -1109,18 +1116,18 @@ impl<'a> Widget for Crafting<'a> { let bg_col = Color::Rgba(1.0, 1.0, 1.0, 0.4); if !prim_item_placed { Image::new(prim_icon) - .middle_of(state.ids.modular_inputs[0]) + .middle_of(state.ids.craft_slots[0]) .color(Some(bg_col)) .w_h(34.0, 34.0) - .graphics_for(state.ids.modular_inputs[0]) + .graphics_for(state.ids.craft_slots[0]) .set(state.ids.modular_wep_ing_1_bg, ui); } if !sec_item_placed { Image::new(sec_icon) - .middle_of(state.ids.modular_inputs[1]) + .middle_of(state.ids.craft_slots[1]) .color(Some(bg_col)) .w_h(50.0, 50.0) - .graphics_for(state.ids.modular_inputs[1]) + .graphics_for(state.ids.craft_slots[1]) .set(state.ids.modular_wep_ing_2_bg, ui); } @@ -1129,10 +1136,8 @@ impl<'a> Widget for Crafting<'a> { let output_item = match recipe_kind { RecipeKind::ModularWeapon => { - if let Some((primary_comp, toolkind, hand_restriction)) = primary_slot - .invslot - .and_then(|slot| self.inventory.get(slot)) - .and_then(|item| { + if let Some((primary_comp, toolkind, hand_restriction)) = + primary_slot.item(self.inventory).and_then(|item| { if let ItemKind::ModularComponent( ModularComponent::ToolPrimaryComponent { toolkind, @@ -1148,8 +1153,7 @@ impl<'a> Widget for Crafting<'a> { }) { secondary_slot - .invslot - .and_then(|slot| self.inventory.get(slot)) + .item(self.inventory) .filter(|item| { matches!( &*item.kind(), @@ -1174,22 +1178,19 @@ impl<'a> Widget for Crafting<'a> { } }, RecipeKind::Component(toolkind) => { - if let Some(material) = primary_slot - .invslot - .and_then(|slot| self.inventory.get(slot)) - .and_then(|item| { + if let Some(material) = + primary_slot.item(self.inventory).and_then(|item| { item.item_definition_id().itemdef_id().map(String::from) }) { let component_key = ComponentKey { toolkind, material, - modifier: secondary_slot - .invslot - .and_then(|slot| self.inventory.get(slot)) - .and_then(|item| { + modifier: secondary_slot.item(self.inventory).and_then( + |item| { item.item_definition_id().itemdef_id().map(String::from) - }), + }, + ), }; self.client.component_recipe_book().get(&component_key).map( |component_recipe| { @@ -1200,7 +1201,7 @@ impl<'a> Widget for Crafting<'a> { None } }, - RecipeKind::Simple => None, + RecipeKind::Simple | RecipeKind::Repair => None, }; if let Some(output_item) = output_item { @@ -1226,8 +1227,8 @@ impl<'a> Widget for Crafting<'a> { ) .set(state.ids.output_img, ui); ( - primary_slot.invslot, - secondary_slot.invslot, + primary_slot.slot, + secondary_slot.slot, self.show.crafting_fields.craft_sprite.map(|(_, s)| s) == recipe.craft_sprite, ) @@ -1244,7 +1245,7 @@ impl<'a> Widget for Crafting<'a> { .w_h(70.0, 70.0) .graphics_for(state.ids.output_img) .set(state.ids.modular_wep_empty_bg, ui); - (primary_slot.invslot, secondary_slot.invslot, false) + (primary_slot.slot, secondary_slot.slot, false) } }, RecipeKind::Simple => { @@ -1349,6 +1350,64 @@ impl<'a> Widget for Crafting<'a> { }), ) }, + RecipeKind::Repair => { + if state.ids.craft_slots.len() < 1 { + state.update(|s| { + s.ids.craft_slots.resize(1, &mut ui.widget_id_generator()); + }); + } + + // Slot for item to be repaired + let repair_slot = CraftSlot { + index: 0, + slot: self.show.crafting_fields.recipe_inputs.get(&0).copied(), + requirement: |item, _, _| item.durability().map_or(false, |d| d > 0), + info: None, + }; + + let repair_slot_widget = slot_maker + .fabricate(repair_slot, [40.0; 2]) + .mid_top_with_margin_on(state.ids.align_ing, 20.0) + .parent(state.ids.align_ing); + + if let Some(item) = repair_slot.item(self.inventory) { + repair_slot_widget + .with_item_tooltip( + self.item_tooltip_manager, + core::iter::once(item as &dyn ItemDesc), + &None, + &item_tooltip, + ) + .set(state.ids.craft_slots[0], ui); + } else { + repair_slot_widget + .with_tooltip( + self.tooltip_manager, + &self + .localized_strings + .get_msg("hud-crafting-repair_slot_title"), + &self + .localized_strings + .get_msg("hud-crafting-repair_slot_desc"), + &tabs_tooltip, + TEXT_COLOR, + ) + .set(state.ids.craft_slots[0], ui); + } + + let can_perform = repair_slot.item(self.inventory).map_or(false, |item| { + self.client.repair_recipe_book().repair_recipe(item).map_or( + false, + |recipe| { + recipe + .inventory_contains_ingredients(item, self.inventory) + .is_ok() + }, + ) + }); + + (repair_slot.slot, None, can_perform) + }, }; // Craft button @@ -1386,8 +1445,10 @@ impl<'a> Widget for Crafting<'a> { { match recipe_kind { RecipeKind::ModularWeapon => { - if let (Some(primary_slot), Some(secondary_slot)) = - (modular_primary_slot, modular_secondary_slot) + if let ( + Some(Slot::Inventory(primary_slot)), + Some(Slot::Inventory(secondary_slot)), + ) = (craft_slot_1, craft_slot_2) { events.push(Event::CraftModularWeapon { primary_slot, @@ -1396,11 +1457,14 @@ impl<'a> Widget for Crafting<'a> { } }, RecipeKind::Component(toolkind) => { - if let Some(primary_slot) = modular_primary_slot { + if let Some(Slot::Inventory(primary_slot)) = craft_slot_1 { events.push(Event::CraftModularWeaponComponent { toolkind, material: primary_slot, - modifier: modular_secondary_slot, + modifier: craft_slot_2.and_then(|slot| match slot { + Slot::Inventory(slot) => Some(slot), + Slot::Equip(_) => None, + }), }); } }, @@ -1408,6 +1472,11 @@ impl<'a> Widget for Crafting<'a> { recipe_name, amount: 1, }), + RecipeKind::Repair => { + if let Some(slot) = craft_slot_1 { + events.push(Event::RepairItem { slot }); + } + }, } } @@ -1477,6 +1546,9 @@ impl<'a> Widget for Crafting<'a> { RecipeKind::ModularWeapon | RecipeKind::Component(_) => { t.top_left_with_margins_on(state.ids.align_ing, 325.0, 5.0) }, + RecipeKind::Repair => { + t.top_left_with_margins_on(state.ids.align_ing, 80.0, 5.0) + }, }) .set(state.ids.req_station_title, ui); let station_img = match recipe.craft_sprite { @@ -1489,6 +1561,7 @@ impl<'a> Widget for Crafting<'a> { Some(SpriteKind::SpinningWheel) => "SpinningWheel", Some(SpriteKind::TanningRack) => "TanningRack", Some(SpriteKind::DismantlingBench) => "DismantlingBench", + Some(SpriteKind::RepairBench) => "RepairBench", None => "CraftsmanHammer", _ => "CraftsmanHammer", }; @@ -1513,6 +1586,7 @@ impl<'a> Widget for Crafting<'a> { Some(SpriteKind::SpinningWheel) => "hud-crafting-spinning_wheel", Some(SpriteKind::TanningRack) => "hud-crafting-tanning_rack", Some(SpriteKind::DismantlingBench) => "hud-crafting-salvaging_station", + Some(SpriteKind::RepairBench) => "hud-crafting-repair_bench", _ => "", }; Text::new(&self.localized_strings.get_msg(station_name)) @@ -1532,7 +1606,7 @@ impl<'a> Widget for Crafting<'a> { } // Ingredients Text // Hack from Sharp to account for iterators not having the same type - let (mut iter_a, mut iter_b, mut iter_c); + let (mut iter_a, mut iter_b, mut iter_c, mut iter_d); let ingredients = match recipe_kind { RecipeKind::Simple => { iter_a = recipe @@ -1546,15 +1620,21 @@ impl<'a> Widget for Crafting<'a> { &mut iter_b }, RecipeKind::Component(toolkind) => { - if let Some(material) = modular_primary_slot - .and_then(|slot| self.inventory.get(slot)) + if let Some(material) = craft_slot_1 + .and_then(|slot| match slot { + Slot::Inventory(slot) => self.inventory.get(slot), + Slot::Equip(_) => None, + }) .and_then(|item| item.item_definition_id().itemdef_id().map(String::from)) { let component_key = ComponentKey { toolkind, material, - modifier: modular_secondary_slot - .and_then(|slot| self.inventory.get(slot)) + modifier: craft_slot_2 + .and_then(|slot| match slot { + Slot::Inventory(slot) => self.inventory.get(slot), + Slot::Equip(_) => None, + }) .and_then(|item| { item.item_definition_id().itemdef_id().map(String::from) }), @@ -1573,6 +1653,24 @@ impl<'a> Widget for Crafting<'a> { &mut iter_b } }, + RecipeKind::Repair => { + if let Some(item) = match craft_slot_1 { + Some(Slot::Inventory(slot)) => self.inventory.get(slot), + Some(Slot::Equip(slot)) => self.inventory.equipped(slot), + None => None, + } { + if let Some(recipe) = self.client.repair_recipe_book().repair_recipe(item) { + iter_d = recipe.inputs(item); + &mut iter_d as &mut dyn ExactSizeIterator + } else { + iter_b = core::iter::empty(); + &mut iter_b + } + } else { + iter_b = core::iter::empty(); + &mut iter_b + } + }, }; let num_ingredients = ingredients.len(); @@ -1861,42 +1959,6 @@ impl<'a> Widget for Crafting<'a> { .color(TEXT_COLOR) .parent(state.ids.window) .set(state.ids.dismantle_txt, ui); - } else if *sel_crafting_tab == CraftingTab::Repair { - // Title - Text::new(&self.localized_strings.get_msg("hud-crafting-repair_title")) - .mid_top_with_margin_on(state.ids.align_ing, 0.0) - .font_id(self.fonts.cyri.conrod_id) - .font_size(self.fonts.cyri.scale(24)) - .color(TEXT_COLOR) - .parent(state.ids.window) - .set(state.ids.repair_title, ui); - - // Bench Icon - let size = 140.0; - Image::new(animate_by_pulse( - &self - .item_imgs - .img_ids_or_not_found_img(ItemKey::Simple("RepairBench".to_string())), - self.pulse, - )) - .wh([size; 2]) - .mid_top_with_margin_on(state.ids.align_ing, 50.0) - .parent(state.ids.align_ing) - .set(state.ids.repair_img, ui); - - // Explanation - - Text::new( - &self - .localized_strings - .get_msg("hud-crafting-repair_explanation"), - ) - .mid_bottom_with_margin_on(state.ids.repair_img, -60.0) - .font_id(self.fonts.cyri.conrod_id) - .font_size(self.fonts.cyri.scale(14)) - .color(TEXT_COLOR) - .parent(state.ids.window) - .set(state.ids.repair_txt, ui); } // Search / Title Recipes diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 03d0129229..b9462d8580 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -3271,6 +3271,19 @@ impl Hud { crafting::Event::ClearRecipeInputs => { self.show.crafting_fields.recipe_inputs.clear(); }, + crafting::Event::RepairItem { slot } => { + if let Some(sprite_pos) = self + .show + .crafting_fields + .craft_sprite + .map(|(pos, _sprite)| pos) + { + events.push(Event::RepairItem { + item: slot, + sprite_pos, + }); + } + }, } } } @@ -3805,7 +3818,21 @@ impl Hud { self.show .crafting_fields .recipe_inputs - .insert(c.index, i.slot); + .insert(c.index, Slot::Inventory(i.slot)); + } + } else if let (Equip(e), Crafting(c)) = (a, b) { + // Add item to crafting input + if inventories + .get(client.entity()) + .and_then(|inv| inv.equipped(e)) + .map_or(false, |item| { + (c.requirement)(item, client.component_recipe_book(), c.info) + }) + { + self.show + .crafting_fields + .recipe_inputs + .insert(c.index, Slot::Equip(e)); } } else if let (Crafting(c), Inventory(_)) = (a, b) { // Remove item from crafting input @@ -3937,17 +3964,6 @@ impl Hud { { events.push(Event::SalvageItem { slot, salvage_pos }) } - } else if self.show.crafting_fields.repair - && matches!(self.show.crafting_fields.crafting_tab, CraftingTab::Repair) - { - if let Some((sprite_pos, _sprite_kind)) = - self.show.crafting_fields.craft_sprite - { - events.push(Event::RepairItem { - item: from, - sprite_pos, - }) - } } else { events.push(Event::UseSlot { slot: from, diff --git a/voxygen/src/hud/slots.rs b/voxygen/src/hud/slots.rs index 04e52acc6e..32770ba423 100644 --- a/voxygen/src/hud/slots.rs +++ b/voxygen/src/hud/slots.rs @@ -9,7 +9,7 @@ use common::{ comp::{ ability::{Ability, AbilityInput, AuxiliaryAbility}, item::tool::{AbilityContext, ToolKind}, - slot::InvSlotId, + slot::{InvSlotId, Slot}, ActiveAbilities, Body, CharacterState, Combo, Energy, Inventory, Item, ItemKey, SkillSet, Stance, }, @@ -276,27 +276,35 @@ impl<'a> SlotKey, img_ids::Imgs> for AbilitySlot { #[derive(Clone, Copy)] pub struct CraftSlot { pub index: u32, - pub invslot: Option, + pub slot: Option, pub requirement: fn(&Item, &ComponentRecipeBook, Option) -> bool, pub info: Option, } +impl CraftSlot { + pub fn item<'a>(&'a self, inv: &'a Inventory) -> Option<&'a Item> { + match self.slot { + Some(Slot::Inventory(slot)) => inv.get(slot), + Some(Slot::Equip(slot)) => inv.equipped(slot), + None => None, + } + } +} + #[derive(Clone, Copy, Debug)] pub enum CraftSlotInfo { Tool(ToolKind), } impl PartialEq for CraftSlot { - fn eq(&self, other: &Self) -> bool { - (self.index, self.invslot) == (other.index, other.invslot) - } + fn eq(&self, other: &Self) -> bool { (self.index, self.slot) == (other.index, other.slot) } } impl Debug for CraftSlot { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { f.debug_struct("CraftSlot") .field("index", &self.index) - .field("invslot", &self.invslot) + .field("slot", &self.slot) .field("requirement", &"fn ptr") .finish() } @@ -306,14 +314,11 @@ impl SlotKey for CraftSlot { type ImageKey = ItemKey; fn image_key(&self, source: &Inventory) -> Option<(Self::ImageKey, Option)> { - self.invslot - .and_then(|invslot| source.get(invslot)) - .map(|i| (i.into(), None)) + self.item(source).map(|i| (i.into(), None)) } fn amount(&self, source: &Inventory) -> Option { - self.invslot - .and_then(|invslot| source.get(invslot)) + self.item(source) .map(|item| item.amount()) .filter(|amount| *amount > 1) } From ca879173be6e7c5ac9f5093485766f4cc199a4b0 Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 3 Aug 2022 21:31:39 -0400 Subject: [PATCH 12/28] Added a repair equipped and a repair all button. Cleaned up some hacks that used to exist. --- assets/voxygen/i18n/en/hud/crafting.ftl | 3 + common/src/comp/inventory/item/mod.rs | 12 +- common/src/comp/inventory/loadout.rs | 20 +++- common/src/comp/inventory/mod.rs | 16 ++- common/src/recipe.rs | 2 +- voxygen/src/hud/crafting.rs | 141 +++++++++++++++++++----- voxygen/src/hud/mod.rs | 9 +- voxygen/src/scene/terrain/watcher.rs | 2 +- 8 files changed, 158 insertions(+), 47 deletions(-) diff --git a/assets/voxygen/i18n/en/hud/crafting.ftl b/assets/voxygen/i18n/en/hud/crafting.ftl index 7c356bf048..0d61188054 100644 --- a/assets/voxygen/i18n/en/hud/crafting.ftl +++ b/assets/voxygen/i18n/en/hud/crafting.ftl @@ -3,6 +3,9 @@ hud-crafting-recipes = Recipes hud-crafting-ingredients = Ingredients: hud-crafting-craft = Craft hud-crafting-craft_all = Craft All +hud-crafting-repair = Repair +hud-crafting-repair_equipped = Repair Equipped +hud-crafting-repair_all = Repair All hud-crafting-tool_cata = Requires: hud-crafting-req_crafting_station = Requires: hud-crafting-anvil = Anvil diff --git a/common/src/comp/inventory/item/mod.rs b/common/src/comp/inventory/item/mod.rs index beb642bf50..b00bf7a19c 100644 --- a/common/src/comp/inventory/item/mod.rs +++ b/common/src/comp/inventory/item/mod.rs @@ -1222,10 +1222,13 @@ impl Item { } } - pub fn apply_durability(&mut self) { + pub fn apply_durability(&mut self, ability_map: &AbilityMap, msm: &MaterialStatManifest) { if let Some(durability) = &mut self.durability { *durability += 1; } + // Update item state after applying durability because stats have potential to + // change from different durability + self.update_item_state(ability_map, msm); } pub fn persistence_durability(&self) -> Option { @@ -1244,7 +1247,12 @@ impl Item { } } - pub fn reset_durability(&mut self) { self.durability = self.durability.map(|_| 0); } + pub fn reset_durability(&mut self, ability_map: &AbilityMap, msm: &MaterialStatManifest) { + self.durability = self.durability.map(|_| 0); + // Update item state after applying durability because stats have potential to + // change from different durability + self.update_item_state(ability_map, msm); + } #[cfg(test)] pub fn create_test_item_from_kind(kind: ItemKind) -> Self { diff --git a/common/src/comp/inventory/loadout.rs b/common/src/comp/inventory/loadout.rs index 25d45716f3..08192c8c18 100644 --- a/common/src/comp/inventory/loadout.rs +++ b/common/src/comp/inventory/loadout.rs @@ -311,6 +311,12 @@ impl Loadout { self.slots.iter().filter_map(|x| x.slot.as_ref()) } + pub(super) fn items_with_slot(&self) -> impl Iterator { + self.slots + .iter() + .filter_map(|x| x.slot.as_ref().map(|i| (x.equip_slot, i))) + } + /// Checks that a slot can hold a given item pub(super) fn slot_can_hold( &self, @@ -428,23 +434,25 @@ impl Loadout { .filter(|slot| slot.slot.as_ref().map_or(false, |i| i.has_durability())) .for_each(|slot| { if let Some(item) = &mut slot.slot { - item.apply_durability(); - // Update item state after applying durability because stats have potential to - // change from different durability - item.update_item_state(ability_map, msm); + item.apply_durability(ability_map, msm); } }) } /// Resets durability of item in specified slot - pub(super) fn repair_item_at_slot(&mut self, equip_slot: EquipSlot) { + pub(super) fn repair_item_at_slot( + &mut self, + equip_slot: EquipSlot, + ability_map: &item::tool::AbilityMap, + msm: &item::MaterialStatManifest, + ) { if let Some(item) = self .slots .iter_mut() .find(|slot| slot.equip_slot == equip_slot) .and_then(|slot| slot.slot.as_mut()) { - item.reset_durability(); + item.reset_durability(ability_map, msm); } } } diff --git a/common/src/comp/inventory/mod.rs b/common/src/comp/inventory/mod.rs index f10ac9f4c4..1965786433 100644 --- a/common/src/comp/inventory/mod.rs +++ b/common/src/comp/inventory/mod.rs @@ -605,6 +605,10 @@ impl Inventory { pub fn equipped_items(&self) -> impl Iterator { self.loadout.items() } + pub fn equipped_items_with_slot(&self) -> impl Iterator { + self.loadout.items_with_slot() + } + /// Replaces the loadout item (if any) in the given EquipSlot with the /// provided item, returning the item that was previously in the slot. pub fn replace_loadout_item( @@ -889,15 +893,21 @@ impl Inventory { } /// Resets durability of item in specified slot - pub fn repair_item_at_slot(&mut self, slot: Slot) { + pub fn repair_item_at_slot( + &mut self, + slot: Slot, + ability_map: &item::tool::AbilityMap, + msm: &item::MaterialStatManifest, + ) { match slot { Slot::Inventory(invslot) => { if let Some(Some(item)) = self.slot_mut(invslot).map(Option::as_mut) { - item.reset_durability(); + item.reset_durability(ability_map, msm); } }, Slot::Equip(equip_slot) => { - self.loadout.repair_item_at_slot(equip_slot); + self.loadout + .repair_item_at_slot(equip_slot, ability_map, msm); }, } } diff --git a/common/src/recipe.rs b/common/src/recipe.rs index 060d257067..0c6727f82d 100644 --- a/common/src/recipe.rs +++ b/common/src/recipe.rs @@ -1048,7 +1048,7 @@ impl RepairRecipeBook { } } - inv.repair_item_at_slot(item); + inv.repair_item_at_slot(item, ability_map, msm); Ok(()) } else { diff --git a/voxygen/src/hud/crafting.rs b/voxygen/src/hud/crafting.rs index 15eb730e56..7585fca117 100644 --- a/voxygen/src/hud/crafting.rs +++ b/voxygen/src/hud/crafting.rs @@ -86,6 +86,7 @@ widget_ids! { dismantle_title, dismantle_img, dismantle_txt, + repair_buttons[], craft_slots[], modular_art, modular_desc_txt, @@ -124,7 +125,7 @@ pub struct CraftingShow { pub crafting_search_key: Option, pub craft_sprite: Option<(Vec3, SpriteKind)>, pub salvage: bool, - pub repair: bool, + pub initialize_repair: bool, // TODO: Maybe try to do something that doesn't need to allocate? pub recipe_inputs: HashMap, } @@ -136,7 +137,7 @@ impl Default for CraftingShow { crafting_search_key: None, craft_sprite: None, salvage: false, - repair: false, + initialize_repair: false, recipe_inputs: HashMap::new(), } } @@ -212,7 +213,6 @@ pub enum CraftingTab { Utility, Glider, Dismantle, - Repair, } impl CraftingTab { @@ -229,7 +229,6 @@ impl CraftingTab { CraftingTab::Bag => "hud-crafting-tabs-bag", CraftingTab::ProcessedMaterial => "hud-crafting-tabs-processed_material", CraftingTab::Dismantle => "hud-crafting-tabs-dismantle", - CraftingTab::Repair => "hud-crafting-tabs-repair", } } @@ -247,14 +246,13 @@ impl CraftingTab { CraftingTab::ProcessedMaterial => imgs.icon_processed_material, // These tabs are never shown, so using not found is fine CraftingTab::Dismantle => imgs.not_found, - CraftingTab::Repair => imgs.not_found, } } fn satisfies(self, recipe: &Recipe) -> bool { let (item, _count) = &recipe.output; match self { - CraftingTab::All | CraftingTab::Dismantle | CraftingTab::Repair => true, + CraftingTab::All | CraftingTab::Dismantle => true, CraftingTab::Food => item.tags().contains(&ItemTag::Food), CraftingTab::Armor => match &*item.kind() { ItemKind::Armor(_) => !item.tags().contains(&ItemTag::Bag), @@ -325,6 +323,16 @@ impl<'a> Widget for Crafting<'a> { let mut events = Vec::new(); + // Handle any initialization + // TODO: Replace with struct instead of making assorted booleans once there is + // more than 1 field. + if self.show.crafting_fields.initialize_repair { + state.update(|s| { + s.selected_recipe = Some(String::from("veloren.core.pseudo_recipe.repair")) + }); + } + self.show.crafting_fields.initialize_repair = false; + // Tooltips let item_tooltip = ItemTooltip::new( { @@ -570,7 +578,7 @@ impl<'a> Widget for Crafting<'a> { ); pseudo_entries.insert( String::from("veloren.core.pseudo_recipe.repair"), - (&repair_recipe, "Repair Equipment", CraftingTab::Repair), + (&repair_recipe, "Repair Equipment", CraftingTab::All), ); pseudo_entries }; @@ -1356,6 +1364,13 @@ impl<'a> Widget for Crafting<'a> { s.ids.craft_slots.resize(1, &mut ui.widget_id_generator()); }); } + if state.ids.repair_buttons.len() < 2 { + state.update(|s| { + s.ids + .repair_buttons + .resize(2, &mut ui.widget_id_generator()); + }); + } // Slot for item to be repaired let repair_slot = CraftSlot { @@ -1367,7 +1382,7 @@ impl<'a> Widget for Crafting<'a> { let repair_slot_widget = slot_maker .fabricate(repair_slot, [40.0; 2]) - .mid_top_with_margin_on(state.ids.align_ing, 20.0) + .top_left_with_margins_on(state.ids.align_ing, 20.0, 40.0) .parent(state.ids.align_ing); if let Some(item) = repair_slot.item(self.inventory) { @@ -1395,16 +1410,85 @@ impl<'a> Widget for Crafting<'a> { .set(state.ids.craft_slots[0], ui); } - let can_perform = repair_slot.item(self.inventory).map_or(false, |item| { - self.client.repair_recipe_book().repair_recipe(item).map_or( - false, - |recipe| { - recipe - .inventory_contains_ingredients(item, self.inventory) - .is_ok() - }, + let can_repair = |item: &Item| { + // Check that item needs to be repaired, and that inventory has sufficient + // materials to repair + item.durability().map_or(false, |d| d > 0) + && self.client.repair_recipe_book().repair_recipe(item).map_or( + false, + |recipe| { + recipe + .inventory_contains_ingredients(item, self.inventory) + .is_ok() + }, + ) + }; + + // Repair equipped button + if Button::image(self.imgs.button) + .w_h(105.0, 25.0) + .hover_image(self.imgs.button_hover) + .press_image(self.imgs.button_press) + .label( + &self + .localized_strings + .get_msg("hud-crafting-repair_equipped"), ) - }); + .label_y(conrod_core::position::Relative::Scalar(1.0)) + .label_color(TEXT_COLOR) + .label_font_size(self.fonts.cyri.scale(12)) + .label_font_id(self.fonts.cyri.conrod_id) + .image_color(TEXT_COLOR) + .top_right_with_margins_on(state.ids.align_ing, 20.0, 20.0) + .set(state.ids.repair_buttons[0], ui) + .was_clicked() + { + self.inventory + .equipped_items_with_slot() + .filter(|(_, item)| can_repair(item)) + .for_each(|(slot, _)| { + events.push(Event::RepairItem { + slot: Slot::Equip(slot), + }); + }) + } + + // Repair all button + if Button::image(self.imgs.button) + .w_h(105.0, 25.0) + .hover_image(self.imgs.button_hover) + .press_image(self.imgs.button_press) + .label(&self.localized_strings.get_msg("hud-crafting-repair_all")) + .label_y(conrod_core::position::Relative::Scalar(1.0)) + .label_color(TEXT_COLOR) + .label_font_size(self.fonts.cyri.scale(12)) + .label_font_id(self.fonts.cyri.conrod_id) + .image_color(TEXT_COLOR) + .mid_bottom_with_margin_on(state.ids.repair_buttons[0], -45.0) + .set(state.ids.repair_buttons[1], ui) + .was_clicked() + { + self.inventory + .equipped_items_with_slot() + .filter(|(_, item)| can_repair(item)) + .for_each(|(slot, _)| { + events.push(Event::RepairItem { + slot: Slot::Equip(slot), + }); + }); + self.inventory + .slots_with_id() + .filter(|(_, item)| item.as_ref().map_or(false, |i| can_repair(i))) + .for_each(|(slot, _)| { + events.push(Event::RepairItem { + slot: Slot::Inventory(slot), + }); + }); + } + + let can_perform = repair_slot + .item(self.inventory) + .map_or(false, |item| can_repair(item)); (repair_slot.slot, None, can_perform) }, @@ -1413,17 +1497,20 @@ impl<'a> Widget for Crafting<'a> { // Craft button if Button::image(self.imgs.button) .w_h(105.0, 25.0) - .hover_image(if can_perform { - self.imgs.button_hover - } else { - self.imgs.button + .hover_image( + can_perform + .then_some(self.imgs.button_hover) + .unwrap_or(self.imgs.button), + ) + .press_image( + can_perform + .then_some(self.imgs.button_press) + .unwrap_or(self.imgs.button), + ) + .label(&match recipe_kind { + RecipeKind::Repair => self.localized_strings.get_msg("hud-crafting-repair"), + _ => self.localized_strings.get_msg("hud-crafting-craft"), }) - .press_image(if can_perform { - self.imgs.button_press - } else { - self.imgs.button - }) - .label(&self.localized_strings.get_msg("hud-crafting-craft")) .label_y(conrod_core::position::Relative::Scalar(1.0)) .label_color(if can_perform { TEXT_COLOR diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index b9462d8580..21350db11e 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -922,7 +922,6 @@ impl Show { self.bag = open; self.map = false; self.crafting_fields.salvage = false; - self.crafting_fields.repair = false; if !open { self.crafting = false; @@ -947,7 +946,6 @@ impl Show { self.bag = false; self.crafting = false; self.crafting_fields.salvage = false; - self.crafting_fields.repair = false; self.social = false; self.quest = false; self.diary = false; @@ -984,7 +982,6 @@ impl Show { } self.crafting = open; self.crafting_fields.salvage = false; - self.crafting_fields.repair = false; self.crafting_fields.recipe_inputs = HashMap::new(); self.bag = open; self.map = false; @@ -1004,10 +1001,10 @@ impl Show { self.crafting_fields.craft_sprite, Some((_, SpriteKind::DismantlingBench)) ) && matches!(tab, CraftingTab::Dismantle); - self.crafting_fields.repair = matches!( + self.crafting_fields.initialize_repair = matches!( self.crafting_fields.craft_sprite, Some((_, SpriteKind::RepairBench)) - ) && matches!(tab, CraftingTab::Repair); + ); } fn diary(&mut self, open: bool) { @@ -1016,7 +1013,6 @@ impl Show { self.quest = false; self.crafting = false; self.crafting_fields.salvage = false; - self.crafting_fields.repair = false; self.bag = false; self.map = false; self.diary_fields = diary::DiaryShow::default(); @@ -1037,7 +1033,6 @@ impl Show { self.quest = false; self.crafting = false; self.crafting_fields.salvage = false; - self.crafting_fields.repair = false; self.diary = false; self.want_grab = !self.any_window_requires_cursor(); } diff --git a/voxygen/src/scene/terrain/watcher.rs b/voxygen/src/scene/terrain/watcher.rs index d73ee86a95..5b0ec09b90 100644 --- a/voxygen/src/scene/terrain/watcher.rs +++ b/voxygen/src/scene/terrain/watcher.rs @@ -166,7 +166,7 @@ impl BlocksOfInterest { interactables.push((pos, Interaction::Craft(CraftingTab::Dismantle))) }, Some(SpriteKind::RepairBench) => { - interactables.push((pos, Interaction::Craft(CraftingTab::Repair))) + interactables.push((pos, Interaction::Craft(CraftingTab::All))) }, _ => {}, }, From 807a8bcea0873bd88e849796ff6c8a38963aa925 Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 3 Aug 2022 21:32:56 -0400 Subject: [PATCH 13/28] Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ccc82d68a..6a3d3e8a46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Custom spots can be added without recompilation (only ron and vox files) - Setting in userdata/server/server_config/settings.ron that controls the length of each day/night cycle. - Starting site can now be chosen during character creation +- Durability loss of equipped items on death ### Changed - Bats move slower and use a simple proportional controller to maintain altitude From e4ebd173635e319f3e57c65269facdfcdf3bc6ff Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 4 Aug 2022 21:40:40 -0400 Subject: [PATCH 14/28] Addressed feedback --- assets/voxygen/i18n/en/hud/crafting.ftl | 4 ---- common/src/comp/inventory/item/armor.rs | 2 +- common/src/comp/inventory/item/tool.rs | 5 ++-- voxygen/src/hud/util.rs | 11 ++++++++- voxygen/src/ui/widgets/item_tooltip.rs | 31 +++++++++++++++++++++++-- 5 files changed, 43 insertions(+), 10 deletions(-) diff --git a/assets/voxygen/i18n/en/hud/crafting.ftl b/assets/voxygen/i18n/en/hud/crafting.ftl index 0d61188054..2f580931c7 100644 --- a/assets/voxygen/i18n/en/hud/crafting.ftl +++ b/assets/voxygen/i18n/en/hud/crafting.ftl @@ -30,16 +30,12 @@ hud-crafting-tabs-utility = Utility hud-crafting-tabs-weapon = Weapons hud-crafting-tabs-bag = Bags hud-crafting-tabs-processed_material = Materials -hud-crafting-tabs-repair = Repair hud-crafting-dismantle_title = Dismantling hud-crafting-dismantle_explanation = Hover items in your bag to see what you can salvage. Double-Click them to start dismantling. -hud-crafting-repair_title = Repair Items -hud-crafting-repair_slot_title = Damaged Item -hud-crafting-repair_slot_desc = Place a damaged item here to repair it. hud-crafting-modular_desc = Drag Item-Parts here to craft a weapon hud-crafting-mod_weap_prim_slot_title = Primary Weapon Component hud-crafting-mod_weap_prim_slot_desc = Place a primary weapon component here (e.g. a sword blade, axe head, or bow limbs). diff --git a/common/src/comp/inventory/item/armor.rs b/common/src/comp/inventory/item/armor.rs index 1c8efe8458..4ef7b7c595 100644 --- a/common/src/comp/inventory/item/armor.rs +++ b/common/src/comp/inventory/item/armor.rs @@ -38,7 +38,7 @@ impl ArmorKind { Back => true, Ring => false, Neck => false, - Head => false, + Head => true, Tabard => false, Bag => false, } diff --git a/common/src/comp/inventory/item/tool.rs b/common/src/comp/inventory/item/tool.rs index 3309725771..71df384890 100644 --- a/common/src/comp/inventory/item/tool.rs +++ b/common/src/comp/inventory/item/tool.rs @@ -218,11 +218,12 @@ impl Mul for Stats { fn mul(self, value: DurabilityMultiplier) -> Self { let DurabilityMultiplier(value) = value; + let less_scaled = value * 0.5 + 0.5; Self { - equip_time_secs: self.equip_time_secs / value.max(0.01), + equip_time_secs: self.equip_time_secs / less_scaled.max(0.01), power: self.power * value, effect_power: self.effect_power * value, - speed: self.speed * value, + speed: self.speed * less_scaled, crit_chance: self.crit_chance * value, range: self.range * value, energy_efficiency: self.energy_efficiency * value, diff --git a/voxygen/src/hud/util.rs b/voxygen/src/hud/util.rs index 0dd1abebc1..ffef7e3540 100644 --- a/voxygen/src/hud/util.rs +++ b/voxygen/src/hud/util.rs @@ -5,7 +5,8 @@ use common::{ item::{ armor::{Armor, ArmorKind, Protection}, tool::{Hands, Tool, ToolKind}, - Effects, ItemDefinitionId, ItemDesc, ItemKind, MaterialKind, MaterialStatManifest, + Effects, Item, ItemDefinitionId, ItemDesc, ItemKind, MaterialKind, + MaterialStatManifest, }, BuffKind, }, @@ -355,6 +356,14 @@ pub fn protec2string(stat: Protection) -> String { } } +/// Gets the durability of an item in a format more intuitive for UI +pub fn item_durability(item: &dyn ItemDesc) -> Option { + let durability = item + .durability() + .or_else(|| item.has_durability().then_some(0)); + durability.map(|d| Item::MAX_DURABILITY - d) +} + pub fn ability_image(imgs: &img_ids::Imgs, ability_id: &str) -> image::Id { match ability_id { // Debug stick diff --git a/voxygen/src/ui/widgets/item_tooltip.rs b/voxygen/src/ui/widgets/item_tooltip.rs index e6db678eb8..761fa0818e 100644 --- a/voxygen/src/ui/widgets/item_tooltip.rs +++ b/voxygen/src/ui/widgets/item_tooltip.rs @@ -711,6 +711,13 @@ impl<'a> Widget for ItemTooltip<'a> { equipped_tool_stats.buff_strength, ); + let tool_durability = + util::item_durability(item).unwrap_or(Item::MAX_DURABILITY); + let equipped_durability = + util::item_durability(equipped_item).unwrap_or(Item::MAX_DURABILITY); + let durability_diff = + util::comparison(tool_durability, equipped_durability); + let mut diff_text = |text: String, color, id_index| { widget::Text::new(&text) .align_middle_y_of(state.ids.stats[id_index]) @@ -766,6 +773,14 @@ impl<'a> Widget for ItemTooltip<'a> { ); diff_text(text, buff_strength_diff.1, 6) } + if tool_durability != equipped_durability { + let text = format!( + "{} {}", + &durability_diff.0, + tool_durability as i32 - equipped_durability as i32 + ); + diff_text(text, durability_diff.1, 7) + } } } }, @@ -890,8 +905,7 @@ impl<'a> Widget for ItemTooltip<'a> { index += 1; } - if item.has_durability() { - let durability = Item::MAX_DURABILITY - item.durability().unwrap_or(0); + if let Some(durability) = util::item_durability(item) { stat_text( format!( "{} : {}/{}", @@ -931,6 +945,11 @@ impl<'a> Widget for ItemTooltip<'a> { let stealth_diff = util::option_comparison(&armor_stats.stealth, &equipped_stats.stealth); + let armor_durability = util::item_durability(item); + let equipped_durability = util::item_durability(equipped_item); + let durability_diff = + util::option_comparison(&armor_durability, &equipped_durability); + let mut diff_text = |text: String, color, id_index| { widget::Text::new(&text) .align_middle_y_of(state.ids.stats[id_index]) @@ -999,6 +1018,14 @@ impl<'a> Widget for ItemTooltip<'a> { diff_text(text, stealth_diff.1, index); } } + index += armor_stats.stealth.is_some() as usize; + + if armor_durability != equipped_durability { + let diff = armor_durability.unwrap_or(Item::MAX_DURABILITY) as i32 + - equipped_durability.unwrap_or(Item::MAX_DURABILITY) as i32; + let text = format!("{} {}", &durability_diff.0, diff); + diff_text(text, durability_diff.1, index); + } } } }, From 3dae7b6d4ac97d61e13d33005bf65d1c43b5e13a Mon Sep 17 00:00:00 2001 From: UncomfySilence Date: Fri, 5 Aug 2022 12:47:13 +0200 Subject: [PATCH 15/28] small tweaks to repair recipe and addition of cultist, and uncraftable weapon repairs --- assets/common/repair_recipe_book.ron | 139 ++++++++++++++++++++++++++- 1 file changed, 138 insertions(+), 1 deletion(-) diff --git a/assets/common/repair_recipe_book.ron b/assets/common/repair_recipe_book.ron index 640498f2af..4491214f2a 100644 --- a/assets/common/repair_recipe_book.ron +++ b/assets/common/repair_recipe_book.ron @@ -21,21 +21,25 @@ recipes: { ModularWeapon(material: "common.items.mineral.ingot.steel"): ( inputs: [ (Item("common.items.mineral.ingot.steel"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 1), ], ), ModularWeapon(material: "common.items.mineral.ingot.cobalt"): ( inputs: [ (Item("common.items.mineral.ingot.cobalt"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 2), ], ), ModularWeapon(material: "common.items.mineral.ingot.bloodsteel"): ( inputs: [ (Item("common.items.mineral.ingot.bloodsteel"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 3), ], ), ModularWeapon(material: "common.items.mineral.ingot.orichalcum"): ( inputs: [ (Item("common.items.mineral.ingot.orichalcum"), 1), + (Item("common.items.mineral.ore.velorite"), 1), ], ), ModularWeapon(material: "common.items.log.wood"): ( @@ -51,23 +55,100 @@ recipes: { ModularWeapon(material: "common.items.log.hardwood"): ( inputs: [ (Item("common.items.log.hardwood"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 1), ], ), ModularWeapon(material: "common.items.log.ironwood"): ( inputs: [ (Item("common.items.log.ironwood"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 2), ], ), ModularWeapon(material: "common.items.log.frostwood"): ( inputs: [ (Item("common.items.log.frostwood"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 3), ], ), ModularWeapon(material: "common.items.log.eldwood"): ( inputs: [ (Item("common.items.log.eldwood"), 1), + (Item("common.items.mineral.ore.velorite"), 1), ], ), + // UNCRAFTABLE WEAPONS + ItemDefId("common.items.weapons.sword.cultist"): ( + inputs: [ + (Item("common.items.mineral.ore.veloritefrag"), 8), + ], + ), + ItemDefId("common.items.weapons.axe.malachite"): ( + inputs: [ + (Item("common.items.mineral.ore.velorite"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 3), + ], + ), + ItemDefId("common.items.weapons.hammer.cultist_purp_2h-0"): ( + inputs: [ + (Item("common.items.mineral.ore.veloritefrag"), 8), + ], + ), + ItemDefId("common.items.weapons.bow.velorite"): ( + inputs: [ + (Item("common.items.mineral.ore.velorite"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 3), + ], + ), + ItemDefId("common.items.weapons.staff.cultist_staff"): ( + inputs: [ + (Item("common.items.mineral.ore.veloritefrag"), 8), + ], + ), + ItemDefId("common.items.weapons.scepter.root_evil"): ( + inputs: [ + (Item("common.items.mineral.ore.veloritefrag"), 8), + ], + ), + ItemDefId("common.items.weapons.scepter.sceptre_velorite_0"): ( + inputs: [ + (Item("common.items.mineral.ore.velorite"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 3), + ], + ), + ItemDefId("common.items.weapons.sword.caladbolg"): ( + inputs: [ + (Item("common.items.mineral.ore.velorite"), 5), + ], + ), + ItemDefId("common.items.weapons.axe.parashu"): ( + inputs: [ + (Item("common.items.mineral.ore.velorite"), 5), + ], + ), + ItemDefId("common.items.weapons.hammer.mjolnir"): ( + inputs: [ + (Item("common.items.mineral.ore.velorite"), 5), + ], + ), + ItemDefId("common.items.weapons.bow.sagitta"): ( + inputs: [ + (Item("common.items.mineral.ore.velorite"), 5), + ], + ), + ItemDefId("common.items.weapons.staff.laevateinn"): ( + inputs: [ + (Item("common.items.mineral.ore.velorite"), 5), + ], + ), + ItemDefId("common.items.weapons.scepter.caduceus"): ( + inputs: [ + (Item("common.items.mineral.ore.velorite"), 5), + ], + ), + // ARMOR + ItemDefId("common.items.armor.misc.chest.worker_purple_0"): ( inputs: [], ), + ItemDefId("common.items.armor.misc.pants.worker_brown"): ( inputs: [], ), + ItemDefId("common.items.armor.misc.foot.sandals"): ( inputs: [], ), // ARMOR/HIDE/RAWHIDE ItemDefId("common.items.armor.hide.rawhide.back"): ( inputs: [ @@ -288,7 +369,7 @@ recipes: { (Item("common.items.mineral.ore.velorite"), 3), ], ), - ItemDefId("common.items.armorhide.dragonscale.foot"): ( + ItemDefId("common.items.armor.hide.dragonscale.foot"): ( inputs: [ (Item("common.items.crafting_ing.hide.dragon_scale"), 1), (Item("common.items.mineral.ore.velorite"), 2), @@ -800,7 +881,63 @@ recipes: { (Item("common.items.mineral.ore.velorite"), 3), ], ), + // ARMOR/CULTIST + ItemDefId("common.items.armor.cultist.back"): ( + inputs: [ + (Item("common.items.crafting_ing.leather.rigid_leather"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 3), + ], + ), + ItemDefId("common.items.armor.cultist.belt"): ( + inputs: [ + (Item("common.items.crafting_ing.leather.rigid_leather"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 3), + ], + ), + ItemDefId("common.items.armor.cultist.chest"): ( + inputs: [ + (Item("common.items.food.spore_corruption"), 1), + (Item("common.items.crafting_ing.leather.rigid_leather"), 4), + (Item("common.items.mineral.ore.veloritefrag"), 7), + ], + ), + ItemDefId("common.items.armor.cultist.foot"): ( + inputs: [ + (Item("common.items.crafting_ing.leather.rigid_leather"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 3), + ], + ), + ItemDefId("common.items.armor.cultist.hand"): ( + inputs: [ + (Item("common.items.crafting_ing.leather.rigid_leather"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 3), + ], + ), + ItemDefId("common.items.armor.cultist.pants"): ( + inputs: [ + (Item("common.items.food.spore_corruption"), 1), + (Item("common.items.crafting_ing.leather.rigid_leather"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 5), + ], + ), + ItemDefId("common.items.armor.cultist.shoulder"): ( + inputs: [ + (Item("common.items.food.spore_corruption"), 1), + (Item("common.items.crafting_ing.leather.rigid_leather"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 5), + ], + ), // MISC/HEAD + ItemDefId("common.items.armor.misc.head.bandana.red"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.linen_red"), 1), + ], + ), + ItemDefId("common.items.armor.misc.head.bandana.thief"): ( + inputs: [ + (Item("common.items.crafting_ing.cloth.linen"), 2), + ], + ), ItemDefId("common.items.armor.misc.head.hood"): ( inputs: [ (Item("common.items.crafting_ing.leather.thick_leather"), 1), From 00710a4eb05051715d917c8d0270ece44820c793 Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 6 Aug 2022 15:09:32 -0400 Subject: [PATCH 16/28] Fixed csv tools --- common/src/bin/csv_export/main.rs | 35 +++++++++++-------- ...urability.sql => V51__item_durability.sql} | 0 voxygen/src/hud/crafting.rs | 20 +++++------ 3 files changed, 30 insertions(+), 25 deletions(-) rename server/src/migrations/{V50__item_durability.sql => V51__item_durability.sql} (100%) diff --git a/common/src/bin/csv_export/main.rs b/common/src/bin/csv_export/main.rs index 9ea38040bd..5b41bd5d86 100644 --- a/common/src/bin/csv_export/main.rs +++ b/common/src/bin/csv_export/main.rs @@ -15,7 +15,7 @@ use veloren_common::{ armor::{ArmorKind, Protection}, modular::{generate_weapon_primary_components, generate_weapons}, tool::{Hands, Tool, ToolKind}, - Item, MaterialStatManifest, + DurabilityMultiplier, Item, MaterialStatManifest, }, }, generation::{EntityConfig, EntityInfo}, @@ -58,21 +58,23 @@ fn armor_stats() -> Result<(), Box> { } let msm = &MaterialStatManifest::load().read(); + let dur_mult = DurabilityMultiplier(1.0); + let armor_stats = armor.stats(msm, dur_mult); - let protection = match armor.stats(msm).protection { + let protection = match armor_stats.protection { Some(Protection::Invincible) => "Invincible".to_string(), Some(Protection::Normal(value)) => value.to_string(), None => "0.0".to_string(), }; - let poise_resilience = match armor.stats(msm).poise_resilience { + let poise_resilience = match armor_stats.poise_resilience { Some(Protection::Invincible) => "Invincible".to_string(), Some(Protection::Normal(value)) => value.to_string(), None => "0.0".to_string(), }; - let max_energy = armor.stats(msm).energy_max.unwrap_or(0.0).to_string(); - let energy_reward = armor.stats(msm).energy_reward.unwrap_or(0.0).to_string(); - let crit_power = armor.stats(msm).crit_power.unwrap_or(0.0).to_string(); - let stealth = armor.stats(msm).stealth.unwrap_or(0.0).to_string(); + let max_energy = armor_stats.energy_max.unwrap_or(0.0).to_string(); + let energy_reward = armor_stats.energy_reward.unwrap_or(0.0).to_string(); + let crit_power = armor_stats.crit_power.unwrap_or(0.0).to_string(); + let stealth = armor_stats.stealth.unwrap_or(0.0).to_string(); wtr.write_record([ item.item_definition_id() @@ -124,14 +126,17 @@ fn weapon_stats() -> Result<(), Box> { for item in items.iter() { if let comp::item::ItemKind::Tool(tool) = &*item.kind() { - let power = tool.base_power().to_string(); - let effect_power = tool.base_effect_power().to_string(); - let speed = tool.base_speed().to_string(); - let crit_chance = tool.base_crit_chance().to_string(); - let range = tool.base_range().to_string(); - let energy_efficiency = tool.base_energy_efficiency().to_string(); - let buff_strength = tool.base_buff_strength().to_string(); - let equip_time = tool.equip_time().as_secs_f32().to_string(); + let dur_mult = DurabilityMultiplier(1.0); + let tool_stats = tool.stats(dur_mult); + + let power = tool_stats.power.to_string(); + let effect_power = tool_stats.effect_power.to_string(); + let speed = tool_stats.speed.to_string(); + let crit_chance = tool_stats.crit_chance.to_string(); + let range = tool_stats.range.to_string(); + let energy_efficiency = tool_stats.energy_efficiency.to_string(); + let buff_strength = tool_stats.buff_strength.to_string(); + let equip_time = tool_stats.equip_time_secs.to_string(); let kind = get_tool_kind(&tool.kind); let hands = get_tool_hands(tool); diff --git a/server/src/migrations/V50__item_durability.sql b/server/src/migrations/V51__item_durability.sql similarity index 100% rename from server/src/migrations/V50__item_durability.sql rename to server/src/migrations/V51__item_durability.sql diff --git a/voxygen/src/hud/crafting.rs b/voxygen/src/hud/crafting.rs index 7585fca117..4207147773 100644 --- a/voxygen/src/hud/crafting.rs +++ b/voxygen/src/hud/crafting.rs @@ -1497,16 +1497,16 @@ impl<'a> Widget for Crafting<'a> { // Craft button if Button::image(self.imgs.button) .w_h(105.0, 25.0) - .hover_image( - can_perform - .then_some(self.imgs.button_hover) - .unwrap_or(self.imgs.button), - ) - .press_image( - can_perform - .then_some(self.imgs.button_press) - .unwrap_or(self.imgs.button), - ) + .hover_image(if can_perform { + self.imgs.button_hover + } else { + self.imgs.button + }) + .press_image(if can_perform { + self.imgs.button_press + } else { + self.imgs.button + }) .label(&match recipe_kind { RecipeKind::Repair => self.localized_strings.get_msg("hud-crafting-repair"), _ => self.localized_strings.get_msg("hud-crafting-craft"), From ff9f755c0c8baacda2033e9d39aa0706b2cc4bd6 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 12 Jan 2023 22:07:45 -0500 Subject: [PATCH 17/28] Changed repair recipe to round down on material costs. --- common/src/recipe.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/recipe.rs b/common/src/recipe.rs index 0c6727f82d..dc9a2f7df6 100644 --- a/common/src/recipe.rs +++ b/common/src/recipe.rs @@ -982,7 +982,7 @@ impl RepairRecipe { pub fn inputs(&self, item: &Item) -> impl ExactSizeIterator { let item_durability = item.durability().unwrap_or(0); self.inputs.iter().map(move |(input, amount)| { - let amount = amount.mul(item_durability).div_ceil(Item::MAX_DURABILITY); + let amount = amount.mul(item_durability).div_floor(Item::MAX_DURABILITY); (input, amount) }) } From 6a8fc3a423046ac5f2bb5d0a865970e067cf33fc Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 16 Feb 2023 21:38:19 -0500 Subject: [PATCH 18/28] Tweaked repair recipes --- assets/common/repair_recipe_book.ron | 228 ++++++++++++++------------- 1 file changed, 115 insertions(+), 113 deletions(-) diff --git a/assets/common/repair_recipe_book.ron b/assets/common/repair_recipe_book.ron index 4491214f2a..7aa2f28b81 100644 --- a/assets/common/repair_recipe_book.ron +++ b/assets/common/repair_recipe_book.ron @@ -77,9 +77,14 @@ recipes: { ], ), // UNCRAFTABLE WEAPONS + ItemDefId("common.items.weapons.hammer.burnt_drumstick"): ( + inputs: [ + (Item("common.items.food.meat.bird_large_cooked"), 1), + ], + ), ItemDefId("common.items.weapons.sword.cultist"): ( inputs: [ - (Item("common.items.mineral.ore.veloritefrag"), 8), + (Item("common.items.mineral.ore.veloritefrag"), 4), ], ), ItemDefId("common.items.weapons.axe.malachite"): ( @@ -90,7 +95,7 @@ recipes: { ), ItemDefId("common.items.weapons.hammer.cultist_purp_2h-0"): ( inputs: [ - (Item("common.items.mineral.ore.veloritefrag"), 8), + (Item("common.items.mineral.ore.veloritefrag"), 4), ], ), ItemDefId("common.items.weapons.bow.velorite"): ( @@ -101,12 +106,12 @@ recipes: { ), ItemDefId("common.items.weapons.staff.cultist_staff"): ( inputs: [ - (Item("common.items.mineral.ore.veloritefrag"), 8), + (Item("common.items.mineral.ore.veloritefrag"), 4), ], ), ItemDefId("common.items.weapons.scepter.root_evil"): ( inputs: [ - (Item("common.items.mineral.ore.veloritefrag"), 8), + (Item("common.items.mineral.ore.veloritefrag"), 4), ], ), ItemDefId("common.items.weapons.scepter.sceptre_velorite_0"): ( @@ -162,7 +167,7 @@ recipes: { ), ItemDefId("common.items.armor.hide.rawhide.chest"): ( inputs: [ - (Item("common.items.crafting_ing.leather.simple_leather"), 4), + (Item("common.items.crafting_ing.leather.simple_leather"), 3), ], ), ItemDefId("common.items.armorhide.rawhide.foot"): ( @@ -177,12 +182,12 @@ recipes: { ), ItemDefId("common.items.armor.hide.rawhide.pants"): ( inputs: [ - (Item("common.items.crafting_ing.leather.simple_leather"), 3), + (Item("common.items.crafting_ing.leather.simple_leather"), 2), ], ), ItemDefId("common.items.armor.hide.rawhide.shoulder"): ( inputs: [ - (Item("common.items.crafting_ing.leather.simple_leather"), 3), + (Item("common.items.crafting_ing.leather.simple_leather"), 2), ], ), // ARMOR/HIDE/LEATHER @@ -198,7 +203,7 @@ recipes: { ), ItemDefId("common.items.armor.hide.leather.chest"): ( inputs: [ - (Item("common.items.crafting_ing.leather.thick_leather"), 4), + (Item("common.items.crafting_ing.leather.thick_leather"), 3), ], ), ItemDefId("common.items.armorhide.leather.foot"): ( @@ -213,12 +218,12 @@ recipes: { ), ItemDefId("common.items.armor.hide.leather.pants"): ( inputs: [ - (Item("common.items.crafting_ing.leather.thick_leather"), 3), + (Item("common.items.crafting_ing.leather.thick_leather"), 2), ], ), ItemDefId("common.items.armor.hide.leather.shoulder"): ( inputs: [ - (Item("common.items.crafting_ing.leather.thick_leather"), 3), + (Item("common.items.crafting_ing.leather.thick_leather"), 2), ], ), // ARMOR/HIDE/SCALE @@ -236,8 +241,8 @@ recipes: { ), ItemDefId("common.items.armor.hide.scale.chest"): ( inputs: [ - (Item("common.items.crafting_ing.hide.scales"), 4), - (Item("common.items.mineral.ore.veloritefrag"), 7), + (Item("common.items.crafting_ing.hide.scales"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 3), ], ), ItemDefId("common.items.armorhide.scale.foot"): ( @@ -254,14 +259,14 @@ recipes: { ), ItemDefId("common.items.armor.hide.scale.pants"): ( inputs: [ - (Item("common.items.crafting_ing.hide.scales"), 3), - (Item("common.items.mineral.ore.veloritefrag"), 5), + (Item("common.items.crafting_ing.hide.scales"), 2), + (Item("common.items.mineral.ore.veloritefrag"), 2), ], ), ItemDefId("common.items.armor.hide.scale.shoulder"): ( inputs: [ - (Item("common.items.crafting_ing.hide.scales"), 3), - (Item("common.items.mineral.ore.veloritefrag"), 5), + (Item("common.items.crafting_ing.hide.scales"), 2), + (Item("common.items.mineral.ore.veloritefrag"), 2), ], ), // ARMOR/HIDE/CARAPACE @@ -279,8 +284,8 @@ recipes: { ), ItemDefId("common.items.armor.hide.carapace.chest"): ( inputs: [ - (Item("common.items.crafting_ing.hide.carapace"), 4), - (Item("common.items.mineral.ore.veloritefrag"), 10), + (Item("common.items.crafting_ing.hide.carapace"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 6), ], ), ItemDefId("common.items.armorhide.carapace.foot"): ( @@ -297,14 +302,14 @@ recipes: { ), ItemDefId("common.items.armor.hide.carapace.pants"): ( inputs: [ - (Item("common.items.crafting_ing.hide.carapace"), 3), - (Item("common.items.mineral.ore.veloritefrag"), 7), + (Item("common.items.crafting_ing.hide.carapace"), 2), + (Item("common.items.mineral.ore.veloritefrag"), 4), ], ), ItemDefId("common.items.armor.hide.carapace.shoulder"): ( inputs: [ - (Item("common.items.crafting_ing.hide.carapace"), 3), - (Item("common.items.mineral.ore.veloritefrag"), 7), + (Item("common.items.crafting_ing.hide.carapace"), 2), + (Item("common.items.mineral.ore.veloritefrag"), 4), ], ), // ARMOR/HIDE/PRIMAL @@ -322,8 +327,8 @@ recipes: { ), ItemDefId("common.items.armor.hide.primal.chest"): ( inputs: [ - (Item("common.items.crafting_ing.hide.plate"), 4), - (Item("common.items.mineral.ore.veloritefrag"), 14), + (Item("common.items.crafting_ing.hide.plate"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 9), ], ), ItemDefId("common.items.armorhide.primal.foot"): ( @@ -340,14 +345,14 @@ recipes: { ), ItemDefId("common.items.armor.hide.primal.pants"): ( inputs: [ - (Item("common.items.crafting_ing.hide.plate"), 3), - (Item("common.items.mineral.ore.veloritefrag"), 10), + (Item("common.items.crafting_ing.hide.plate"), 2), + (Item("common.items.mineral.ore.veloritefrag"), 6), ], ), ItemDefId("common.items.armor.hide.primal.shoulder"): ( inputs: [ - (Item("common.items.crafting_ing.hide.plate"), 3), - (Item("common.items.mineral.ore.veloritefrag"), 10), + (Item("common.items.crafting_ing.hide.plate"), 2), + (Item("common.items.mineral.ore.veloritefrag"), 6), ], ), // ARMOR/HIDE/DRAGONSCALE @@ -365,32 +370,32 @@ recipes: { ), ItemDefId("common.items.armor.hide.dragonscale.chest"): ( inputs: [ - (Item("common.items.crafting_ing.hide.dragon_scale"), 4), + (Item("common.items.crafting_ing.hide.dragon_scale"), 3), (Item("common.items.mineral.ore.velorite"), 3), ], ), ItemDefId("common.items.armor.hide.dragonscale.foot"): ( inputs: [ (Item("common.items.crafting_ing.hide.dragon_scale"), 1), - (Item("common.items.mineral.ore.velorite"), 2), + (Item("common.items.mineral.ore.velorite"), 1), ], ), ItemDefId("common.items.armor.hide.dragonscale.hand"): ( inputs: [ (Item("common.items.crafting_ing.hide.dragon_scale"), 1), - (Item("common.items.mineral.ore.velorite"), 2), + (Item("common.items.mineral.ore.velorite"), 1), ], ), ItemDefId("common.items.armor.hide.dragonscale.pants"): ( inputs: [ - (Item("common.items.crafting_ing.hide.dragon_scale"), 3), - (Item("common.items.mineral.ore.velorite"), 3), + (Item("common.items.crafting_ing.hide.dragon_scale"), 2), + (Item("common.items.mineral.ore.velorite"), 2), ], ), ItemDefId("common.items.armor.hide.dragonscale.shoulder"): ( inputs: [ - (Item("common.items.crafting_ing.hide.dragon_scale"), 3), - (Item("common.items.mineral.ore.velorite"), 3), + (Item("common.items.crafting_ing.hide.dragon_scale"), 2), + (Item("common.items.mineral.ore.velorite"), 2), ], ), // ARMOR/CLOTH/LINEN @@ -406,7 +411,7 @@ recipes: { ), ItemDefId("common.items.armor.cloth.linen.chest"): ( inputs: [ - (Item("common.items.crafting_ing.cloth.linen"), 4), + (Item("common.items.crafting_ing.cloth.linen"), 3), ], ), ItemDefId("common.items.armor.cloth.linen.foot"): ( @@ -421,12 +426,12 @@ recipes: { ), ItemDefId("common.items.armor.cloth.linen.pants"): ( inputs: [ - (Item("common.items.crafting_ing.cloth.linen"), 3), + (Item("common.items.crafting_ing.cloth.linen"), 2), ], ), ItemDefId("common.items.armor.cloth.linen.shoulder"): ( inputs: [ - (Item("common.items.crafting_ing.cloth.linen"), 3), + (Item("common.items.crafting_ing.cloth.linen"), 2), ], ), // ARMOR/CLOTH/WOOL @@ -442,7 +447,7 @@ recipes: { ), ItemDefId("common.items.armor.cloth.woolen.chest"): ( inputs: [ - (Item("common.items.crafting_ing.cloth.wool"), 4), + (Item("common.items.crafting_ing.cloth.wool"), 3), ], ), ItemDefId("common.items.armor.cloth.woolen.foot"): ( @@ -457,12 +462,12 @@ recipes: { ), ItemDefId("common.items.armor.cloth.woolen.pants"): ( inputs: [ - (Item("common.items.crafting_ing.cloth.wool"), 3), + (Item("common.items.crafting_ing.cloth.wool"), 2), ], ), ItemDefId("common.items.armor.cloth.woolen.shoulder"): ( inputs: [ - (Item("common.items.crafting_ing.cloth.wool"), 3), + (Item("common.items.crafting_ing.cloth.wool"), 2), ], ), // ARMOR/CLOTH/SILK @@ -480,8 +485,8 @@ recipes: { ), ItemDefId("common.items.armor.cloth.silken.chest"): ( inputs: [ - (Item("common.items.crafting_ing.cloth.silk"), 4), - (Item("common.items.mineral.ore.veloritefrag"), 7), + (Item("common.items.crafting_ing.cloth.silk"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 3), ], ), ItemDefId("common.items.armor.cloth.silken.foot"): ( @@ -498,14 +503,14 @@ recipes: { ), ItemDefId("common.items.armor.cloth.silken.pants"): ( inputs: [ - (Item("common.items.crafting_ing.cloth.silk"), 3), - (Item("common.items.mineral.ore.veloritefrag"), 5), + (Item("common.items.crafting_ing.cloth.silk"), 2), + (Item("common.items.mineral.ore.veloritefrag"), 2), ], ), ItemDefId("common.items.armor.cloth.silken.shoulder"): ( inputs: [ - (Item("common.items.crafting_ing.cloth.silk"), 3), - (Item("common.items.mineral.ore.veloritefrag"), 5), + (Item("common.items.crafting_ing.cloth.silk"), 2), + (Item("common.items.mineral.ore.veloritefrag"), 2), ], ), // ARMOR/CLOTH/DRUID @@ -523,8 +528,8 @@ recipes: { ), ItemDefId("common.items.armor.cloth.druid.chest"): ( inputs: [ - (Item("common.items.crafting_ing.cloth.lifecloth"), 4), - (Item("common.items.mineral.ore.veloritefrag"), 10), + (Item("common.items.crafting_ing.cloth.lifecloth"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 6), ], ), ItemDefId("common.items.armor.cloth.druid.foot"): ( @@ -541,14 +546,14 @@ recipes: { ), ItemDefId("common.items.armor.cloth.druid.pants"): ( inputs: [ - (Item("common.items.crafting_ing.cloth.lifecloth"), 3), - (Item("common.items.mineral.ore.veloritefrag"), 7), + (Item("common.items.crafting_ing.cloth.lifecloth"), 2), + (Item("common.items.mineral.ore.veloritefrag"), 4), ], ), ItemDefId("common.items.armor.cloth.druid.shoulder"): ( inputs: [ - (Item("common.items.crafting_ing.cloth.lifecloth"), 3), - (Item("common.items.mineral.ore.veloritefrag"), 7), + (Item("common.items.crafting_ing.cloth.lifecloth"), 2), + (Item("common.items.mineral.ore.veloritefrag"), 4), ], ), // ARMOR/CLOTH/MOONWEAVE @@ -566,8 +571,8 @@ recipes: { ), ItemDefId("common.items.armor.cloth.moonweave.chest"): ( inputs: [ - (Item("common.items.crafting_ing.cloth.moonweave"), 4), - (Item("common.items.mineral.ore.veloritefrag"), 14), + (Item("common.items.crafting_ing.cloth.moonweave"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 9), ], ), ItemDefId("common.items.armor.cloth.moonweave.foot"): ( @@ -584,14 +589,14 @@ recipes: { ), ItemDefId("common.items.armor.cloth.moonweave.pants"): ( inputs: [ - (Item("common.items.crafting_ing.cloth.moonweave"), 3), - (Item("common.items.mineral.ore.veloritefrag"), 10), + (Item("common.items.crafting_ing.cloth.moonweave"), 2), + (Item("common.items.mineral.ore.veloritefrag"), 6), ], ), ItemDefId("common.items.armor.cloth.moonweave.shoulder"): ( inputs: [ - (Item("common.items.crafting_ing.cloth.moonweave"), 3), - (Item("common.items.mineral.ore.veloritefrag"), 10), + (Item("common.items.crafting_ing.cloth.moonweave"), 2), + (Item("common.items.mineral.ore.veloritefrag"), 6), ], ), // ARMOR/CLOTH/SUNSILK @@ -609,32 +614,32 @@ recipes: { ), ItemDefId("common.items.armor.cloth.sunsilk.chest"): ( inputs: [ - (Item("common.items.crafting_ing.cloth.sunsilk"), 4), + (Item("common.items.crafting_ing.cloth.sunsilk"), 3), (Item("common.items.mineral.ore.velorite"), 3), ], ), ItemDefId("common.items.armor.cloth.sunsilk.foot"): ( inputs: [ (Item("common.items.crafting_ing.cloth.sunsilk"), 1), - (Item("common.items.mineral.ore.velorite"), 2), + (Item("common.items.mineral.ore.velorite"), 1), ], ), ItemDefId("common.items.armor.cloth.sunsilk.hand"): ( inputs: [ (Item("common.items.crafting_ing.cloth.sunsilk"), 1), - (Item("common.items.mineral.ore.velorite"), 2), + (Item("common.items.mineral.ore.velorite"), 1), ], ), ItemDefId("common.items.armor.cloth.sunsilk.pants"): ( inputs: [ - (Item("common.items.crafting_ing.cloth.sunsilk"), 3), - (Item("common.items.mineral.ore.velorite"), 3), + (Item("common.items.crafting_ing.cloth.sunsilk"), 2), + (Item("common.items.mineral.ore.velorite"), 2), ], ), ItemDefId("common.items.armor.cloth.sunsilk.shoulder"): ( inputs: [ - (Item("common.items.crafting_ing.cloth.sunsilk"), 3), - (Item("common.items.mineral.ore.velorite"), 3), + (Item("common.items.crafting_ing.cloth.sunsilk"), 2), + (Item("common.items.mineral.ore.velorite"), 2), ], ), // ARMOR/MAIL/BRONZE @@ -650,7 +655,7 @@ recipes: { ), ItemDefId("common.items.armor.mail.bronze.chest"): ( inputs: [ - (Item("common.items.mineral.ingot.bronze"), 4), + (Item("common.items.mineral.ingot.bronze"), 3), ], ), ItemDefId("common.items.armor.mail.bronze.foot"): ( @@ -665,12 +670,12 @@ recipes: { ), ItemDefId("common.items.armor.mail.bronze.pants"): ( inputs: [ - (Item("common.items.mineral.ingot.bronze"), 3), + (Item("common.items.mineral.ingot.bronze"), 2), ], ), ItemDefId("common.items.armor.mail.bronze.shoulder"): ( inputs: [ - (Item("common.items.mineral.ingot.bronze"), 3), + (Item("common.items.mineral.ingot.bronze"), 2), ], ), // ARMOR/MAIL/IRON @@ -686,7 +691,7 @@ recipes: { ), ItemDefId("common.items.armor.mail.iron.chest"): ( inputs: [ - (Item("common.items.mineral.ingot.iron"), 4), + (Item("common.items.mineral.ingot.iron"), 3), ], ), ItemDefId("common.items.armor.mail.iron.foot"): ( @@ -701,12 +706,12 @@ recipes: { ), ItemDefId("common.items.armor.mail.iron.pants"): ( inputs: [ - (Item("common.items.mineral.ingot.iron"), 3), + (Item("common.items.mineral.ingot.iron"), 2), ], ), ItemDefId("common.items.armor.mail.iron.shoulder"): ( inputs: [ - (Item("common.items.mineral.ingot.iron"), 3), + (Item("common.items.mineral.ingot.iron"), 2), ], ), // ARMOR/MAIL/STEEL @@ -724,8 +729,8 @@ recipes: { ), ItemDefId("common.items.armor.mail.steel.chest"): ( inputs: [ - (Item("common.items.mineral.ingot.steel"), 4), - (Item("common.items.mineral.ore.veloritefrag"), 7), + (Item("common.items.mineral.ingot.steel"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 3), ], ), ItemDefId("common.items.armor.mail.steel.foot"): ( @@ -742,14 +747,14 @@ recipes: { ), ItemDefId("common.items.armor.mail.steel.pants"): ( inputs: [ - (Item("common.items.mineral.ingot.steel"), 3), - (Item("common.items.mineral.ore.veloritefrag"), 5), + (Item("common.items.mineral.ingot.steel"), 2), + (Item("common.items.mineral.ore.veloritefrag"), 2), ], ), ItemDefId("common.items.armor.mail.steel.shoulder"): ( inputs: [ - (Item("common.items.mineral.ingot.steel"), 3), - (Item("common.items.mineral.ore.veloritefrag"), 5), + (Item("common.items.mineral.ingot.steel"), 2), + (Item("common.items.mineral.ore.veloritefrag"), 2), ], ), // ARMOR/MAIL/COBALT @@ -767,8 +772,8 @@ recipes: { ), ItemDefId("common.items.armor.mail.cobalt.chest"): ( inputs: [ - (Item("common.items.mineral.ingot.cobalt"), 4), - (Item("common.items.mineral.ore.veloritefrag"), 10), + (Item("common.items.mineral.ingot.cobalt"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 6), ], ), ItemDefId("common.items.armor.mail.cobalt.foot"): ( @@ -785,14 +790,14 @@ recipes: { ), ItemDefId("common.items.armor.mail.cobalt.pants"): ( inputs: [ - (Item("common.items.mineral.ingot.cobalt"), 3), - (Item("common.items.mineral.ore.veloritefrag"), 7), + (Item("common.items.mineral.ingot.cobalt"), 2), + (Item("common.items.mineral.ore.veloritefrag"), 4), ], ), ItemDefId("common.items.armor.mail.cobalt.shoulder"): ( inputs: [ - (Item("common.items.mineral.ingot.cobalt"), 3), - (Item("common.items.mineral.ore.veloritefrag"), 7), + (Item("common.items.mineral.ingot.cobalt"), 2), + (Item("common.items.mineral.ore.veloritefrag"), 4), ], ), // ARMOR/MAIL/BLOODSTEEL @@ -810,8 +815,8 @@ recipes: { ), ItemDefId("common.items.armor.mail.bloodsteel.chest"): ( inputs: [ - (Item("common.items.mineral.ingot.bloodsteel"), 4), - (Item("common.items.mineral.ore.veloritefrag"), 14), + (Item("common.items.mineral.ingot.bloodsteel"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 9), ], ), ItemDefId("common.items.armor.mail.bloodsteel.foot"): ( @@ -828,14 +833,14 @@ recipes: { ), ItemDefId("common.items.armor.mail.bloodsteel.pants"): ( inputs: [ - (Item("common.items.mineral.ingot.bloodsteel"), 3), - (Item("common.items.mineral.ore.veloritefrag"), 10), + (Item("common.items.mineral.ingot.bloodsteel"), 2), + (Item("common.items.mineral.ore.veloritefrag"), 6), ], ), ItemDefId("common.items.armor.mail.bloodsteel.shoulder"): ( inputs: [ - (Item("common.items.mineral.ingot.bloodsteel"), 3), - (Item("common.items.mineral.ore.veloritefrag"), 10), + (Item("common.items.mineral.ingot.bloodsteel"), 2), + (Item("common.items.mineral.ore.veloritefrag"), 6), ], ), // ARMOR/MAIL/ORICHALCUM @@ -853,78 +858,75 @@ recipes: { ), ItemDefId("common.items.armor.mail.orichalcum.chest"): ( inputs: [ - (Item("common.items.mineral.ingot.orichalcum"), 4), + (Item("common.items.mineral.ingot.orichalcum"), 3), (Item("common.items.mineral.ore.velorite"), 3), ], ), ItemDefId("common.items.armor.mail.orichalcum.foot"): ( inputs: [ (Item("common.items.mineral.ingot.orichalcum"), 1), - (Item("common.items.mineral.ore.velorite"), 2), + (Item("common.items.mineral.ore.velorite"), 1), ], ), ItemDefId("common.items.armor.mail.orichalcum.hand"): ( inputs: [ (Item("common.items.mineral.ingot.orichalcum"), 1), - (Item("common.items.mineral.ore.velorite"), 2), + (Item("common.items.mineral.ore.velorite"), 1), ], ), ItemDefId("common.items.armor.mail.orichalcum.pants"): ( inputs: [ - (Item("common.items.mineral.ingot.orichalcum"), 3), - (Item("common.items.mineral.ore.velorite"), 3), + (Item("common.items.mineral.ingot.orichalcum"), 2), + (Item("common.items.mineral.ore.velorite"), 2), ], ), ItemDefId("common.items.armor.mail.orichalcum.shoulder"): ( inputs: [ - (Item("common.items.mineral.ingot.orichalcum"), 3), - (Item("common.items.mineral.ore.velorite"), 3), + (Item("common.items.mineral.ingot.orichalcum"), 2), + (Item("common.items.mineral.ore.velorite"), 2), ], ), // ARMOR/CULTIST ItemDefId("common.items.armor.cultist.back"): ( inputs: [ (Item("common.items.crafting_ing.leather.rigid_leather"), 1), - (Item("common.items.mineral.ore.veloritefrag"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 2), ], ), ItemDefId("common.items.armor.cultist.belt"): ( inputs: [ (Item("common.items.crafting_ing.leather.rigid_leather"), 1), - (Item("common.items.mineral.ore.veloritefrag"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 2), ], ), ItemDefId("common.items.armor.cultist.chest"): ( inputs: [ - (Item("common.items.food.spore_corruption"), 1), - (Item("common.items.crafting_ing.leather.rigid_leather"), 4), - (Item("common.items.mineral.ore.veloritefrag"), 7), + (Item("common.items.crafting_ing.leather.rigid_leather"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 6), ], ), ItemDefId("common.items.armor.cultist.foot"): ( inputs: [ (Item("common.items.crafting_ing.leather.rigid_leather"), 1), - (Item("common.items.mineral.ore.veloritefrag"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 2), ], ), ItemDefId("common.items.armor.cultist.hand"): ( inputs: [ (Item("common.items.crafting_ing.leather.rigid_leather"), 1), - (Item("common.items.mineral.ore.veloritefrag"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 2), ], ), ItemDefId("common.items.armor.cultist.pants"): ( inputs: [ - (Item("common.items.food.spore_corruption"), 1), - (Item("common.items.crafting_ing.leather.rigid_leather"), 3), - (Item("common.items.mineral.ore.veloritefrag"), 5), + (Item("common.items.crafting_ing.leather.rigid_leather"), 2), + (Item("common.items.mineral.ore.veloritefrag"), 4), ], ), ItemDefId("common.items.armor.cultist.shoulder"): ( inputs: [ - (Item("common.items.food.spore_corruption"), 1), - (Item("common.items.crafting_ing.leather.rigid_leather"), 3), - (Item("common.items.mineral.ore.veloritefrag"), 5), + (Item("common.items.crafting_ing.leather.rigid_leather"), 2), + (Item("common.items.mineral.ore.veloritefrag"), 4), ], ), // MISC/HEAD @@ -968,7 +970,7 @@ recipes: { ), ItemDefId("common.items.armor.misc.head.crown"): ( inputs: [ - (Item("common.items.crafting_ing.cloth.linen_red"), 3), + (Item("common.items.crafting_ing.cloth.linen_red"), 1), (Item("common.items.mineral.ingot.gold"), 1), (Item("common.items.mineral.ore.veloritefrag"), 3), ], @@ -988,7 +990,7 @@ recipes: { ), ItemDefId("common.items.armor.merchant.hat"): ( inputs: [ - (Item("common.items.crafting_ing.cloth.linen_red"), 8), + (Item("common.items.crafting_ing.cloth.linen_red"), 4), (Item("common.items.mineral.gem.diamond"), 1), (Item("common.items.mineral.ore.veloritefrag"), 3), ], @@ -996,7 +998,7 @@ recipes: { }, fallback: ( inputs: [ - (Item("common.items.mineral.ore.veloritefrag"), 25), + (Item("common.items.mineral.ore.veloritefrag"), 16), ], ), ) \ No newline at end of file From 92165a8fd8e21580efff176ff8892f16ed837d14 Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 18 Feb 2023 11:16:07 -0500 Subject: [PATCH 19/28] Repair recipes no longer show ingredients as required when they are not damaged enough to need any --- assets/common/repair_recipe_book.ron | 35 +++++++++++++++++++++++++--- common/src/recipe.rs | 14 +++++++---- 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/assets/common/repair_recipe_book.ron b/assets/common/repair_recipe_book.ron index 7aa2f28b81..8b0ac36a41 100644 --- a/assets/common/repair_recipe_book.ron +++ b/assets/common/repair_recipe_book.ron @@ -39,6 +39,7 @@ recipes: { ModularWeapon(material: "common.items.mineral.ingot.orichalcum"): ( inputs: [ (Item("common.items.mineral.ingot.orichalcum"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 4), (Item("common.items.mineral.ore.velorite"), 1), ], ), @@ -73,6 +74,7 @@ recipes: { ModularWeapon(material: "common.items.log.eldwood"): ( inputs: [ (Item("common.items.log.eldwood"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 4), (Item("common.items.mineral.ore.velorite"), 1), ], ), @@ -90,7 +92,7 @@ recipes: { ItemDefId("common.items.weapons.axe.malachite"): ( inputs: [ (Item("common.items.mineral.ore.velorite"), 1), - (Item("common.items.mineral.ore.veloritefrag"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 4), ], ), ItemDefId("common.items.weapons.hammer.cultist_purp_2h-0"): ( @@ -101,7 +103,7 @@ recipes: { ItemDefId("common.items.weapons.bow.velorite"): ( inputs: [ (Item("common.items.mineral.ore.velorite"), 1), - (Item("common.items.mineral.ore.veloritefrag"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 4), ], ), ItemDefId("common.items.weapons.staff.cultist_staff"): ( @@ -117,37 +119,43 @@ recipes: { ItemDefId("common.items.weapons.scepter.sceptre_velorite_0"): ( inputs: [ (Item("common.items.mineral.ore.velorite"), 1), - (Item("common.items.mineral.ore.veloritefrag"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 4), ], ), ItemDefId("common.items.weapons.sword.caladbolg"): ( inputs: [ (Item("common.items.mineral.ore.velorite"), 5), + (Item("common.items.mineral.ore.veloritefrag"), 8), ], ), ItemDefId("common.items.weapons.axe.parashu"): ( inputs: [ (Item("common.items.mineral.ore.velorite"), 5), + (Item("common.items.mineral.ore.veloritefrag"), 8), ], ), ItemDefId("common.items.weapons.hammer.mjolnir"): ( inputs: [ (Item("common.items.mineral.ore.velorite"), 5), + (Item("common.items.mineral.ore.veloritefrag"), 8), ], ), ItemDefId("common.items.weapons.bow.sagitta"): ( inputs: [ (Item("common.items.mineral.ore.velorite"), 5), + (Item("common.items.mineral.ore.veloritefrag"), 8), ], ), ItemDefId("common.items.weapons.staff.laevateinn"): ( inputs: [ (Item("common.items.mineral.ore.velorite"), 5), + (Item("common.items.mineral.ore.veloritefrag"), 8), ], ), ItemDefId("common.items.weapons.scepter.caduceus"): ( inputs: [ (Item("common.items.mineral.ore.velorite"), 5), + (Item("common.items.mineral.ore.veloritefrag"), 8), ], ), // ARMOR @@ -359,42 +367,49 @@ recipes: { ItemDefId("common.items.armor.hide.dragonscale.back"): ( inputs: [ (Item("common.items.crafting_ing.hide.dragon_scale"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 4), (Item("common.items.mineral.ore.velorite"), 1), ], ), ItemDefId("common.items.armor.hide.dragonscale.belt"): ( inputs: [ (Item("common.items.crafting_ing.hide.dragon_scale"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 4), (Item("common.items.mineral.ore.velorite"), 1), ], ), ItemDefId("common.items.armor.hide.dragonscale.chest"): ( inputs: [ (Item("common.items.crafting_ing.hide.dragon_scale"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 12), (Item("common.items.mineral.ore.velorite"), 3), ], ), ItemDefId("common.items.armor.hide.dragonscale.foot"): ( inputs: [ (Item("common.items.crafting_ing.hide.dragon_scale"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 4), (Item("common.items.mineral.ore.velorite"), 1), ], ), ItemDefId("common.items.armor.hide.dragonscale.hand"): ( inputs: [ (Item("common.items.crafting_ing.hide.dragon_scale"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 4), (Item("common.items.mineral.ore.velorite"), 1), ], ), ItemDefId("common.items.armor.hide.dragonscale.pants"): ( inputs: [ (Item("common.items.crafting_ing.hide.dragon_scale"), 2), + (Item("common.items.mineral.ore.veloritefrag"), 8), (Item("common.items.mineral.ore.velorite"), 2), ], ), ItemDefId("common.items.armor.hide.dragonscale.shoulder"): ( inputs: [ (Item("common.items.crafting_ing.hide.dragon_scale"), 2), + (Item("common.items.mineral.ore.veloritefrag"), 8), (Item("common.items.mineral.ore.velorite"), 2), ], ), @@ -603,42 +618,49 @@ recipes: { ItemDefId("common.items.armor.cloth.sunsilk.back"): ( inputs: [ (Item("common.items.crafting_ing.cloth.sunsilk"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 4), (Item("common.items.mineral.ore.velorite"), 1), ], ), ItemDefId("common.items.armor.cloth.sunsilk.belt"): ( inputs: [ (Item("common.items.crafting_ing.cloth.sunsilk"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 4), (Item("common.items.mineral.ore.velorite"), 1), ], ), ItemDefId("common.items.armor.cloth.sunsilk.chest"): ( inputs: [ (Item("common.items.crafting_ing.cloth.sunsilk"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 12), (Item("common.items.mineral.ore.velorite"), 3), ], ), ItemDefId("common.items.armor.cloth.sunsilk.foot"): ( inputs: [ (Item("common.items.crafting_ing.cloth.sunsilk"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 4), (Item("common.items.mineral.ore.velorite"), 1), ], ), ItemDefId("common.items.armor.cloth.sunsilk.hand"): ( inputs: [ (Item("common.items.crafting_ing.cloth.sunsilk"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 4), (Item("common.items.mineral.ore.velorite"), 1), ], ), ItemDefId("common.items.armor.cloth.sunsilk.pants"): ( inputs: [ (Item("common.items.crafting_ing.cloth.sunsilk"), 2), + (Item("common.items.mineral.ore.veloritefrag"), 8), (Item("common.items.mineral.ore.velorite"), 2), ], ), ItemDefId("common.items.armor.cloth.sunsilk.shoulder"): ( inputs: [ (Item("common.items.crafting_ing.cloth.sunsilk"), 2), + (Item("common.items.mineral.ore.veloritefrag"), 8), (Item("common.items.mineral.ore.velorite"), 2), ], ), @@ -847,42 +869,49 @@ recipes: { ItemDefId("common.items.armor.mail.orichalcum.back"): ( inputs: [ (Item("common.items.mineral.ingot.orichalcum"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 4), (Item("common.items.mineral.ore.velorite"), 1), ], ), ItemDefId("common.items.armor.mail.orichalcum.belt"): ( inputs: [ (Item("common.items.mineral.ingot.orichalcum"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 4), (Item("common.items.mineral.ore.velorite"), 1), ], ), ItemDefId("common.items.armor.mail.orichalcum.chest"): ( inputs: [ (Item("common.items.mineral.ingot.orichalcum"), 3), + (Item("common.items.mineral.ore.veloritefrag"), 12), (Item("common.items.mineral.ore.velorite"), 3), ], ), ItemDefId("common.items.armor.mail.orichalcum.foot"): ( inputs: [ (Item("common.items.mineral.ingot.orichalcum"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 4), (Item("common.items.mineral.ore.velorite"), 1), ], ), ItemDefId("common.items.armor.mail.orichalcum.hand"): ( inputs: [ (Item("common.items.mineral.ingot.orichalcum"), 1), + (Item("common.items.mineral.ore.veloritefrag"), 4), (Item("common.items.mineral.ore.velorite"), 1), ], ), ItemDefId("common.items.armor.mail.orichalcum.pants"): ( inputs: [ (Item("common.items.mineral.ingot.orichalcum"), 2), + (Item("common.items.mineral.ore.veloritefrag"), 8), (Item("common.items.mineral.ore.velorite"), 2), ], ), ItemDefId("common.items.armor.mail.orichalcum.shoulder"): ( inputs: [ (Item("common.items.mineral.ingot.orichalcum"), 2), + (Item("common.items.mineral.ore.veloritefrag"), 8), (Item("common.items.mineral.ore.velorite"), 2), ], ), diff --git a/common/src/recipe.rs b/common/src/recipe.rs index dc9a2f7df6..4c1ecf78b3 100644 --- a/common/src/recipe.rs +++ b/common/src/recipe.rs @@ -981,10 +981,16 @@ impl RepairRecipe { pub fn inputs(&self, item: &Item) -> impl ExactSizeIterator { let item_durability = item.durability().unwrap_or(0); - self.inputs.iter().map(move |(input, amount)| { - let amount = amount.mul(item_durability).div_floor(Item::MAX_DURABILITY); - (input, amount) - }) + // TODO: Figure out how to avoid vec collection to maintain exact size iterator + self.inputs.iter().filter_map(move |(input, original_amount)| { + let amount = original_amount.mul(item_durability).div_floor(Item::MAX_DURABILITY); + // If original repair recipe consumed ingredients, but item not damaged enough to actually need to consume item, remove item as requirement. + if *original_amount > 0 && amount == 0 { + None + } else { + Some((input, amount)) + } + }).collect::>().into_iter() } } From f7765a84711fecfdd1b3ff4cd62e474e3d83c4bc Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 18 Feb 2023 15:58:04 -0500 Subject: [PATCH 20/28] Added some repair recipes for some missed items. --- assets/common/repair_recipe_book.ron | 36 ++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/assets/common/repair_recipe_book.ron b/assets/common/repair_recipe_book.ron index 8b0ac36a41..395c4d6e86 100644 --- a/assets/common/repair_recipe_book.ron +++ b/assets/common/repair_recipe_book.ron @@ -159,8 +159,44 @@ recipes: { ], ), // ARMOR + ItemDefId("common.items.armor.cloth_purple.chest"): ( inputs: [], ), + ItemDefId("common.items.armor.cloth_purple.belt"): ( inputs: [], ), + ItemDefId("common.items.armor.cloth_purple.foot"): ( inputs: [], ), + ItemDefId("common.items.armor.cloth_purple.hand"): ( inputs: [], ), + ItemDefId("common.items.armor.cloth_purple.pants"): ( inputs: [], ), + ItemDefId("common.items.armor.cloth_purple.shoulder"): ( inputs: [], ), + ItemDefId("common.items.armor.cloth_green.chest"): ( inputs: [], ), + ItemDefId("common.items.armor.cloth_green.belt"): ( inputs: [], ), + ItemDefId("common.items.armor.cloth_green.foot"): ( inputs: [], ), + ItemDefId("common.items.armor.cloth_green.hand"): ( inputs: [], ), + ItemDefId("common.items.armor.cloth_green.pants"): ( inputs: [], ), + ItemDefId("common.items.armor.cloth_green.shoulder"): ( inputs: [], ), + ItemDefId("common.items.armor.cloth_green.chest"): ( inputs: [], ), + ItemDefId("common.items.armor.cloth_green.belt"): ( inputs: [], ), + ItemDefId("common.items.armor.cloth_green.foot"): ( inputs: [], ), + ItemDefId("common.items.armor.cloth_green.hand"): ( inputs: [], ), + ItemDefId("common.items.armor.cloth_green.pants"): ( inputs: [], ), + ItemDefId("common.items.armor.cloth_green.shoulder"): ( inputs: [], ), + ItemDefId("common.items.armor.cloth_blue.chest"): ( inputs: [], ), + ItemDefId("common.items.armor.cloth_blue.belt"): ( inputs: [], ), + ItemDefId("common.items.armor.cloth_blue.foot"): ( inputs: [], ), + ItemDefId("common.items.armor.cloth_blue.hand"): ( inputs: [], ), + ItemDefId("common.items.armor.cloth_blue.pants"): ( inputs: [], ), + ItemDefId("common.items.armor.cloth_blue.shoulder_0"): ( inputs: [], ), + ItemDefId("common.items.armor.cloth_blue.shoulder_1"): ( inputs: [], ), ItemDefId("common.items.armor.misc.chest.worker_purple_0"): ( inputs: [], ), + ItemDefId("common.items.armor.misc.chest.worker_purple_1"): ( inputs: [], ), + ItemDefId("common.items.armor.misc.chest.worker_purple_brown"): ( inputs: [], ), + ItemDefId("common.items.armor.misc.chest.worker_green_0"): ( inputs: [], ), + ItemDefId("common.items.armor.misc.chest.worker_green_1"): ( inputs: [], ), + ItemDefId("common.items.armor.misc.chest.worker_orange_0"): ( inputs: [], ), + ItemDefId("common.items.armor.misc.chest.worker_orange_1"): ( inputs: [], ), + ItemDefId("common.items.armor.misc.chest.worker_red_0"): ( inputs: [], ), + ItemDefId("common.items.armor.misc.chest.worker_red_1"): ( inputs: [], ), + ItemDefId("common.items.armor.misc.chest.worker_yellow_0"): ( inputs: [], ), + ItemDefId("common.items.armor.misc.chest.worker_yellow_1"): ( inputs: [], ), ItemDefId("common.items.armor.misc.pants.worker_brown"): ( inputs: [], ), + ItemDefId("common.items.armor.misc.pants.worker_blue"): ( inputs: [], ), ItemDefId("common.items.armor.misc.foot.sandals"): ( inputs: [], ), // ARMOR/HIDE/RAWHIDE ItemDefId("common.items.armor.hide.rawhide.back"): ( From 79812a7326ba851d8558b50c69e6e22297cf5f07 Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 11 Mar 2023 11:14:08 -0500 Subject: [PATCH 21/28] Addressed persistence review comments. --- server/src/persistence/character/conversions.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/server/src/persistence/character/conversions.rs b/server/src/persistence/character/conversions.rs index dbeb0eab61..61a7549cb9 100644 --- a/server/src/persistence/character/conversions.rs +++ b/server/src/persistence/character/conversions.rs @@ -182,7 +182,7 @@ pub fn convert_items_to_database_items( 1 }, properties: serde_json::to_string(&item_properties) - .expect("We probably want to crash if this gets triggered?"), + .expect("Failed to convert item properties to a json string."), }, // Continue to remember the atomic, in case we detect an error later and want // to roll back to preserve liveness. @@ -367,8 +367,7 @@ pub fn convert_inventory_from_database_items( let mut item = get_item_from_asset(db_item.item_definition_id.as_str())?; let item_properties = - serde_json::de::from_str::(&db_item.properties) - .expect("We probably want to crash if this gets triggered?"); + serde_json::de::from_str::(&db_item.properties)?; json_models::apply_db_item_properties(&mut item, &item_properties); // NOTE: Since this is freshly loaded, the atomic is *unique.* @@ -472,8 +471,7 @@ pub fn convert_loadout_from_database_items( let mut item = get_item_from_asset(db_item.item_definition_id.as_str())?; let item_properties = - serde_json::de::from_str::(&db_item.properties) - .expect("We probably want to crash if this gets triggered?"); + serde_json::de::from_str::(&db_item.properties)?; json_models::apply_db_item_properties(&mut item, &item_properties); // NOTE: item id is currently *unique*, so we can store the ID safely. From 132f26dbc056a1dc9e6d61b3eb32de40c2bc0fae Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 19 Mar 2023 21:28:09 -0400 Subject: [PATCH 22/28] Addressed review --- common/src/comp/inventory/item/armor.rs | 28 ++++++++-------- common/src/comp/inventory/item/mod.rs | 42 +++++++++++++++--------- common/src/comp/inventory/item/tool.rs | 39 +++++++++++----------- common/src/comp/inventory/loadout.rs | 11 +++---- common/src/comp/inventory/mod.rs | 6 ++-- common/src/recipe.rs | 31 ++++++++--------- server/src/events/entity_manipulation.rs | 2 +- voxygen/src/hud/crafting.rs | 2 +- 8 files changed, 82 insertions(+), 79 deletions(-) diff --git a/common/src/comp/inventory/item/armor.rs b/common/src/comp/inventory/item/armor.rs index 4ef7b7c595..a26ef044bb 100644 --- a/common/src/comp/inventory/item/armor.rs +++ b/common/src/comp/inventory/item/armor.rs @@ -27,20 +27,19 @@ pub enum ArmorKind { impl ArmorKind { pub fn has_durability(self) -> bool { - use ArmorKind::*; match self { - Shoulder => true, - Chest => true, - Belt => true, - Hand => true, - Pants => true, - Foot => true, - Back => true, - Ring => false, - Neck => false, - Head => true, - Tabard => false, - Bag => false, + ArmorKind::Shoulder => true, + ArmorKind::Chest => true, + ArmorKind::Belt => true, + ArmorKind::Hand => true, + ArmorKind::Pants => true, + ArmorKind::Foot => true, + ArmorKind::Back => true, + ArmorKind::Ring => false, + ArmorKind::Neck => false, + ArmorKind::Head => true, + ArmorKind::Tabard => false, + ArmorKind::Bag => false, } } } @@ -262,8 +261,7 @@ impl Armor { set_stats * multiplier }, }; - let DurabilityMultiplier(mult) = durability_multiplier; - base_stats * mult + base_stats * durability_multiplier.0 } #[cfg(test)] diff --git a/common/src/comp/inventory/item/mod.rs b/common/src/comp/inventory/item/mod.rs index b00bf7a19c..4eb7c2ddea 100644 --- a/common/src/comp/inventory/item/mod.rs +++ b/common/src/comp/inventory/item/mod.rs @@ -399,7 +399,7 @@ pub struct Item { /// Tracks how many deaths occurred while item was equipped, which is /// converted into the items durability. Only tracked for tools and armor /// currently. - durability: Option, + durability_lost: Option, } use std::hash::{Hash, Hasher}; @@ -793,9 +793,9 @@ impl Item { // These fields are updated immediately below item_config: None, hash: 0, - durability: None, + durability_lost: None, }; - item.durability = item.has_durability().then_some(0); + item.durability_lost = item.has_durability().then_some(0); item.update_item_state(ability_map, msm); item } @@ -1198,16 +1198,17 @@ impl Item { } } - pub fn durability(&self) -> Option { self.durability.map(|x| x.min(Self::MAX_DURABILITY)) } + pub fn durability(&self) -> Option { + self.durability_lost.map(|x| x.min(Self::MAX_DURABILITY)) + } pub fn stats_durability_multiplier(&self) -> DurabilityMultiplier { - let durability = self - .durability - .map_or(0, |x| x.clamp(0, Self::MAX_DURABILITY)); + let durability_lost = self.durability_lost.unwrap_or(0); + debug_assert!(durability_lost <= Self::MAX_DURABILITY); const DURABILITY_THRESHOLD: u32 = 4; const MIN_FRAC: f32 = 0.2; let mult = (1.0 - - durability.saturating_sub(DURABILITY_THRESHOLD) as f32 + - durability_lost.saturating_sub(DURABILITY_THRESHOLD) as f32 / (Self::MAX_DURABILITY - DURABILITY_THRESHOLD) as f32) * (1.0 - MIN_FRAC) + MIN_FRAC; @@ -1218,13 +1219,22 @@ impl Item { match &*self.kind() { ItemKind::Tool(_) => true, ItemKind::Armor(armor) => armor.kind.has_durability(), - _ => false, + ItemKind::ModularComponent(_) + | ItemKind::Lantern(_) + | ItemKind::Glider + | ItemKind::Consumable { .. } + | ItemKind::Throwable { .. } + | ItemKind::Utility { .. } + | ItemKind::Ingredient { .. } + | ItemKind::TagExamples { .. } => false, } } - pub fn apply_durability(&mut self, ability_map: &AbilityMap, msm: &MaterialStatManifest) { - if let Some(durability) = &mut self.durability { - *durability += 1; + pub fn increment_damage(&mut self, ability_map: &AbilityMap, msm: &MaterialStatManifest) { + if let Some(durability_lost) = &mut self.durability_lost { + if *durability_lost < Self::MAX_DURABILITY { + *durability_lost += 1; + } } // Update item state after applying durability because stats have potential to // change from different durability @@ -1232,23 +1242,23 @@ impl Item { } pub fn persistence_durability(&self) -> Option { - self.durability.and_then(NonZeroU32::new) + self.durability_lost.and_then(NonZeroU32::new) } pub fn persistence_set_durability(&mut self, value: Option) { // If changes have been made so that item no longer needs to track durability, // set to None if !self.has_durability() { - self.durability = None; + self.durability_lost = None; } else { // Set durability to persisted value, and if item previously had no durability, // set to Some(0) so that durability will be tracked - self.durability = Some(value.map_or(0, NonZeroU32::get)); + self.durability_lost = Some(value.map_or(0, NonZeroU32::get)); } } pub fn reset_durability(&mut self, ability_map: &AbilityMap, msm: &MaterialStatManifest) { - self.durability = self.durability.map(|_| 0); + self.durability_lost = self.has_durability().then_some(0); // Update item state after applying durability because stats have potential to // change from different durability self.update_item_state(ability_map, msm); diff --git a/common/src/comp/inventory/item/tool.rs b/common/src/comp/inventory/item/tool.rs index 71df384890..ee0d36ca3c 100644 --- a/common/src/comp/inventory/item/tool.rs +++ b/common/src/comp/inventory/item/tool.rs @@ -146,6 +146,20 @@ impl Stats { let diminished = (self.buff_strength - base + 1.0).log(5.0); base + diminished } + + pub fn with_durability_mult(&self, dur_mult: DurabilityMultiplier) -> Self { + let less_scaled = dur_mult.0 * 0.5 + 0.5; + Self { + equip_time_secs: self.equip_time_secs / less_scaled.max(0.01), + power: self.power * dur_mult.0, + effect_power: self.effect_power * dur_mult.0, + speed: self.speed * less_scaled, + crit_chance: self.crit_chance * dur_mult.0, + range: self.range * dur_mult.0, + energy_efficiency: self.energy_efficiency * dur_mult.0, + buff_strength: self.buff_strength * dur_mult.0, + } + } } impl Asset for Stats { @@ -213,25 +227,6 @@ impl MulAssign for Stats { fn mul_assign(&mut self, other: Stats) { *self = *self * other; } } -impl Mul for Stats { - type Output = Self; - - fn mul(self, value: DurabilityMultiplier) -> Self { - let DurabilityMultiplier(value) = value; - let less_scaled = value * 0.5 + 0.5; - Self { - equip_time_secs: self.equip_time_secs / less_scaled.max(0.01), - power: self.power * value, - effect_power: self.effect_power * value, - speed: self.speed * less_scaled, - crit_chance: self.crit_chance * value, - range: self.range * value, - energy_efficiency: self.energy_efficiency * value, - buff_strength: self.buff_strength * value, - } - } -} - impl Div for Stats { type Output = Self; @@ -249,6 +244,12 @@ impl Div for Stats { } } +impl Mul for Stats { + type Output = Self; + + fn mul(self, value: DurabilityMultiplier) -> Self { self.with_durability_mult(value) } +} + #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Tool { pub kind: ToolKind, diff --git a/common/src/comp/inventory/loadout.rs b/common/src/comp/inventory/loadout.rs index 08192c8c18..3c2f01576a 100644 --- a/common/src/comp/inventory/loadout.rs +++ b/common/src/comp/inventory/loadout.rs @@ -424,19 +424,16 @@ impl Loadout { } /// Increments durability by 1 of all valid items - pub(super) fn apply_durability( + pub(super) fn damage_items( &mut self, ability_map: &item::tool::AbilityMap, msm: &item::MaterialStatManifest, ) { self.slots .iter_mut() - .filter(|slot| slot.slot.as_ref().map_or(false, |i| i.has_durability())) - .for_each(|slot| { - if let Some(item) = &mut slot.slot { - item.apply_durability(ability_map, msm); - } - }) + .filter_map(|slot| slot.slot.as_mut()) + .filter(|item| item.has_durability()) + .for_each(|item| item.increment_damage(ability_map, msm)); } /// Resets durability of item in specified slot diff --git a/common/src/comp/inventory/mod.rs b/common/src/comp/inventory/mod.rs index 1965786433..51d1db3ead 100644 --- a/common/src/comp/inventory/mod.rs +++ b/common/src/comp/inventory/mod.rs @@ -884,12 +884,12 @@ impl Inventory { } /// Increments durability of all valid items equipped in loaodut by 1 - pub fn apply_durability( + pub fn damage_items( &mut self, ability_map: &item::tool::AbilityMap, msm: &item::MaterialStatManifest, ) { - self.loadout.apply_durability(ability_map, msm) + self.loadout.damage_items(ability_map, msm) } /// Resets durability of item in specified slot @@ -901,7 +901,7 @@ impl Inventory { ) { match slot { Slot::Inventory(invslot) => { - if let Some(Some(item)) = self.slot_mut(invslot).map(Option::as_mut) { + if let Some(Some(item)) = self.slot_mut(invslot) { item.reset_durability(ability_map, msm); } }, diff --git a/common/src/recipe.rs b/common/src/recipe.rs index 4c1ecf78b3..b14abb1a7c 100644 --- a/common/src/recipe.rs +++ b/common/src/recipe.rs @@ -14,7 +14,7 @@ use crate::{ }; use hashbrown::HashMap; use serde::{Deserialize, Serialize}; -use std::{borrow::Cow, ops::Mul, sync::Arc}; +use std::{borrow::Cow, sync::Arc}; #[derive(Clone, Debug, Serialize, Deserialize)] pub enum RecipeInput { @@ -517,13 +517,11 @@ impl assets::Compound for RecipeBook { cache: assets::AnyCache, specifier: &assets::SharedString, ) -> Result { - #[inline] fn load_item_def(spec: &(String, u32)) -> Result<(Arc, u32), assets::Error> { let def = Arc::::load_cloned(&spec.0)?; Ok((def, spec.1)) } - #[inline] fn load_recipe_input( (input, amount, is_mod_comp): &(RawRecipeInput, u32, bool), ) -> Result<(RecipeInput, u32, bool), assets::Error> { @@ -835,7 +833,6 @@ impl assets::Compound for ComponentRecipeBook { cache: assets::AnyCache, specifier: &assets::SharedString, ) -> Result { - #[inline] fn create_recipe_key(raw_recipe: &RawComponentRecipe) -> ComponentKey { match &raw_recipe.output { RawComponentOutput::ToolPrimaryComponent { toolkind, item: _ } => { @@ -853,7 +850,6 @@ impl assets::Compound for ComponentRecipeBook { } } - #[inline] fn load_recipe(raw_recipe: &RawComponentRecipe) -> Result { let output = match &raw_recipe.output { RawComponentOutput::ToolPrimaryComponent { toolkind: _, item } => { @@ -979,18 +975,20 @@ impl RepairRecipe { inventory_contains_ingredients(self.inputs(item), inv, 1) } - pub fn inputs(&self, item: &Item) -> impl ExactSizeIterator { + pub fn inputs(&self, item: &Item) -> impl Iterator { let item_durability = item.durability().unwrap_or(0); - // TODO: Figure out how to avoid vec collection to maintain exact size iterator - self.inputs.iter().filter_map(move |(input, original_amount)| { - let amount = original_amount.mul(item_durability).div_floor(Item::MAX_DURABILITY); - // If original repair recipe consumed ingredients, but item not damaged enough to actually need to consume item, remove item as requirement. - if *original_amount > 0 && amount == 0 { - None - } else { - Some((input, amount)) - } - }).collect::>().into_iter() + self.inputs + .iter() + .filter_map(move |(input, original_amount)| { + let amount = (original_amount * item_durability) / Item::MAX_DURABILITY; + // If original repair recipe consumed ingredients, but item not damaged enough + // to actually need to consume item, remove item as requirement. + if *original_amount > 0 && amount == 0 { + None + } else { + Some((input, amount)) + } + }) } } @@ -1068,7 +1066,6 @@ impl assets::Compound for RepairRecipeBook { cache: assets::AnyCache, specifier: &assets::SharedString, ) -> Result { - #[inline] fn load_recipe_input( (input, amount): &(RawRecipeInput, u32), ) -> Result<(RecipeInput, u32), assets::Error> { diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 55976ca2f5..bddc7423ae 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -515,7 +515,7 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, last_change: Healt if let Some(mut inventory) = state.ecs().write_storage::().get_mut(entity) { let ability_map = state.ecs().read_resource::(); let msm = state.ecs().read_resource::(); - inventory.apply_durability(&ability_map, &msm); + inventory.damage_items(&ability_map, &msm); } if should_delete { diff --git a/voxygen/src/hud/crafting.rs b/voxygen/src/hud/crafting.rs index 4207147773..227e62a868 100644 --- a/voxygen/src/hud/crafting.rs +++ b/voxygen/src/hud/crafting.rs @@ -1747,7 +1747,7 @@ impl<'a> Widget for Crafting<'a> { None => None, } { if let Some(recipe) = self.client.repair_recipe_book().repair_recipe(item) { - iter_d = recipe.inputs(item); + iter_d = recipe.inputs(item).collect::>().into_iter(); &mut iter_d as &mut dyn ExactSizeIterator } else { iter_b = core::iter::empty(); From a68ef7af309300d83da577d3e07377bd8bc02ad8 Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 31 Mar 2023 23:04:47 -0400 Subject: [PATCH 23/28] Tweaks --- assets/common/repair_recipe_book.ron | 11 ++++++++--- assets/common/trading/sellable_materials.ron | 4 ++-- assets/voxygen/i18n/en/hud/crafting.ftl | 4 +++- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/assets/common/repair_recipe_book.ron b/assets/common/repair_recipe_book.ron index 395c4d6e86..5103c0e864 100644 --- a/assets/common/repair_recipe_book.ron +++ b/assets/common/repair_recipe_book.ron @@ -78,10 +78,10 @@ recipes: { (Item("common.items.mineral.ore.velorite"), 1), ], ), - // UNCRAFTABLE WEAPONS + // NON-MODULAR WEAPONS ItemDefId("common.items.weapons.hammer.burnt_drumstick"): ( inputs: [ - (Item("common.items.food.meat.bird_large_cooked"), 1), + (Item("common.items.food.meat.bird_large_raw"), 1), ], ), ItemDefId("common.items.weapons.sword.cultist"): ( @@ -995,6 +995,11 @@ recipes: { ], ), // MISC/HEAD + ItemDefId("common.items.armor.misc.head.straw"): ( + inputs: [ + (Item("common.items.flowers.wild_flax"), 2), + ], + ), ItemDefId("common.items.armor.misc.head.bandana.red"): ( inputs: [ (Item("common.items.crafting_ing.cloth.linen_red"), 1), @@ -1053,7 +1058,7 @@ recipes: { (Item("common.items.mineral.ore.veloritefrag"), 3), ], ), - ItemDefId("common.items.armor.merchant.hat"): ( + ItemDefId("common.items.armor.merchant.turban"): ( inputs: [ (Item("common.items.crafting_ing.cloth.linen_red"), 4), (Item("common.items.mineral.gem.diamond"), 1), diff --git a/assets/common/trading/sellable_materials.ron b/assets/common/trading/sellable_materials.ron index 3007c4905a..61a39a6467 100644 --- a/assets/common/trading/sellable_materials.ron +++ b/assets/common/trading/sellable_materials.ron @@ -11,10 +11,10 @@ (0.08, Item("common.items.mineral.gem.ruby")), (0.1, Item("common.items.mineral.gem.emerald")), (0.2, Item("common.items.mineral.gem.sapphire")), - (0.25, Item("common.items.mineral.ore.velorite")), - (0.5, Item("common.items.mineral.ore.veloritefrag")), + (0.5, Item("common.items.mineral.ore.velorite")), (0.75, Item("common.items.mineral.gem.topaz")), (1.0, Item("common.items.mineral.gem.amethyst")), + (2.0, Item("common.items.mineral.ore.veloritefrag")), (4.0, Item("common.items.crafting_ing.stones")), // Ores diff --git a/assets/voxygen/i18n/en/hud/crafting.ftl b/assets/voxygen/i18n/en/hud/crafting.ftl index 2f580931c7..f633f721b2 100644 --- a/assets/voxygen/i18n/en/hud/crafting.ftl +++ b/assets/voxygen/i18n/en/hud/crafting.ftl @@ -46,4 +46,6 @@ hud-crafting-mod_comp_metal_prim_slot_desc = Place a metal ingot here, only cert hud-crafting-mod_comp_wood_prim_slot_title = Wood hud-crafting-mod_comp_wood_prim_slot_desc = Place a kind of wood here, only certain woods can be used to make weapons. hud-crafting-mod_comp_sec_slot_title = Animal Material -hud-crafting-mod_comp_sec_slot_desc = Optionally place an animal crafting ingredient, only certain ingredients can be used to augment weapons. \ No newline at end of file +hud-crafting-mod_comp_sec_slot_desc = Optionally place an animal crafting ingredient, only certain ingredients can be used to augment weapons. +hud-crafting-repair_slot_title = Damaged Item +hud-crafting-repair_slot_desc = Place an item here to see the cost of repairing it at its current durability level. \ No newline at end of file From 29a7520a48ee4754677b2af15d08ac5ad3ccea4c Mon Sep 17 00:00:00 2001 From: Monty Marz <44173739+Pfauenauge90@users.noreply.github.com> Date: Sat, 1 Apr 2023 13:14:48 +0200 Subject: [PATCH 24/28] adjusted death message, fixed repair bench text --- assets/voxygen/i18n/en/hud/misc.ftl | 1 + .../voxygen/voxel/humanoid_armor_head_manifest.ron | 4 ++-- voxygen/src/hud/mod.rs | 1 + voxygen/src/hud/skillbar.rs | 14 ++++++++++++++ 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/assets/voxygen/i18n/en/hud/misc.ftl b/assets/voxygen/i18n/en/hud/misc.ftl index f3ed55dfd4..cace9048a7 100644 --- a/assets/voxygen/i18n/en/hud/misc.ftl +++ b/assets/voxygen/i18n/en/hud/misc.ftl @@ -13,6 +13,7 @@ hud-press_key_to_toggle_lantern_fmt = [{ $key }] Lantern hud-press_key_to_show_debug_info_fmt = Press { $key } to show debug info hud-press_key_to_toggle_keybindings_fmt = Press { $key } to toggle keybindings hud-press_key_to_toggle_debug_info_fmt = Press { $key } to toggle debug info +hud_items_lost_dur = Your equipped items have lost Durability. hud-press_key_to_respawn = Press { $key } to respawn at the last campfire you visited. hud-tutorial_btn = Tutorial hud-tutorial_click_here = Press [ { $key } ] to free your cursor and click this button! diff --git a/assets/voxygen/voxel/humanoid_armor_head_manifest.ron b/assets/voxygen/voxel/humanoid_armor_head_manifest.ron index 789bf09542..59f4cb98ab 100644 --- a/assets/voxygen/voxel/humanoid_armor_head_manifest.ron +++ b/assets/voxygen/voxel/humanoid_armor_head_manifest.ron @@ -447,7 +447,7 @@ color: None ), (Danari, Male, "common.items.armor.misc.head.straw"): ( - vox_spec: ("armor.misc.head.straw", (-2.0, -5.0, 7.0)), + vox_spec: ("armor.misc.head.straw", (-2.0, -4.0, 7.0)), color: None ), (Danari, Female, "common.items.armor.misc.head.straw"): ( @@ -463,7 +463,7 @@ color: None ), (Orc, Male, "common.items.armor.misc.head.straw"): ( - vox_spec: ("armor.misc.head.straw", (-3.0, -3.0, 8.0)), + vox_spec: ("armor.misc.head.straw", (-3.0, -4.0, 7.0)), color: None ), (Orc, Female, "common.items.armor.misc.head.straw"): ( diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 21350db11e..3182b7e6ef 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -5082,6 +5082,7 @@ pub fn get_sprite_desc(sprite: SpriteKind, localized_strings: &Localization) -> SpriteKind::Anvil => "hud-crafting-anvil", SpriteKind::Cauldron => "hud-crafting-cauldron", SpriteKind::CookingPot => "hud-crafting-cooking_pot", + SpriteKind::RepairBench => "hud-crafting-repair_bench", SpriteKind::CraftingBench => "hud-crafting-crafting_bench", SpriteKind::Forge => "hud-crafting-forge", SpriteKind::Loom => "hud-crafting-loom", diff --git a/voxygen/src/hud/skillbar.rs b/voxygen/src/hud/skillbar.rs index 54e0bae0e1..638ec938ad 100644 --- a/voxygen/src/hud/skillbar.rs +++ b/voxygen/src/hud/skillbar.rs @@ -47,6 +47,8 @@ widget_ids! { death_message_2, death_message_1_bg, death_message_2_bg, + death_message_3, + death_message_3_bg, death_bg, // Level up message level_up, @@ -436,6 +438,12 @@ impl<'a> Skillbar<'a> { .font_id(self.fonts.cyri.conrod_id) .color(Color::Rgba(0.0, 0.0, 0.0, 1.0)) .set(state.ids.death_message_2_bg, ui); + Text::new(&self.localized_strings.get_msg("hud_items_lost_dur")) + .mid_bottom_with_margin_on(state.ids.death_message_2_bg, -50.0) + .font_size(self.fonts.cyri.scale(30)) + .font_id(self.fonts.cyri.conrod_id) + .color(Color::Rgba(0.0, 0.0, 0.0, 1.0)) + .set(state.ids.death_message_3_bg, ui); Text::new(&self.localized_strings.get_msg("hud-you_died")) .bottom_left_with_margins_on(state.ids.death_message_1_bg, 2.0, 2.0) .font_size(self.fonts.cyri.scale(50)) @@ -448,6 +456,12 @@ impl<'a> Skillbar<'a> { .font_id(self.fonts.cyri.conrod_id) .color(CRITICAL_HP_COLOR) .set(state.ids.death_message_2, ui); + Text::new(&self.localized_strings.get_msg("hud_items_lost_dur")) + .bottom_left_with_margins_on(state.ids.death_message_3_bg, 2.0, 2.0) + .font_size(self.fonts.cyri.scale(30)) + .font_id(self.fonts.cyri.conrod_id) + .color(CRITICAL_HP_COLOR) + .set(state.ids.death_message_3, ui); } } From 038929d66dd5d4d3f5a78fe1db48816ec453d163 Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 1 Apr 2023 10:59:18 -0400 Subject: [PATCH 25/28] New models for ores and repair bench --- assets/voxygen/item_image_manifest.ron | 2 +- assets/voxygen/voxel/item_drop_manifest.ron | 2 +- .../sprite/mineral/deposit/bloodstone.vox | 4 +- .../voxel/sprite/mineral/deposit/coal.vox | 4 +- .../voxel/sprite/mineral/deposit/cobalt.vox | 4 +- .../voxel/sprite/mineral/deposit/copper.vox | 4 +- .../voxel/sprite/mineral/deposit/gold.vox | 4 +- .../voxel/sprite/mineral/deposit/iron.vox | 4 +- .../voxel/sprite/mineral/deposit/silver.vox | 4 +- .../voxel/sprite/mineral/deposit/tin.vox | 4 +- .../voxel/sprite/mineral/gem/amethyst-0.vox | 4 +- .../voxel/sprite/mineral/gem/amethyst_S-0.vox | 4 +- .../voxel/sprite/mineral/gem/diamond-0.vox | 4 +- .../voxel/sprite/mineral/gem/diamond_S-0.vox | 4 +- .../voxel/sprite/mineral/gem/emerald-0.vox | 4 +- .../voxel/sprite/mineral/gem/emerald_S-0.vox | 4 +- .../voxel/sprite/mineral/gem/ruby-0.vox | 4 +- .../voxel/sprite/mineral/gem/ruby_S-0.vox | 4 +- .../voxel/sprite/mineral/gem/sapphire-0.vox | 4 +- .../voxel/sprite/mineral/gem/sapphire_S-0.vox | 4 +- .../voxel/sprite/mineral/gem/topaz-0.vox | 4 +- .../voxel/sprite/mineral/gem/topaz_S-0.vox | 4 +- .../voxel/sprite/mineral/ore/bloodstone.vox | 2 +- .../voxygen/voxel/sprite/mineral/ore/coal.vox | 2 +- .../voxel/sprite/mineral/ore/cobalt.vox | 4 +- .../voxel/sprite/mineral/ore/copper.vox | 4 +- .../voxygen/voxel/sprite/mineral/ore/gold.vox | 4 +- .../voxygen/voxel/sprite/mineral/ore/iron.vox | 2 +- .../voxel/sprite/mineral/ore/silver.vox | 2 +- .../voxygen/voxel/sprite/mineral/ore/tin.vox | 4 +- .../sprite/repair_bench/repair_bench-0.vox | 4 +- .../voxel/sprite/velorite/velorite.vox | 3 + .../voxel/sprite/velorite/velorite_1.vox | 3 - .../voxel/sprite/velorite/velorite_10.vox | 3 - .../voxel/sprite/velorite/velorite_2.vox | 3 - .../voxel/sprite/velorite/velorite_3.vox | 3 - .../voxel/sprite/velorite/velorite_4.vox | 3 - .../voxel/sprite/velorite/velorite_5.vox | 3 - .../voxel/sprite/velorite/velorite_6.vox | 3 - .../voxel/sprite/velorite/velorite_7.vox | 3 - .../voxel/sprite/velorite/velorite_8.vox | 3 - .../voxel/sprite/velorite/velorite_9.vox | 3 - .../voxel/sprite/velorite/velorite_ore.vox | 4 +- assets/voxygen/voxel/sprite_manifest.ron | 55 ++----------------- 44 files changed, 66 insertions(+), 138 deletions(-) create mode 100644 assets/voxygen/voxel/sprite/velorite/velorite.vox delete mode 100644 assets/voxygen/voxel/sprite/velorite/velorite_1.vox delete mode 100644 assets/voxygen/voxel/sprite/velorite/velorite_10.vox delete mode 100644 assets/voxygen/voxel/sprite/velorite/velorite_2.vox delete mode 100644 assets/voxygen/voxel/sprite/velorite/velorite_3.vox delete mode 100644 assets/voxygen/voxel/sprite/velorite/velorite_4.vox delete mode 100644 assets/voxygen/voxel/sprite/velorite/velorite_5.vox delete mode 100644 assets/voxygen/voxel/sprite/velorite/velorite_6.vox delete mode 100644 assets/voxygen/voxel/sprite/velorite/velorite_7.vox delete mode 100644 assets/voxygen/voxel/sprite/velorite/velorite_8.vox delete mode 100644 assets/voxygen/voxel/sprite/velorite/velorite_9.vox diff --git a/assets/voxygen/item_image_manifest.ron b/assets/voxygen/item_image_manifest.ron index df1d68f37c..4e97e7e35b 100644 --- a/assets/voxygen/item_image_manifest.ron +++ b/assets/voxygen/item_image_manifest.ron @@ -3159,7 +3159,7 @@ (0.0, -1.0, 0.0), (-50.0, 40.0, 20.0), 0.8, ), Simple("common.items.mineral.ore.veloritefrag"): VoxTrans( - "voxel.sprite.velorite.velorite_1", + "voxel.sprite.velorite.velorite", (0.0, 0.0, 0.0), (-50.0, 40.0, 20.0), 0.8, ), Simple("common.items.food.apple_mushroom_curry"): VoxTrans( diff --git a/assets/voxygen/voxel/item_drop_manifest.ron b/assets/voxygen/voxel/item_drop_manifest.ron index c545b9f6b6..3605f70cc0 100644 --- a/assets/voxygen/voxel/item_drop_manifest.ron +++ b/assets/voxygen/voxel/item_drop_manifest.ron @@ -800,7 +800,7 @@ Simple("common.items.food.blue_cheese"): "voxel.object.blue_cheese", Simple("common.items.food.mushroom"): "voxel.sprite.mushrooms.mushroom-10", Simple("common.items.mineral.ore.velorite"): "voxel.sprite.velorite.velorite_ore", - Simple("common.items.mineral.ore.veloritefrag"): "voxel.sprite.velorite.velorite_1", + Simple("common.items.mineral.ore.veloritefrag"): "voxel.sprite.velorite.velorite", Simple("common.items.food.apple_mushroom_curry"): "voxel.object.mushroom_curry", Simple("common.items.food.spore_corruption"): "voxel.sprite.spore.corruption_spore", Simple("common.items.food.apple_stick"): "voxel.object.apple_stick", diff --git a/assets/voxygen/voxel/sprite/mineral/deposit/bloodstone.vox b/assets/voxygen/voxel/sprite/mineral/deposit/bloodstone.vox index 7ec4dfe872..f60fb4f64c 100644 --- a/assets/voxygen/voxel/sprite/mineral/deposit/bloodstone.vox +++ b/assets/voxygen/voxel/sprite/mineral/deposit/bloodstone.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8272af67384202ecc9fb0ff0e7dfc7fa1f93105c7da2f1ecf2edb4f46044c096 -size 2228 +oid sha256:c5a0ccb582a37782b82b28d81eb0972f8e1ef0e0e0b041b0c71996e231064951 +size 2360 diff --git a/assets/voxygen/voxel/sprite/mineral/deposit/coal.vox b/assets/voxygen/voxel/sprite/mineral/deposit/coal.vox index 8036bd718c..d8dbaf14a6 100644 --- a/assets/voxygen/voxel/sprite/mineral/deposit/coal.vox +++ b/assets/voxygen/voxel/sprite/mineral/deposit/coal.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b0818816670a55f46edf0cfd730add93e157045102b8928874114657bb70e6f4 -size 2372 +oid sha256:721c5b1c7f504e92151cec49361881ce923e49f73c6481535e2d83f34c92c093 +size 2476 diff --git a/assets/voxygen/voxel/sprite/mineral/deposit/cobalt.vox b/assets/voxygen/voxel/sprite/mineral/deposit/cobalt.vox index 9a66eea5dd..d3436239f7 100644 --- a/assets/voxygen/voxel/sprite/mineral/deposit/cobalt.vox +++ b/assets/voxygen/voxel/sprite/mineral/deposit/cobalt.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7ed9188532a2c2dbcf46dc4dce8f977cff8635e504d94fda918c82cd99702364 -size 2372 +oid sha256:bda57536eea1c9a6948004c5c7ea914905e8f3471313ce5040fb6274192f1e3e +size 2436 diff --git a/assets/voxygen/voxel/sprite/mineral/deposit/copper.vox b/assets/voxygen/voxel/sprite/mineral/deposit/copper.vox index 61a8056232..1d39d7c1f0 100644 --- a/assets/voxygen/voxel/sprite/mineral/deposit/copper.vox +++ b/assets/voxygen/voxel/sprite/mineral/deposit/copper.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d737c3d77bd8a0b89243aeb9bf569496038e4d41a5a247b01499f89eda5fdfc4 -size 2372 +oid sha256:a34bf0acde151eb7932272b6d318ce2c751e47c9501b4d442c274a76d452e02c +size 2460 diff --git a/assets/voxygen/voxel/sprite/mineral/deposit/gold.vox b/assets/voxygen/voxel/sprite/mineral/deposit/gold.vox index 6506b300ab..2cafa5e8f5 100644 --- a/assets/voxygen/voxel/sprite/mineral/deposit/gold.vox +++ b/assets/voxygen/voxel/sprite/mineral/deposit/gold.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2a70efb5e148605b7c212acf35b2626a442f9b101d2cd3d1286426049b5dadaa -size 1948 +oid sha256:0cf41ac1cf59a1e0977e78202579d1c76e5c3494ac6f0f7d5e638b5cc1dbdcf0 +size 1952 diff --git a/assets/voxygen/voxel/sprite/mineral/deposit/iron.vox b/assets/voxygen/voxel/sprite/mineral/deposit/iron.vox index 36be977765..8adc895092 100644 --- a/assets/voxygen/voxel/sprite/mineral/deposit/iron.vox +++ b/assets/voxygen/voxel/sprite/mineral/deposit/iron.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:55d608a594955b6b2ddddea605d323450b8daa5eed264b8b4afe413d695ce2aa -size 2372 +oid sha256:b4108b62362e462b2a740555248e8291a34f39295924d22d4ce18b3890f9cb3b +size 2424 diff --git a/assets/voxygen/voxel/sprite/mineral/deposit/silver.vox b/assets/voxygen/voxel/sprite/mineral/deposit/silver.vox index caaf379f89..8a5a658601 100644 --- a/assets/voxygen/voxel/sprite/mineral/deposit/silver.vox +++ b/assets/voxygen/voxel/sprite/mineral/deposit/silver.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:88dee6d9cfd7dddecd8832773d2ae070385663ecffd022d732fe0e9e48c71122 -size 1964 +oid sha256:49d66bdf2c287f9c4faee97dd52b3a0bb93b994dd43ed1ee117e2df704fe68df +size 2040 diff --git a/assets/voxygen/voxel/sprite/mineral/deposit/tin.vox b/assets/voxygen/voxel/sprite/mineral/deposit/tin.vox index 8cfe3fda7a..f0230b75f2 100644 --- a/assets/voxygen/voxel/sprite/mineral/deposit/tin.vox +++ b/assets/voxygen/voxel/sprite/mineral/deposit/tin.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:313c16d8b76353ca559140668a209a0c58d47fc4806642979cf810cc508d72a6 -size 2372 +oid sha256:955522ad9136803389306dca46e8898fc31354ac34ed9fb2584222eb3c4c6c3c +size 2412 diff --git a/assets/voxygen/voxel/sprite/mineral/gem/amethyst-0.vox b/assets/voxygen/voxel/sprite/mineral/gem/amethyst-0.vox index 0f67625298..e1d87cd7dc 100644 --- a/assets/voxygen/voxel/sprite/mineral/gem/amethyst-0.vox +++ b/assets/voxygen/voxel/sprite/mineral/gem/amethyst-0.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a71e251d0a56d6bbb99100474c57688e53c689f94450c0952162c11a77419185 -size 29597 +oid sha256:12c464ca081e860896762b7def46658470fd1f014f2271205c24528f37ff1521 +size 2476 diff --git a/assets/voxygen/voxel/sprite/mineral/gem/amethyst_S-0.vox b/assets/voxygen/voxel/sprite/mineral/gem/amethyst_S-0.vox index cd9a33e252..dc3305c912 100644 --- a/assets/voxygen/voxel/sprite/mineral/gem/amethyst_S-0.vox +++ b/assets/voxygen/voxel/sprite/mineral/gem/amethyst_S-0.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:857d4afef91541ab2db61f5b6dee72367aaacf93c8e4b45af1ff4177e2cfa286 -size 27456 +oid sha256:95c61b997dc2600be0a5e78ab1f6afcd0d4e6828bc47912963b87d32e9166c91 +size 1412 diff --git a/assets/voxygen/voxel/sprite/mineral/gem/diamond-0.vox b/assets/voxygen/voxel/sprite/mineral/gem/diamond-0.vox index d98ebe8253..607c7c3f97 100644 --- a/assets/voxygen/voxel/sprite/mineral/gem/diamond-0.vox +++ b/assets/voxygen/voxel/sprite/mineral/gem/diamond-0.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4ed3aeb3318ba029cf461ca2638617b9378f9bfad4f720dda263861e1fa566e8 -size 28300 +oid sha256:dbf26b45ccc58b6627df99b58f892bfb44492a896d79bacf5ca9e296a9a88662 +size 2404 diff --git a/assets/voxygen/voxel/sprite/mineral/gem/diamond_S-0.vox b/assets/voxygen/voxel/sprite/mineral/gem/diamond_S-0.vox index b61eeb216d..5c33330627 100644 --- a/assets/voxygen/voxel/sprite/mineral/gem/diamond_S-0.vox +++ b/assets/voxygen/voxel/sprite/mineral/gem/diamond_S-0.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cd44a2050af332dbe5b0e27edee8c39b49639e716ddd2d47ca86e48e6f9af49f -size 27277 +oid sha256:40a765810fdf14f2fe5a47d2fbe1d85cc41c6658130f4f67f535b714cd318070 +size 1436 diff --git a/assets/voxygen/voxel/sprite/mineral/gem/emerald-0.vox b/assets/voxygen/voxel/sprite/mineral/gem/emerald-0.vox index cda25112b6..1dab50f854 100644 --- a/assets/voxygen/voxel/sprite/mineral/gem/emerald-0.vox +++ b/assets/voxygen/voxel/sprite/mineral/gem/emerald-0.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7949ddbef6bf8c5a3c33e17e811be07fdd137e356121ba976cf37abe623ff34e -size 28213 +oid sha256:0518f1a2131e1afca6dc92a4b724c7eae46a3879cba57a182ed8b6784067e2b1 +size 2196 diff --git a/assets/voxygen/voxel/sprite/mineral/gem/emerald_S-0.vox b/assets/voxygen/voxel/sprite/mineral/gem/emerald_S-0.vox index 003fba3b86..e91c37fb23 100644 --- a/assets/voxygen/voxel/sprite/mineral/gem/emerald_S-0.vox +++ b/assets/voxygen/voxel/sprite/mineral/gem/emerald_S-0.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4762ddae846b11c23d873aaf339407edfbe8daf7dca85537a4d07c4f397f0b53 -size 27257 +oid sha256:5f9bb1d513ea89bb03d848feeccbac9c7fc544eca5e73ec733c8eab5c0a49055 +size 1336 diff --git a/assets/voxygen/voxel/sprite/mineral/gem/ruby-0.vox b/assets/voxygen/voxel/sprite/mineral/gem/ruby-0.vox index 90c81a4804..395fdfeb50 100644 --- a/assets/voxygen/voxel/sprite/mineral/gem/ruby-0.vox +++ b/assets/voxygen/voxel/sprite/mineral/gem/ruby-0.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b60b78fa0bc3f58db4a4e72e57383dae8985946a3b6ceb8fd9e9dc9c8c78c18b -size 28065 +oid sha256:63da89b3dd7df59dc928c3e2462a6882afa754526e134edd79a397b544818835 +size 2032 diff --git a/assets/voxygen/voxel/sprite/mineral/gem/ruby_S-0.vox b/assets/voxygen/voxel/sprite/mineral/gem/ruby_S-0.vox index 50864f2517..6dcdcaca6c 100644 --- a/assets/voxygen/voxel/sprite/mineral/gem/ruby_S-0.vox +++ b/assets/voxygen/voxel/sprite/mineral/gem/ruby_S-0.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fa87a0a75f40094c2745292a8e112a4dc228dd3849dc82e0379732161b916d3f -size 27289 +oid sha256:f522666d899bb4a11979e5642315094e345edbf0c7329cb0f00c7e774049a470 +size 1356 diff --git a/assets/voxygen/voxel/sprite/mineral/gem/sapphire-0.vox b/assets/voxygen/voxel/sprite/mineral/gem/sapphire-0.vox index ac4fb43f67..9dc8d0417c 100644 --- a/assets/voxygen/voxel/sprite/mineral/gem/sapphire-0.vox +++ b/assets/voxygen/voxel/sprite/mineral/gem/sapphire-0.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:36f60807a7efb02bf01f9a2736759c6c9a97e6500c9e998cbfe0f2300620e4cd -size 28249 +oid sha256:ba1c56f4126c9379a6e353d582ec9ed5e0a5a0933735a48df5621814341fb397 +size 2120 diff --git a/assets/voxygen/voxel/sprite/mineral/gem/sapphire_S-0.vox b/assets/voxygen/voxel/sprite/mineral/gem/sapphire_S-0.vox index 79cd5cd0bf..ed1c1be9c7 100644 --- a/assets/voxygen/voxel/sprite/mineral/gem/sapphire_S-0.vox +++ b/assets/voxygen/voxel/sprite/mineral/gem/sapphire_S-0.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0075c7315a6ed4008f2ce3c629719183c95a34f48550f2746615d52328e07e66 -size 27293 +oid sha256:92c3c00f720045ddec6ec28b248e025b8e7168a62f0e548d34c724f56bf8c598 +size 1420 diff --git a/assets/voxygen/voxel/sprite/mineral/gem/topaz-0.vox b/assets/voxygen/voxel/sprite/mineral/gem/topaz-0.vox index 07fcd89129..d5033d612a 100644 --- a/assets/voxygen/voxel/sprite/mineral/gem/topaz-0.vox +++ b/assets/voxygen/voxel/sprite/mineral/gem/topaz-0.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bc49de3d05cb7a3f868c05c2a8bc49401f19cbbf805b515efaf37ca0f5e389d1 -size 29142 +oid sha256:3c781596d79a8ece6e6e7889b07ee809b2082b65269dde88104f9ecd295572cd +size 2724 diff --git a/assets/voxygen/voxel/sprite/mineral/gem/topaz_S-0.vox b/assets/voxygen/voxel/sprite/mineral/gem/topaz_S-0.vox index b57cdc3bc8..2e22717d47 100644 --- a/assets/voxygen/voxel/sprite/mineral/gem/topaz_S-0.vox +++ b/assets/voxygen/voxel/sprite/mineral/gem/topaz_S-0.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c2729c2360308a270f6cabd44146a445543998a60419c9221334642e74095bc8 -size 27352 +oid sha256:f7c68becf0568210e5de14bcb3c0c6af317e7ef918c5ca4f64f1ff2f3943a801 +size 55921 diff --git a/assets/voxygen/voxel/sprite/mineral/ore/bloodstone.vox b/assets/voxygen/voxel/sprite/mineral/ore/bloodstone.vox index 438ebf0444..78f48caecf 100644 --- a/assets/voxygen/voxel/sprite/mineral/ore/bloodstone.vox +++ b/assets/voxygen/voxel/sprite/mineral/ore/bloodstone.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:51e1a4f5513ef80b810ed76f7b01f8ff58bb5f19eb7a98fb6a7fde3436273440 +oid sha256:313d7d2bc03910894291c6e026df29eccd15365a45be7651d0c2639ab6409add size 1384 diff --git a/assets/voxygen/voxel/sprite/mineral/ore/coal.vox b/assets/voxygen/voxel/sprite/mineral/ore/coal.vox index 839abed9c7..aa03ffbbcd 100644 --- a/assets/voxygen/voxel/sprite/mineral/ore/coal.vox +++ b/assets/voxygen/voxel/sprite/mineral/ore/coal.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3bc17b941ab277aae3559764789546a15fa8d56e82a9b849f9310e70fd17f62e +oid sha256:c28e885ff5444370087b79e3dc86949e28a51c2ff5bd04c7818d4a3b33a53d11 size 1408 diff --git a/assets/voxygen/voxel/sprite/mineral/ore/cobalt.vox b/assets/voxygen/voxel/sprite/mineral/ore/cobalt.vox index b92c5aa0a8..cf836c33a8 100644 --- a/assets/voxygen/voxel/sprite/mineral/ore/cobalt.vox +++ b/assets/voxygen/voxel/sprite/mineral/ore/cobalt.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6a70804b95cb066dd3e46cfd47b27a02958b50e02a67f348d0da39abe5d31c5f -size 1288 +oid sha256:0f47b32ee091c4cae6734e062aa31eb4024b3187b45c798a7931335ee60be6eb +size 1292 diff --git a/assets/voxygen/voxel/sprite/mineral/ore/copper.vox b/assets/voxygen/voxel/sprite/mineral/ore/copper.vox index 1ae7493f23..8d40e79d3e 100644 --- a/assets/voxygen/voxel/sprite/mineral/ore/copper.vox +++ b/assets/voxygen/voxel/sprite/mineral/ore/copper.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4a02a7d874be3e5cf61a84024206650e39aa7f5a90cefb5c133cdd8caa151caf -size 1364 +oid sha256:9b1986b425bbe8e5ccbcf4fdaafacf46e567d30698d502aa6f1b455740048343 +size 1360 diff --git a/assets/voxygen/voxel/sprite/mineral/ore/gold.vox b/assets/voxygen/voxel/sprite/mineral/ore/gold.vox index fd5ba87961..9ee6d8b692 100644 --- a/assets/voxygen/voxel/sprite/mineral/ore/gold.vox +++ b/assets/voxygen/voxel/sprite/mineral/ore/gold.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7e66b84297b3a05e4e722b7c4949c10c147c7272fd9d1f973745b1c688dae30e -size 1228 +oid sha256:905af11f3a2d198430c906cc81a7563016c7aab241a7bab0961fcf224317823a +size 1236 diff --git a/assets/voxygen/voxel/sprite/mineral/ore/iron.vox b/assets/voxygen/voxel/sprite/mineral/ore/iron.vox index 5a465f9379..d768b0bae9 100644 --- a/assets/voxygen/voxel/sprite/mineral/ore/iron.vox +++ b/assets/voxygen/voxel/sprite/mineral/ore/iron.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a15e355bdaeba7d25a731a994588161d15ef2370cb4b084a89d54752bff3e704 +oid sha256:b008d57166316349f29ab721301043b6f0290a0d17985396a1d37d8f3ebf4385 size 1304 diff --git a/assets/voxygen/voxel/sprite/mineral/ore/silver.vox b/assets/voxygen/voxel/sprite/mineral/ore/silver.vox index 61fd4b9374..c04cc2baa9 100644 --- a/assets/voxygen/voxel/sprite/mineral/ore/silver.vox +++ b/assets/voxygen/voxel/sprite/mineral/ore/silver.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a89be3b774de6b88518ce78225b30fdfba4ab9d501b316579399ea1c36e43bdb +oid sha256:67389b8203368db3b1612827c45a94e3ae79543b601180a7d3b0a446021f50e2 size 1312 diff --git a/assets/voxygen/voxel/sprite/mineral/ore/tin.vox b/assets/voxygen/voxel/sprite/mineral/ore/tin.vox index c265bc6bcc..30aa0b81df 100644 --- a/assets/voxygen/voxel/sprite/mineral/ore/tin.vox +++ b/assets/voxygen/voxel/sprite/mineral/ore/tin.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2306340c1a7a0e2b7b9d9b57c4ef7c56f27e136d626e39cc43073a0f13d2af8e -size 1316 +oid sha256:e1dbb86181b8e4814e9ef1e3b24bc56ce78a7031ae89ea94173ba30780fce3f9 +size 1328 diff --git a/assets/voxygen/voxel/sprite/repair_bench/repair_bench-0.vox b/assets/voxygen/voxel/sprite/repair_bench/repair_bench-0.vox index 265b9eabb4..547086a3ba 100644 --- a/assets/voxygen/voxel/sprite/repair_bench/repair_bench-0.vox +++ b/assets/voxygen/voxel/sprite/repair_bench/repair_bench-0.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ae8a36234e9ed125f1369d0fa396e35885f57c256d14d94149c383090678d326 -size 4692 +oid sha256:f8ecd271f99c24fac4557fa114f5ce9cb7d6fbe6b5ffe074c0329eef22ce27c0 +size 26154 diff --git a/assets/voxygen/voxel/sprite/velorite/velorite.vox b/assets/voxygen/voxel/sprite/velorite/velorite.vox new file mode 100644 index 0000000000..9efea9f2df --- /dev/null +++ b/assets/voxygen/voxel/sprite/velorite/velorite.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2126ccfb3394a2304314a4686e4a8f7ec205f21dedc36a434d91e3236c9f84d7 +size 1556 diff --git a/assets/voxygen/voxel/sprite/velorite/velorite_1.vox b/assets/voxygen/voxel/sprite/velorite/velorite_1.vox deleted file mode 100644 index ceea2e97cd..0000000000 --- a/assets/voxygen/voxel/sprite/velorite/velorite_1.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8eececfffca6379c845a18ad21c226b6e76279574b09ab3db1303db65361f511 -size 1332 diff --git a/assets/voxygen/voxel/sprite/velorite/velorite_10.vox b/assets/voxygen/voxel/sprite/velorite/velorite_10.vox deleted file mode 100644 index 802b532def..0000000000 --- a/assets/voxygen/voxel/sprite/velorite/velorite_10.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:322d7832c4196899797335e7c4eb41887954a38b8b735ff0497b16d6043e3419 -size 1376 diff --git a/assets/voxygen/voxel/sprite/velorite/velorite_2.vox b/assets/voxygen/voxel/sprite/velorite/velorite_2.vox deleted file mode 100644 index c1d8e9ee63..0000000000 --- a/assets/voxygen/voxel/sprite/velorite/velorite_2.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3cdad254625676493f1e3b1546a6d8b37c781251dc0ae2ea25de559f542dedc6 -size 1408 diff --git a/assets/voxygen/voxel/sprite/velorite/velorite_3.vox b/assets/voxygen/voxel/sprite/velorite/velorite_3.vox deleted file mode 100644 index 531f1f6e81..0000000000 --- a/assets/voxygen/voxel/sprite/velorite/velorite_3.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6d69c57dcaedf01985aae2e232c3db9b9da244baea9dddfbdb4bb509511acc43 -size 1348 diff --git a/assets/voxygen/voxel/sprite/velorite/velorite_4.vox b/assets/voxygen/voxel/sprite/velorite/velorite_4.vox deleted file mode 100644 index 35e5cebf53..0000000000 --- a/assets/voxygen/voxel/sprite/velorite/velorite_4.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:cbdad9241fadbca80a21a0254b19d80c16d85ff32492482d899b28e9148aaf15 -size 1332 diff --git a/assets/voxygen/voxel/sprite/velorite/velorite_5.vox b/assets/voxygen/voxel/sprite/velorite/velorite_5.vox deleted file mode 100644 index 7fdd8afea9..0000000000 --- a/assets/voxygen/voxel/sprite/velorite/velorite_5.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:80887f9fb2ddbc44fa26b295bafd895263fe0191723bcbf14377c06627fe82fd -size 1364 diff --git a/assets/voxygen/voxel/sprite/velorite/velorite_6.vox b/assets/voxygen/voxel/sprite/velorite/velorite_6.vox deleted file mode 100644 index a737463af4..0000000000 --- a/assets/voxygen/voxel/sprite/velorite/velorite_6.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e2cd51a3ed11b54706e5e2e90d6e9bc1eea32f7db464b0e0d5a66414a251c734 -size 1304 diff --git a/assets/voxygen/voxel/sprite/velorite/velorite_7.vox b/assets/voxygen/voxel/sprite/velorite/velorite_7.vox deleted file mode 100644 index 2815dd9a59..0000000000 --- a/assets/voxygen/voxel/sprite/velorite/velorite_7.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:97e197a7e220f44a6c324f32512f744ec1206dab2e5aacf6d3a07a2881930a1a -size 1404 diff --git a/assets/voxygen/voxel/sprite/velorite/velorite_8.vox b/assets/voxygen/voxel/sprite/velorite/velorite_8.vox deleted file mode 100644 index 39f3f6af7b..0000000000 --- a/assets/voxygen/voxel/sprite/velorite/velorite_8.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8e1eac4471f5a4b15c3fb1bb59bbdabd29024531b9fb92394bcc1f95844700ea -size 1236 diff --git a/assets/voxygen/voxel/sprite/velorite/velorite_9.vox b/assets/voxygen/voxel/sprite/velorite/velorite_9.vox deleted file mode 100644 index a39e99564b..0000000000 --- a/assets/voxygen/voxel/sprite/velorite/velorite_9.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b31d490ccf8ba14e09b6e1605c292fc1cd37ad5831d8b59793575005ec3272be -size 1312 diff --git a/assets/voxygen/voxel/sprite/velorite/velorite_ore.vox b/assets/voxygen/voxel/sprite/velorite/velorite_ore.vox index 0e34869701..3146b490e6 100644 --- a/assets/voxygen/voxel/sprite/velorite/velorite_ore.vox +++ b/assets/voxygen/voxel/sprite/velorite/velorite_ore.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c2670533afcfb70f2ecac6586ce863e02d067f68c208f7237c707461e06b6e7a -size 3212 +oid sha256:cb4fb841bbc01ac094880e94f92a599a78eb2a86e3512acfda0f9784830f0e21 +size 3056 diff --git a/assets/voxygen/voxel/sprite_manifest.ron b/assets/voxygen/voxel/sprite_manifest.ron index e9f29976c3..e5c607e6d7 100644 --- a/assets/voxygen/voxel/sprite_manifest.ron +++ b/assets/voxygen/voxel/sprite_manifest.ron @@ -725,53 +725,8 @@ Velorite: Some(( VeloriteFrag: Some(( variations: [ ( - model: "voxygen.voxel.sprite.velorite.velorite_1", - offset: (-3.0, -5.0, 0.0), - lod_axes: (1.0, 1.0, 1.0), - ), - ( - model: "voxygen.voxel.sprite.velorite.velorite_2", - offset: (-3.0, -5.0, 0.0), - lod_axes: (1.0, 1.0, 1.0), - ), - ( - model: "voxygen.voxel.sprite.velorite.velorite_3", - offset: (-3.0, -5.0, 0.0), - lod_axes: (1.0, 1.0, 1.0), - ), - ( - model: "voxygen.voxel.sprite.velorite.velorite_4", - offset: (-3.0, -5.0, 0.0), - lod_axes: (1.0, 1.0, 1.0), - ), - ( - model: "voxygen.voxel.sprite.velorite.velorite_5", - offset: (-3.0, -5.0, 0.0), - lod_axes: (1.0, 1.0, 1.0), - ), - ( - model: "voxygen.voxel.sprite.velorite.velorite_6", - offset: (-3.0, -5.0, 0.0), - lod_axes: (1.0, 1.0, 1.0), - ), - ( - model: "voxygen.voxel.sprite.velorite.velorite_7", - offset: (-3.0, -5.0, 0.0), - lod_axes: (1.0, 1.0, 1.0), - ), - ( - model: "voxygen.voxel.sprite.velorite.velorite_8", - offset: (-3.0, -5.0, 0.0), - lod_axes: (1.0, 1.0, 1.0), - ), - ( - model: "voxygen.voxel.sprite.velorite.velorite_9", - offset: (-3.0, -5.0, 0.0), - lod_axes: (1.0, 1.0, 1.0), - ), - ( - model: "voxygen.voxel.sprite.velorite.velorite_10", - offset: (-3.0, -5.0, 0.0), + model: "voxygen.voxel.sprite.velorite.velorite", + offset: (-4.0, -5.0, 0.0), lod_axes: (1.0, 1.0, 1.0), ), ], @@ -784,7 +739,7 @@ Chest: Some(( model: "voxygen.voxel.sprite.chests.chest", offset: (-7.0, -5.0, -0.0), lod_axes: (1.0, 1.0, 1.0), - ), + ), ( model: "voxygen.voxel.sprite.chests.chest_dark", offset: (-7.0, -5.0, -0.0), @@ -799,13 +754,13 @@ Chest: Some(( wind_sway: 0.0, )), CommonLockedChest: Some(( - variations: [ + variations: [ ( model: "voxygen.voxel.sprite.chests.chest_gold", offset: (-7.0, -5.0, -0.0), lod_axes: (1.0, 1.0, 1.0), ), - + ], wind_sway: 0.0, )), From 7fab9d192bbd4aff23f83039480355fe075314c5 Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 1 Apr 2023 17:26:15 -0400 Subject: [PATCH 26/28] Add repair bench to desert and cliff towns. --- world/src/site2/plot/cliff_tower.rs | 1 + world/src/site2/plot/desert_city_multiplot.rs | 1 + world/src/site2/plot/savannah_pit.rs | 1 + 3 files changed, 3 insertions(+) diff --git a/world/src/site2/plot/cliff_tower.rs b/world/src/site2/plot/cliff_tower.rs index 1a78662b9e..c31b78460c 100644 --- a/world/src/site2/plot/cliff_tower.rs +++ b/world/src/site2/plot/cliff_tower.rs @@ -632,6 +632,7 @@ impl Structure for CliffTower { SpriteKind::Forge, SpriteKind::DismantlingBench, SpriteKind::Anvil, + SpriteKind::RepairBench, ]; for dir in LOCALITY { let pos = super_center + dir * (width / 3); diff --git a/world/src/site2/plot/desert_city_multiplot.rs b/world/src/site2/plot/desert_city_multiplot.rs index 5c7b9cd4d5..5e223017b8 100644 --- a/world/src/site2/plot/desert_city_multiplot.rs +++ b/world/src/site2/plot/desert_city_multiplot.rs @@ -1150,6 +1150,7 @@ impl Structure for DesertCityMultiPlot { SpriteKind::Loom, SpriteKind::Anvil, SpriteKind::DismantlingBench, + SpriteKind::RepairBench, ]; 'outer: for d in 0..2 { for dir in NEIGHBORS { diff --git a/world/src/site2/plot/savannah_pit.rs b/world/src/site2/plot/savannah_pit.rs index 130154d181..62153b3b20 100644 --- a/world/src/site2/plot/savannah_pit.rs +++ b/world/src/site2/plot/savannah_pit.rs @@ -888,6 +888,7 @@ impl Structure for SavannahPit { SpriteKind::Loom, SpriteKind::Anvil, SpriteKind::DismantlingBench, + SpriteKind::RepairBench, ]; 'outer: for d in 0..2 { for dir in NEIGHBORS { From d523d310566392eeee07a260f578b533b0bf939b Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 1 Apr 2023 21:58:57 -0400 Subject: [PATCH 27/28] Fixed possible crash in crafting UI --- common/src/comp/inventory/item/mod.rs | 32 ++++++++++++++------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/common/src/comp/inventory/item/mod.rs b/common/src/comp/inventory/item/mod.rs index 4eb7c2ddea..289d92f745 100644 --- a/common/src/comp/inventory/item/mod.rs +++ b/common/src/comp/inventory/item/mod.rs @@ -348,6 +348,21 @@ impl ItemKind { }; result } + + pub fn has_durability(&self) -> bool { + match self { + ItemKind::Tool(_) => true, + ItemKind::Armor(armor) => armor.kind.has_durability(), + ItemKind::ModularComponent(_) + | ItemKind::Lantern(_) + | ItemKind::Glider + | ItemKind::Consumable { .. } + | ItemKind::Throwable { .. } + | ItemKind::Utility { .. } + | ItemKind::Ingredient { .. } + | ItemKind::TagExamples { .. } => false, + } + } } pub type ItemId = AtomicCell>; @@ -1215,20 +1230,7 @@ impl Item { DurabilityMultiplier(mult) } - pub fn has_durability(&self) -> bool { - match &*self.kind() { - ItemKind::Tool(_) => true, - ItemKind::Armor(armor) => armor.kind.has_durability(), - ItemKind::ModularComponent(_) - | ItemKind::Lantern(_) - | ItemKind::Glider - | ItemKind::Consumable { .. } - | ItemKind::Throwable { .. } - | ItemKind::Utility { .. } - | ItemKind::Ingredient { .. } - | ItemKind::TagExamples { .. } => false, - } - } + pub fn has_durability(&self) -> bool { self.kind().has_durability() } pub fn increment_damage(&mut self, ability_map: &AbilityMap, msm: &MaterialStatManifest) { if let Some(durability_lost) = &mut self.durability_lost { @@ -1351,7 +1353,7 @@ impl ItemDesc for ItemDef { fn components(&self) -> &[Item] { &[] } - fn has_durability(&self) -> bool { false } + fn has_durability(&self) -> bool { self.kind().has_durability() } fn durability(&self) -> Option { None } From 4ec27982624189c879df44e83a4dff331728cb32 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 6 Apr 2023 09:58:04 -0400 Subject: [PATCH 28/28] Fix velorite sprite offset --- assets/voxygen/voxel/sprite_manifest.ron | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/voxygen/voxel/sprite_manifest.ron b/assets/voxygen/voxel/sprite_manifest.ron index e5c607e6d7..47a24f4952 100644 --- a/assets/voxygen/voxel/sprite_manifest.ron +++ b/assets/voxygen/voxel/sprite_manifest.ron @@ -716,7 +716,7 @@ Velorite: Some(( variations: [ ( model: "voxygen.voxel.sprite.velorite.velorite_ore", - offset: (-5.0, -5.0, -5.0), + offset: (-5.0, -5.0, 0.0), lod_axes: (1.0, 1.0, 1.0), ), ],