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.", + ); + } +}