Changed item definition id to better handle modular items.

This commit is contained in:
Sam
2022-05-05 16:42:38 -04:00
parent efc0189548
commit 64d07d02c4
17 changed files with 264 additions and 143 deletions

View File

@ -2,7 +2,8 @@ use crate::{
assets::AssetExt, assets::AssetExt,
comp::inventory::item::{ comp::inventory::item::{
armor::{Armor, ArmorKind}, armor::{Armor, ArmorKind},
modular, Glider, ItemDef, ItemDesc, ItemKind, Lantern, Throwable, Utility, modular, Glider, ItemDef, ItemDefinitionId, ItemDesc, ItemKind, Lantern, Throwable,
Utility,
}, },
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -29,29 +30,33 @@ impl<T: ItemDesc> From<&T> for ItemKey {
let item_definition_id = item_desc.item_definition_id(); let item_definition_id = item_desc.item_definition_id();
match &*item_desc.kind() { match &*item_desc.kind() {
ItemKind::Tool(_) => { ItemKind::Tool(_) => match item_definition_id {
if item_desc.is_modular() { ItemDefinitionId::Simple(id) => ItemKey::Tool(id.to_string()),
ItemDefinitionId::Modular { .. } => {
ItemKey::ModularWeapon(modular::weapon_to_key(item_desc)) ItemKey::ModularWeapon(modular::weapon_to_key(item_desc))
} else { },
ItemKey::Tool(item_definition_id.to_owned())
}
}, },
ItemKind::ModularComponent(mod_comp) => { ItemKind::ModularComponent(mod_comp) => {
use modular::ModularComponent; use modular::ModularComponent;
match mod_comp { match mod_comp {
ModularComponent::ToolPrimaryComponent { .. } => { ModularComponent::ToolPrimaryComponent { .. } => {
match modular::weapon_component_to_key( if let Some(id) = item_definition_id.raw() {
item_definition_id, match modular::weapon_component_to_key(id, item_desc.components()) {
item_desc.components(),
) {
Ok(key) => ItemKey::ModularWeaponComponent(key), Ok(key) => ItemKey::ModularWeaponComponent(key),
// TODO: Maybe use a different ItemKey? // TODO: Maybe use a different ItemKey?
Err(_) => ItemKey::Tool(item_definition_id.to_owned()), Err(_) => ItemKey::Tool(id.to_owned()),
}
} else {
ItemKey::Empty
} }
}, },
ModularComponent::ToolSecondaryComponent { .. } => { ModularComponent::ToolSecondaryComponent { .. } => {
if let Some(id) = item_definition_id.raw() {
// TODO: Maybe use a different ItemKey? // TODO: Maybe use a different ItemKey?
ItemKey::Tool(item_definition_id.to_owned()) ItemKey::Tool(id.to_owned())
} else {
ItemKey::Empty
}
}, },
} }
}, },
@ -59,7 +64,13 @@ impl<T: ItemDesc> From<&T> for ItemKey {
ItemKind::Glider(Glider { kind, .. }) => ItemKey::Glider(kind.clone()), ItemKind::Glider(Glider { kind, .. }) => ItemKey::Glider(kind.clone()),
ItemKind::Armor(Armor { kind, .. }) => ItemKey::Armor(kind.clone()), ItemKind::Armor(Armor { kind, .. }) => ItemKey::Armor(kind.clone()),
ItemKind::Utility { kind, .. } => ItemKey::Utility(*kind), ItemKind::Utility { kind, .. } => ItemKey::Utility(*kind),
ItemKind::Consumable { .. } => ItemKey::Consumable(item_definition_id.to_owned()), ItemKind::Consumable { .. } => {
if let Some(id) = item_definition_id.raw() {
ItemKey::Consumable(id.to_owned())
} else {
ItemKey::Empty
}
},
ItemKind::Throwable { kind, .. } => ItemKey::Throwable(*kind), ItemKind::Throwable { kind, .. } => ItemKey::Throwable(*kind),
ItemKind::Ingredient { kind, .. } => ItemKey::Ingredient(kind.clone()), ItemKind::Ingredient { kind, .. } => ItemKey::Ingredient(kind.clone()),
ItemKind::TagExamples { item_ids } => ItemKey::TagExamples( ItemKind::TagExamples { item_ids } => ItemKey::TagExamples(

View File

@ -379,7 +379,7 @@ pub enum ItemName {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum ItemBase { pub enum ItemBase {
Raw(Arc<ItemDef>), Simple(Arc<ItemDef>),
Modular(modular::ModularBase), Modular(modular::ModularBase),
} }
@ -392,7 +392,7 @@ impl Serialize for ItemBase {
S: Serializer, S: Serializer,
{ {
serializer.serialize_str(match self { serializer.serialize_str(match self {
ItemBase::Raw(item_def) => &item_def.item_definition_id, ItemBase::Simple(item_def) => &item_def.item_definition_id,
ItemBase::Modular(mod_base) => mod_base.pseudo_item_id(), ItemBase::Modular(mod_base) => mod_base.pseudo_item_id(),
}) })
} }
@ -422,7 +422,7 @@ impl<'de> Deserialize<'de> for ItemBase {
if serialized_item_base.starts_with(crate::modular_item_id_prefix!()) { if serialized_item_base.starts_with(crate::modular_item_id_prefix!()) {
ItemBase::Modular(ModularBase::load_from_pseudo_id(serialized_item_base)) ItemBase::Modular(ModularBase::load_from_pseudo_id(serialized_item_base))
} else { } else {
ItemBase::Raw(Arc::<ItemDef>::load_expect_cloned(serialized_item_base)) ItemBase::Simple(Arc::<ItemDef>::load_expect_cloned(serialized_item_base))
}, },
) )
} }
@ -435,19 +435,71 @@ impl<'de> Deserialize<'de> for ItemBase {
impl ItemBase { impl ItemBase {
fn num_slots(&self) -> u16 { fn num_slots(&self) -> u16 {
match self { match self {
ItemBase::Raw(item_def) => item_def.num_slots(), ItemBase::Simple(item_def) => item_def.num_slots(),
ItemBase::Modular(_) => 0, ItemBase::Modular(_) => 0,
} }
} }
}
fn item_definition_id(&self) -> &str { #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
match &self { pub enum ItemDefinitionId<'a> {
ItemBase::Raw(item_def) => &item_def.item_definition_id, Simple(&'a str),
ItemBase::Modular(mod_base) => mod_base.pseudo_item_id(), Modular {
pseudo_base: &'a str,
components: Vec<ItemDefinitionId<'a>>,
},
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum ItemDefinitionIdOwned {
Simple(String),
Modular {
pseudo_base: String,
components: Vec<ItemDefinitionIdOwned>,
},
}
impl ItemDefinitionIdOwned {
pub fn as_ref(&self) -> ItemDefinitionId<'_> {
match *self {
Self::Simple(ref id) => ItemDefinitionId::Simple(id),
Self::Modular {
ref pseudo_base,
ref components,
} => ItemDefinitionId::Modular {
pseudo_base,
components: components.iter().map(|comp| comp.as_ref()).collect(),
},
} }
} }
} }
impl<'a> ItemDefinitionId<'a> {
pub fn raw(&self) -> Option<&str> {
match self {
Self::Simple(id) => Some(id),
Self::Modular { .. } => None,
}
}
pub fn to_owned(&self) -> ItemDefinitionIdOwned {
match self {
Self::Simple(id) => ItemDefinitionIdOwned::Simple(String::from(*id)),
Self::Modular {
pseudo_base,
components,
} => ItemDefinitionIdOwned::Modular {
pseudo_base: String::from(*pseudo_base),
components: components.iter().map(|comp| comp.to_owned()).collect(),
},
}
}
}
impl Default for ItemDefinitionIdOwned {
fn default() -> Self { Self::Simple(String::new()) }
}
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct ItemDef { pub struct ItemDef {
#[serde(default)] #[serde(default)]
@ -580,7 +632,7 @@ impl ItemDef {
/// please don't rely on this for anything! /// please don't rely on this for anything!
impl PartialEq for Item { impl PartialEq for Item {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
if let (ItemBase::Raw(self_def), ItemBase::Raw(other_def)) = if let (ItemBase::Simple(self_def), ItemBase::Simple(other_def)) =
(&self.item_base, &other.item_base) (&self.item_base, &other.item_base)
{ {
self_def.item_definition_id == other_def.item_definition_id self_def.item_definition_id == other_def.item_definition_id
@ -706,7 +758,7 @@ impl Item {
let inner_item = if asset.starts_with("veloren.core.pseudo_items.modular") { let inner_item = if asset.starts_with("veloren.core.pseudo_items.modular") {
ItemBase::Modular(ModularBase::load_from_pseudo_id(asset)) ItemBase::Modular(ModularBase::load_from_pseudo_id(asset))
} else { } else {
ItemBase::Raw(Arc::<ItemDef>::load_cloned(asset)?) ItemBase::Simple(Arc::<ItemDef>::load_cloned(asset)?)
}; };
// TODO: Get msm and ability_map less hackily // TODO: Get msm and ability_map less hackily
let msm = &MaterialStatManifest::load().read(); let msm = &MaterialStatManifest::load().read();
@ -729,7 +781,7 @@ impl Item {
.collect(); .collect();
let mut new_item = Item::new_from_item_base( let mut new_item = Item::new_from_item_base(
match &self.item_base { match &self.item_base {
ItemBase::Raw(item_def) => ItemBase::Raw(Arc::clone(item_def)), ItemBase::Simple(item_def) => ItemBase::Simple(Arc::clone(item_def)),
ItemBase::Modular(mod_base) => ItemBase::Modular(mod_base.clone()), ItemBase::Modular(mod_base) => ItemBase::Modular(mod_base.clone()),
}, },
duplicated_components, duplicated_components,
@ -844,10 +896,22 @@ impl Item {
self.slots.iter_mut().filter_map(mem::take) self.slots.iter_mut().filter_map(mem::take)
} }
pub fn item_definition_id(&self) -> &str { self.item_base.item_definition_id() } pub fn item_definition_id(&self) -> ItemDefinitionId<'_> {
match &self.item_base {
ItemBase::Simple(item_def) => ItemDefinitionId::Simple(&item_def.item_definition_id),
ItemBase::Modular(mod_base) => ItemDefinitionId::Modular {
pseudo_base: mod_base.pseudo_item_id(),
components: self
.components
.iter()
.map(|item| item.item_definition_id())
.collect(),
},
}
}
pub fn is_same_item_def(&self, item_def: &ItemDef) -> bool { pub fn is_same_item_def(&self, item_def: &ItemDef) -> bool {
if let ItemBase::Raw(self_def) = &self.item_base { if let ItemBase::Simple(self_def) = &self.item_base {
self_def.item_definition_id == item_def.item_definition_id self_def.item_definition_id == item_def.item_definition_id
} else { } else {
false false
@ -885,7 +949,7 @@ impl Item {
pub fn name(&self) -> Cow<str> { pub fn name(&self) -> Cow<str> {
match &self.item_base { match &self.item_base {
ItemBase::Raw(item_def) => { ItemBase::Simple(item_def) => {
if self.components.is_empty() { if self.components.is_empty() {
Cow::Borrowed(&item_def.name) Cow::Borrowed(&item_def.name)
} else { } else {
@ -898,7 +962,7 @@ impl Item {
pub fn description(&self) -> &str { pub fn description(&self) -> &str {
match &self.item_base { match &self.item_base {
ItemBase::Raw(item_def) => &item_def.description, ItemBase::Simple(item_def) => &item_def.description,
// TODO: See if James wanted to make description, else leave with none // TODO: See if James wanted to make description, else leave with none
ItemBase::Modular(_) => "", ItemBase::Modular(_) => "",
} }
@ -906,7 +970,7 @@ impl Item {
pub fn kind(&self) -> Cow<ItemKind> { pub fn kind(&self) -> Cow<ItemKind> {
match &self.item_base { match &self.item_base {
ItemBase::Raw(item_def) => Cow::Borrowed(&item_def.kind), ItemBase::Simple(item_def) => Cow::Borrowed(&item_def.kind),
ItemBase::Modular(mod_base) => { ItemBase::Modular(mod_base) => {
// TODO: Try to move further upward // TODO: Try to move further upward
let msm = MaterialStatManifest::load().read(); let msm = MaterialStatManifest::load().read();
@ -919,7 +983,7 @@ impl Item {
pub fn is_stackable(&self) -> bool { pub fn is_stackable(&self) -> bool {
match &self.item_base { match &self.item_base {
ItemBase::Raw(item_def) => item_def.is_stackable(), ItemBase::Simple(item_def) => item_def.is_stackable(),
// TODO: Let whoever implements stackable modular items deal with this // TODO: Let whoever implements stackable modular items deal with this
ItemBase::Modular(_) => false, ItemBase::Modular(_) => false,
} }
@ -933,7 +997,7 @@ impl Item {
pub fn quality(&self) -> Quality { pub fn quality(&self) -> Quality {
match &self.item_base { match &self.item_base {
ItemBase::Raw(item_def) => item_def.quality, ItemBase::Simple(item_def) => item_def.quality,
ItemBase::Modular(mod_base) => mod_base.compute_quality(self.components()), ItemBase::Modular(mod_base) => mod_base.compute_quality(self.components()),
} }
} }
@ -964,7 +1028,7 @@ impl Item {
pub fn ability_spec(&self) -> Option<Cow<AbilitySpec>> { pub fn ability_spec(&self) -> Option<Cow<AbilitySpec>> {
match &self.item_base { match &self.item_base {
ItemBase::Raw(item_def) => { ItemBase::Simple(item_def) => {
item_def.ability_spec.as_ref().map(Cow::Borrowed).or({ item_def.ability_spec.as_ref().map(Cow::Borrowed).or({
// If no custom ability set is specified, fall back to abilityset of tool // If no custom ability set is specified, fall back to abilityset of tool
// kind. // kind.
@ -983,7 +1047,7 @@ impl Item {
// iterator? // iterator?
pub fn tags(&self) -> Vec<ItemTag> { pub fn tags(&self) -> Vec<ItemTag> {
match &self.item_base { match &self.item_base {
ItemBase::Raw(item_def) => item_def.tags.to_vec(), ItemBase::Simple(item_def) => item_def.tags.to_vec(),
// TODO: Do this properly. It'll probably be important at some point. // TODO: Do this properly. It'll probably be important at some point.
ItemBase::Modular(mod_base) => mod_base.generate_tags(self.components()), ItemBase::Modular(mod_base) => mod_base.generate_tags(self.components()),
} }
@ -991,7 +1055,7 @@ impl Item {
pub fn is_modular(&self) -> bool { pub fn is_modular(&self) -> bool {
match &self.item_base { match &self.item_base {
ItemBase::Raw(_) => false, ItemBase::Simple(_) => false,
ItemBase::Modular(_) => true, ItemBase::Modular(_) => true,
} }
} }
@ -1003,7 +1067,7 @@ impl Item {
let ability_map = &AbilityMap::load().read(); let ability_map = &AbilityMap::load().read();
let msm = &MaterialStatManifest::load().read(); let msm = &MaterialStatManifest::load().read();
Self::new_from_item_base( Self::new_from_item_base(
ItemBase::Raw(Arc::new(ItemDef::create_test_itemdef_from_kind(kind))), ItemBase::Simple(Arc::new(ItemDef::create_test_itemdef_from_kind(kind))),
Vec::new(), Vec::new(),
ability_map, ability_map,
msm, msm,
@ -1019,7 +1083,7 @@ pub trait ItemDesc {
fn kind(&self) -> Cow<ItemKind>; fn kind(&self) -> Cow<ItemKind>;
fn quality(&self) -> Quality; fn quality(&self) -> Quality;
fn num_slots(&self) -> u16; fn num_slots(&self) -> u16;
fn item_definition_id(&self) -> &str; fn item_definition_id(&self) -> ItemDefinitionId<'_>;
fn tags(&self) -> Vec<ItemTag>; fn tags(&self) -> Vec<ItemTag>;
fn is_modular(&self) -> bool; fn is_modular(&self) -> bool;
@ -1046,7 +1110,7 @@ impl ItemDesc for Item {
fn num_slots(&self) -> u16 { self.num_slots() } fn num_slots(&self) -> u16 { self.num_slots() }
fn item_definition_id(&self) -> &str { self.item_definition_id() } fn item_definition_id(&self) -> ItemDefinitionId<'_> { self.item_definition_id() }
fn tags(&self) -> Vec<ItemTag> { self.tags() } fn tags(&self) -> Vec<ItemTag> { self.tags() }
@ -1066,7 +1130,9 @@ impl ItemDesc for ItemDef {
fn num_slots(&self) -> u16 { self.slots } fn num_slots(&self) -> u16 { self.slots }
fn item_definition_id(&self) -> &str { &self.item_definition_id } fn item_definition_id(&self) -> ItemDefinitionId<'_> {
ItemDefinitionId::Simple(&self.item_definition_id)
}
fn tags(&self) -> Vec<ItemTag> { self.tags.to_vec() } fn tags(&self) -> Vec<ItemTag> { self.tags.to_vec() }
@ -1097,7 +1163,7 @@ impl<'a, T: ItemDesc + ?Sized> ItemDesc for &'a T {
fn num_slots(&self) -> u16 { (*self).num_slots() } fn num_slots(&self) -> u16 { (*self).num_slots() }
fn item_definition_id(&self) -> &str { (*self).item_definition_id() } fn item_definition_id(&self) -> ItemDefinitionId<'_> { (*self).item_definition_id() }
fn components(&self) -> &[Item] { (*self).components() } fn components(&self) -> &[Item] { (*self).components() }

View File

@ -238,7 +238,13 @@ impl ModularComponent {
Self::ToolPrimaryComponent { stats, .. } => { Self::ToolPrimaryComponent { stats, .. } => {
let average_material_mult = components let average_material_mult = components
.iter() .iter()
.filter_map(|comp| msm.0.get(comp.item_definition_id()).copied().zip(Some(1))) .filter_map(|comp| {
comp.item_definition_id()
.raw()
.and_then(|id| msm.0.get(id))
.copied()
.zip(Some(1))
})
.reduce(|(stats_a, count_a), (stats_b, count_b)| { .reduce(|(stats_a, count_a), (stats_b, count_b)| {
(stats_a + stats_b, count_a + count_b) (stats_a + stats_b, count_a + count_b)
}) })
@ -308,7 +314,7 @@ lazy_static! {
if let Ok(items) = Item::new_from_asset_glob(&directory) { if let Ok(items) = Item::new_from_asset_glob(&directory) {
items items
.into_iter() .into_iter()
.map(|comp| comp.item_definition_id().to_owned()) .filter_map(|comp| Some(comp.item_definition_id().raw()?.to_owned()))
.filter_map(|id| Arc::<ItemDef>::load_cloned(&id).ok()) .filter_map(|id| Arc::<ItemDef>::load_cloned(&id).ok())
.for_each(|comp_def| { .for_each(|comp_def| {
if let ItemKind::ModularComponent(ModularComponent::ToolSecondaryComponent { hand_restriction, .. }) = comp_def.kind { if let ItemKind::ModularComponent(ModularComponent::ToolSecondaryComponent { hand_restriction, .. }) = comp_def.kind {
@ -378,7 +384,7 @@ pub fn random_weapon(
.ok_or(ModularWeaponCreationError::SecondaryComponentNotFound)? .ok_or(ModularWeaponCreationError::SecondaryComponentNotFound)?
.0; .0;
Item::new_from_item_base( Item::new_from_item_base(
ItemBase::Raw(Arc::clone(def)), ItemBase::Simple(Arc::clone(def)),
Vec::new(), Vec::new(),
ability_map, ability_map,
msm, msm,
@ -441,9 +447,9 @@ pub fn weapon_to_key(mod_weap: impl ItemDesc) -> ModularWeaponKey {
.iter() .iter()
.find_map(|comp| match &*comp.kind() { .find_map(|comp| match &*comp.kind() {
ItemKind::ModularComponent(ModularComponent::ToolPrimaryComponent { .. }) => { ItemKind::ModularComponent(ModularComponent::ToolPrimaryComponent { .. }) => {
let component_id = comp.item_definition_id().to_owned(); let component_id = comp.item_definition_id().raw()?.to_owned();
let material_id = comp.components().iter().find_map(|mat| match &*mat.kind() { let material_id = comp.components().iter().find_map(|mat| match &*mat.kind() {
ItemKind::Ingredient { .. } => Some(mat.item_definition_id().to_owned()), ItemKind::Ingredient { .. } => Some(mat.item_definition_id().raw()?.to_owned()),
_ => None, _ => None,
}); });
Some((component_id, material_id)) Some((component_id, material_id))
@ -469,7 +475,7 @@ pub fn weapon_component_to_key(
components: &[Item], components: &[Item],
) -> Result<ModularWeaponComponentKey, ModularWeaponComponentKeyError> { ) -> Result<ModularWeaponComponentKey, ModularWeaponComponentKeyError> {
match components.iter().find_map(|mat| match &*mat.kind() { match components.iter().find_map(|mat| match &*mat.kind() {
ItemKind::Ingredient { .. } => Some(mat.item_definition_id().to_owned()), ItemKind::Ingredient { .. } => Some(mat.item_definition_id().raw()?.to_owned()),
_ => None, _ => None,
}) { }) {
Some(material_id) => Ok((item_def_id.to_owned(), material_id)), Some(material_id) => Ok((item_def_id.to_owned(), material_id)),

View File

@ -23,7 +23,7 @@ pub(super) fn get_test_bag(slots: u16) -> Item {
); );
Item::new_from_item_base( Item::new_from_item_base(
ItemBase::Raw(Arc::new(item_def)), ItemBase::Simple(Arc::new(item_def)),
Vec::new(), Vec::new(),
&AbilityMap::load().read(), &AbilityMap::load().read(),
&MaterialStatManifest::load().read(), &MaterialStatManifest::load().read(),

View File

@ -135,7 +135,7 @@ impl Recipe {
let (item_def, quantity) = &self.output; let (item_def, quantity) = &self.output;
let crafted_item = Item::new_from_item_base( let crafted_item = Item::new_from_item_base(
ItemBase::Raw(Arc::clone(item_def)), ItemBase::Simple(Arc::clone(item_def)),
components, components,
ability_map, ability_map,
msm, msm,
@ -659,7 +659,7 @@ impl ComponentRecipe {
.iter() .iter()
.map(|item_def| { .map(|item_def| {
Item::new_from_item_base( Item::new_from_item_base(
ItemBase::Raw(Arc::clone(item_def)), ItemBase::Simple(Arc::clone(item_def)),
Vec::new(), Vec::new(),
ability_map, ability_map,
msm, msm,
@ -667,7 +667,7 @@ impl ComponentRecipe {
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
Item::new_from_item_base( Item::new_from_item_base(
ItemBase::Raw(Arc::clone(item_def)), ItemBase::Simple(Arc::clone(item_def)),
components, components,
ability_map, ability_map,
msm, msm,

View File

@ -29,8 +29,8 @@ pub struct StaticData {
pub recover_duration: Duration, pub recover_duration: Duration,
/// Inventory slot to use item from /// Inventory slot to use item from
pub inv_slot: InvSlotId, pub inv_slot: InvSlotId,
/// Item definition id, used to verify that slot still has the correct item /// Item hash, used to verify that slot still has the correct item
pub item_definition_id: String, pub item_hash: u64,
/// Kind of item used /// Kind of item used
pub item_kind: ItemUseKind, pub item_kind: ItemUseKind,
/// Had weapon wielded /// Had weapon wielded
@ -213,7 +213,7 @@ fn use_item(data: &JoinData, output_events: &mut OutputEvents, state: &Data) {
.inventory .inventory
.and_then(|inv| inv.get(state.static_data.inv_slot)) .and_then(|inv| inv.get(state.static_data.inv_slot))
.map_or(false, |item| { .map_or(false, |item| {
item.item_definition_id() == state.static_data.item_definition_id item.item_hash() == state.static_data.item_hash
}); });
if item_is_same { if item_is_same {
// Create inventory manipulation event // Create inventory manipulation event

View File

@ -714,7 +714,7 @@ pub fn handle_manipulate_loadout(
recover_duration, recover_duration,
inv_slot, inv_slot,
item_kind, item_kind,
item_definition_id: item.item_definition_id().to_string(), item_hash: item.item_hash(),
was_wielded: matches!(data.character, CharacterState::Wielding(_)), was_wielded: matches!(data.character, CharacterState::Wielding(_)),
was_sneak: data.character.is_stealthy(), was_sneak: data.character.is_stealthy(),
}, },

View File

@ -1,7 +1,9 @@
use std::cmp::Ordering; use std::cmp::Ordering;
use crate::{ use crate::{
comp::inventory::{slot::InvSlotId, trade_pricing::TradePricing, Inventory}, comp::inventory::{
item::ItemDefinitionIdOwned, slot::InvSlotId, trade_pricing::TradePricing, Inventory,
},
terrain::BiomeKind, terrain::BiomeKind,
uid::Uid, uid::Uid,
}; };
@ -387,7 +389,12 @@ impl SitePrices {
.as_ref() .as_ref()
.and_then(|ri| { .and_then(|ri| {
ri.inventory.get(slot).map(|item| { ri.inventory.get(slot).map(|item| {
if let Some(vec) = TradePricing::get_materials(&item.name) { if let Some(vec) = item
.name
.as_ref()
.raw()
.and_then(TradePricing::get_materials)
{
vec.iter() vec.iter()
.map(|(amount2, material)| { .map(|(amount2, material)| {
self.values.get(material).copied().unwrap_or_default() self.values.get(material).copied().unwrap_or_default()
@ -409,7 +416,7 @@ impl SitePrices {
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct ReducedInventoryItem { pub struct ReducedInventoryItem {
pub name: String, pub name: ItemDefinitionIdOwned,
pub amount: u32, pub amount: u32,
} }
@ -425,7 +432,7 @@ impl ReducedInventory {
.filter(|(_, it)| it.is_some()) .filter(|(_, it)| it.is_some())
.map(|(sl, it)| { .map(|(sl, it)| {
(sl, ReducedInventoryItem { (sl, ReducedInventoryItem {
name: it.as_ref().unwrap().item_definition_id().to_string(), name: it.as_ref().unwrap().item_definition_id().to_owned(),
amount: it.as_ref().unwrap().amount(), amount: it.as_ref().unwrap().amount(),
}) })
}) })

View File

@ -177,16 +177,15 @@ pub fn handle_mine_block(
if let (Some(tool), Some(uid), Some(exp_reward)) = ( if let (Some(tool), Some(uid), Some(exp_reward)) = (
tool, tool,
state.ecs().uid_from_entity(entity), state.ecs().uid_from_entity(entity),
RESOURCE_EXPERIENCE_MANIFEST item.item_definition_id()
.read() .raw()
.0 .and_then(|id| RESOURCE_EXPERIENCE_MANIFEST.read().0.get(id).copied()),
.get(item.item_definition_id()),
) { ) {
let skill_group = SkillGroupKind::Weapon(tool); let skill_group = SkillGroupKind::Weapon(tool);
let outcome_bus = state.ecs().read_resource::<EventBus<Outcome>>(); let outcome_bus = state.ecs().read_resource::<EventBus<Outcome>>();
let positions = state.ecs().read_component::<comp::Pos>(); let positions = state.ecs().read_component::<comp::Pos>();
if let (Some(level_outcome), Some(pos)) = ( if let (Some(level_outcome), Some(pos)) = (
skillset.add_experience(skill_group, *exp_reward), skillset.add_experience(skill_group, exp_reward),
positions.get(entity), positions.get(entity),
) { ) {
outcome_bus.emit_now(Outcome::SkillPointGain { outcome_bus.emit_now(Outcome::SkillPointGain {
@ -198,7 +197,7 @@ pub fn handle_mine_block(
} }
outcome_bus.emit_now(Outcome::ExpChange { outcome_bus.emit_now(Outcome::ExpChange {
uid, uid,
exp: *exp_reward, exp: exp_reward,
xp_pools: HashSet::from_iter(vec![skill_group]), xp_pools: HashSet::from_iter(vec![skill_group]),
}); });
} }
@ -223,10 +222,10 @@ pub fn handle_mine_block(
rng.gen_bool(chance_mod * f64::from(skill_level)) rng.gen_bool(chance_mod * f64::from(skill_level))
}; };
let double_gain = (item.item_definition_id().contains("mineral.ore.") let double_gain = item.item_definition_id().raw().map_or(false, |id| {
&& need_double_ore(&mut rng)) (id.contains("mineral.ore.") && need_double_ore(&mut rng))
|| (item.item_definition_id().contains("mineral.gem.") || (id.contains("mineral.gem.") && need_double_gem(&mut rng))
&& need_double_gem(&mut rng)); });
if double_gain { if double_gain {
// Ignore non-stackable errors // Ignore non-stackable errors

View File

@ -678,13 +678,17 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
slots, slots,
} => { } => {
let component_recipes = default_component_recipe_book().read(); let component_recipes = default_component_recipe_book().read();
let item_id = |slot| inventory.get(slot).map(|item| item.item_definition_id()); let item_id = |slot| {
inventory
.get(slot)
.and_then(|item| item.item_definition_id().raw().map(String::from))
};
if let Some(material_item_id) = item_id(material) { if let Some(material_item_id) = item_id(material) {
component_recipes component_recipes
.get(&ComponentKey { .get(&ComponentKey {
toolkind, toolkind,
material: String::from(material_item_id), material: material_item_id,
modifier: modifier.and_then(item_id).map(String::from), modifier: modifier.and_then(item_id),
}) })
.filter(|r| { .filter(|r| {
if let Some(needed_sprite) = r.craft_sprite { if let Some(needed_sprite) = r.craft_sprite {

View File

@ -3,7 +3,7 @@ use common::{
comp::{ comp::{
agent::{Agent, AgentEvent}, agent::{Agent, AgentEvent},
inventory::{ inventory::{
item::{tool::AbilityMap, MaterialStatManifest}, item::{tool::AbilityMap, ItemDefinitionIdOwned, MaterialStatManifest},
Inventory, Inventory,
}, },
}, },
@ -239,7 +239,7 @@ fn commit_trade(ecs: &specs::World, trade: &PendingTrade) -> TradeResult {
// Hashmap to compute merged stackable stacks, including overflow checks // Hashmap to compute merged stackable stacks, including overflow checks
// TODO: add a ComponentKey struct to compare items properly, see issue #1226 // TODO: add a ComponentKey struct to compare items properly, see issue #1226
let mut stackable_items: HashMap<String, TradeQuantities> = HashMap::new(); let mut stackable_items: HashMap<ItemDefinitionIdOwned, TradeQuantities> = HashMap::new();
for who in [0, 1].iter().cloned() { for who in [0, 1].iter().cloned() {
for (slot, quantity) in trade.offers[who].iter() { for (slot, quantity) in trade.offers[who].iter() {
let inventory = inventories.get_mut(entities[who]).expect(invmsg); let inventory = inventories.get_mut(entities[who]).expect(invmsg);
@ -274,7 +274,7 @@ fn commit_trade(ecs: &specs::World, trade: &PendingTrade) -> TradeResult {
max_stack_size, max_stack_size,
trade_quantities, trade_quantities,
} = stackable_items } = stackable_items
.entry(item.item_definition_id().to_string()) .entry(item.item_definition_id().to_owned())
.or_insert_with(|| TradeQuantities::new(item.max_amount())); .or_insert_with(|| TradeQuantities::new(item.max_amount()));
trade_quantities[who].full_stacks += 1; trade_quantities[who].full_stacks += 1;
@ -295,7 +295,7 @@ fn commit_trade(ecs: &specs::World, trade: &PendingTrade) -> TradeResult {
max_stack_size: _, max_stack_size: _,
trade_quantities, trade_quantities,
} = stackable_items } = stackable_items
.entry(item.item_definition_id().to_string()) .entry(item.item_definition_id().to_owned())
.or_insert_with(|| TradeQuantities::new(item.max_amount())); .or_insert_with(|| TradeQuantities::new(item.max_amount()));
trade_quantities[who].quantity_sold += *quantity as u128; trade_quantities[who].quantity_sold += *quantity as u128;
@ -338,7 +338,7 @@ fn commit_trade(ecs: &specs::World, trade: &PendingTrade) -> TradeResult {
.slots() .slots()
.flatten() .flatten()
.filter_map(|it| { .filter_map(|it| {
if it.item_definition_id() == item_id { if it.item_definition_id() == item_id.as_ref() {
Some(*max_stack_size as u128 - it.amount() as u128) Some(*max_stack_size as u128 - it.amount() as u128)
} else { } else {
None None

View File

@ -11,7 +11,7 @@ use common::{
character::CharacterId, character::CharacterId,
comp::{ comp::{
inventory::{ inventory::{
item::{tool::AbilityMap, Item as VelorenItem, MaterialStatManifest}, item::{tool::AbilityMap, Item as VelorenItem, ItemDefinitionId, MaterialStatManifest},
loadout::{Loadout, LoadoutError}, loadout::{Loadout, LoadoutError},
loadout_builder::LoadoutBuilder, loadout_builder::LoadoutBuilder,
slot::InvSlotId, slot::InvSlotId,
@ -165,9 +165,14 @@ pub fn convert_items_to_database_items(
bfs_queue.push_back((format!("component_{}", i), Some(component), item_id)); bfs_queue.push_back((format!("component_{}", i), Some(component), item_id));
} }
let persistence_item_id = match item.item_definition_id() {
ItemDefinitionId::Simple(id) => id.to_owned(),
ItemDefinitionId::Modular { pseudo_base, .. } => pseudo_base.to_owned(),
};
let upsert = ItemModelPair { let upsert = ItemModelPair {
model: Item { model: Item {
item_definition_id: item.item_definition_id().to_owned(), item_definition_id: persistence_item_id,
position, position,
parent_container_item_id, parent_container_item_id,
item_id, item_id,

View File

@ -977,8 +977,9 @@ impl<'a> Widget for Crafting<'a> {
// }) }, // }) },
RecipeKind::Component(_) => |item| { RecipeKind::Component(_) => |item| {
item.map_or(false, |item| { item.map_or(false, |item| {
item.item_definition_id() item.item_definition_id().raw().map_or(false, |id| {
.starts_with("common.items.crafting_ing.animal_misc") id.starts_with("common.items.crafting_ing.animal_misc")
})
}) })
}, },
RecipeKind::Simple => |_| unreachable!(), RecipeKind::Simple => |_| unreachable!(),
@ -1099,7 +1100,7 @@ impl<'a> Widget for Crafting<'a> {
if let Some(material) = primary_slot if let Some(material) = primary_slot
.invslot .invslot
.and_then(|slot| self.inventory.get(slot)) .and_then(|slot| self.inventory.get(slot))
.map(|item| String::from(item.item_definition_id())) .and_then(|item| item.item_definition_id().raw().map(String::from))
{ {
let component_key = ComponentKey { let component_key = ComponentKey {
toolkind, toolkind,
@ -1107,7 +1108,9 @@ impl<'a> Widget for Crafting<'a> {
modifier: secondary_slot modifier: secondary_slot
.invslot .invslot
.and_then(|slot| self.inventory.get(slot)) .and_then(|slot| self.inventory.get(slot))
.map(|item| String::from(item.item_definition_id())), .and_then(|item| {
item.item_definition_id().raw().map(String::from)
}),
}; };
self.client.component_recipe_book().get(&component_key).map( self.client.component_recipe_book().get(&component_key).map(
|component_recipe| { |component_recipe| {
@ -1405,14 +1408,14 @@ impl<'a> Widget for Crafting<'a> {
RecipeKind::Component(toolkind) => { RecipeKind::Component(toolkind) => {
if let Some(material) = modular_primary_slot if let Some(material) = modular_primary_slot
.and_then(|slot| self.inventory.get(slot)) .and_then(|slot| self.inventory.get(slot))
.map(|item| String::from(item.item_definition_id())) .and_then(|item| item.item_definition_id().raw().map(String::from))
{ {
let component_key = ComponentKey { let component_key = ComponentKey {
toolkind, toolkind,
material, material,
modifier: modular_secondary_slot modifier: modular_secondary_slot
.and_then(|slot| self.inventory.get(slot)) .and_then(|slot| self.inventory.get(slot))
.map(|item| String::from(item.item_definition_id())), .and_then(|item| item.item_definition_id().raw().map(String::from)),
}; };
if let Some(comp_recipe) = if let Some(comp_recipe) =
self.client.component_recipe_book().get(&component_key) self.client.component_recipe_book().get(&component_key)
@ -1491,30 +1494,35 @@ impl<'a> Widget for Crafting<'a> {
for (i, (recipe_input, amount)) in ingredients.enumerate() { for (i, (recipe_input, amount)) in ingredients.enumerate() {
let item_def = match recipe_input { let item_def = match recipe_input {
RecipeInput::Item(item_def) => Arc::clone(item_def), RecipeInput::Item(item_def) => Arc::clone(item_def),
RecipeInput::Tag(tag) | RecipeInput::TagSameItem(tag) => { RecipeInput::Tag(tag) | RecipeInput::TagSameItem(tag) => self
Arc::<ItemDef>::load_expect_cloned( .inventory
self.inventory
.slots() .slots()
.find_map(|slot| { .find_map(|slot| {
slot.as_ref().and_then(|item| { slot.as_ref().and_then(|item| {
if item.matches_recipe_input(recipe_input, amount) { if item.matches_recipe_input(recipe_input, amount) {
Some(item.item_definition_id()) item.item_definition_id()
.raw()
.map(Arc::<ItemDef>::load_expect_cloned)
} else { } else {
None None
} }
}) })
}) })
.or_else(|| tag.exemplar_identifier()) .unwrap_or_else(|| {
Arc::<ItemDef>::load_expect_cloned(
tag.exemplar_identifier()
.unwrap_or("common.items.weapons.empty.empty"), .unwrap_or("common.items.weapons.empty.empty"),
) )
}, }),
RecipeInput::ListSameItem(item_defs) => Arc::<ItemDef>::load_expect_cloned( RecipeInput::ListSameItem(item_defs) => self
self.inventory .inventory
.slots() .slots()
.find_map(|slot| { .find_map(|slot| {
slot.as_ref().and_then(|item| { slot.as_ref().and_then(|item| {
if item.matches_recipe_input(recipe_input, amount) { if item.matches_recipe_input(recipe_input, amount) {
Some(item.item_definition_id()) item.item_definition_id()
.raw()
.map(Arc::<ItemDef>::load_expect_cloned)
} else { } else {
None None
} }
@ -1523,10 +1531,17 @@ impl<'a> Widget for Crafting<'a> {
.unwrap_or_else(|| { .unwrap_or_else(|| {
item_defs item_defs
.first() .first()
.map(|i| i.item_definition_id()) .and_then(|i| {
.unwrap_or_else(|| "common.items.weapons.empty.empty") i.item_definition_id()
.raw()
.map(Arc::<ItemDef>::load_expect_cloned)
})
.unwrap_or_else(|| {
Arc::<ItemDef>::load_expect_cloned(
"common.items.weapons.empty.empty",
)
})
}), }),
),
}; };
// Grey color for images and text if their amount is too low to craft the // Grey color for images and text if their amount is too low to craft the

View File

@ -3689,8 +3689,10 @@ impl Hud {
false, false,
); );
if let Some(item) = inventory.get(slot) { if let Some(item) = inventory.get(slot) {
if let Some(materials) = if let Some(materials) = item
TradePricing::get_materials(item.item_definition_id()) .item_definition_id()
.raw()
.and_then(TradePricing::get_materials)
{ {
let unit_price: f32 = materials let unit_price: f32 = materials
.iter() .iter()

View File

@ -15,7 +15,7 @@ use common::{
item::{ item::{
armor::{Armor, ArmorKind}, armor::{Armor, ArmorKind},
item_key::ItemKey, item_key::ItemKey,
modular, Item, ItemKind, modular, Item, ItemDefinitionId, ItemKind,
}, },
CharacterState, CharacterState,
}, },
@ -221,12 +221,11 @@ impl CharacterCacheKey {
}) })
}, },
tool: if are_tools_visible { tool: if are_tools_visible {
let tool_key_from_item = |item: &Item| { let tool_key_from_item = |item: &Item| match item.item_definition_id() {
if item.is_modular() { ItemDefinitionId::Simple(id) => ToolKey::Tool(String::from(id)),
ItemDefinitionId::Modular { .. } => {
ToolKey::Modular(modular::weapon_to_key(item)) ToolKey::Modular(modular::weapon_to_key(item))
} else { },
ToolKey::Tool(item.item_definition_id().to_owned())
}
}; };
Some(CharacterToolKey { Some(CharacterToolKey {
active: inventory active: inventory

View File

@ -8,7 +8,7 @@ use std::{cell::RefCell, collections::HashSet, rc::Rc, result::Result, sync::Arc
use mumble_link::SharedLink; use mumble_link::SharedLink;
use ordered_float::OrderedFloat; use ordered_float::OrderedFloat;
use specs::{Join, WorldExt}; use specs::{Join, WorldExt};
use tracing::{error, info, warn}; use tracing::{error, info, trace, warn};
use vek::*; use vek::*;
use client::{self, Client}; use client::{self, Client};
@ -18,7 +18,7 @@ use common::{
comp::{ comp::{
inventory::slot::{EquipSlot, Slot}, inventory::slot::{EquipSlot, Slot},
invite::InviteKind, invite::InviteKind,
item::{tool::ToolKind, ItemDef, ItemDesc}, item::{tool::ToolKind, ItemDef, ItemDefinitionId, ItemDesc},
ChatMsg, ChatType, InputKind, InventoryUpdateEvent, Pos, Stats, UtteranceKind, Vel, ChatMsg, ChatType, InputKind, InventoryUpdateEvent, Pos, Stats, UtteranceKind, Vel,
}, },
consts::MAX_MOUNT_RANGE, consts::MAX_MOUNT_RANGE,
@ -250,8 +250,8 @@ impl SessionState {
self.hud.add_failed_entity_pickup(entity); self.hud.add_failed_entity_pickup(entity);
} }
}, },
InventoryUpdateEvent::Collected(item) => { InventoryUpdateEvent::Collected(item) => match item.item_definition_id() {
match Arc::<ItemDef>::load_cloned(item.item_definition_id()) { ItemDefinitionId::Simple(id) => match Arc::<ItemDef>::load_cloned(id) {
Result::Ok(item_def) => { Result::Ok(item_def) => {
self.hud.new_loot_message(LootMessage { self.hud.new_loot_message(LootMessage {
item: item_def, item: item_def,
@ -261,11 +261,14 @@ impl SessionState {
Result::Err(e) => { Result::Err(e) => {
warn!( warn!(
?e, ?e,
"Item not present on client: {}", "Item not present on client: {:?}",
item.item_definition_id() item.item_definition_id()
); );
}, },
} },
ItemDefinitionId::Modular { .. } => {
trace!("Modular items not currently supported for loot messages.")
},
}, },
_ => {}, _ => {},
}; };
@ -1445,7 +1448,9 @@ impl PlayState for SessionState {
.inventories() .inventories()
.get(client.entity()) .get(client.entity())
.and_then(|inv| inv.get(slot)) .and_then(|inv| inv.get(slot))
.map(|item| String::from(item.item_definition_id())) .and_then(|item| {
item.item_definition_id().raw().map(String::from)
})
}; };
if let Some(material_id) = item_id(material) { if let Some(material_id) = item_id(material) {
let key = recipe::ComponentKey { let key = recipe::ComponentKey {

View File

@ -1316,8 +1316,10 @@ impl<'a> Widget for ItemTooltip<'a> {
} }
// Price display // Price display
if let Some((buy, sell, factor)) = if let Some((buy, sell, factor)) = item
util::price_desc(self.prices, item.item_definition_id(), i18n) .item_definition_id()
.raw()
.and_then(|id| util::price_desc(self.prices, id, i18n))
{ {
widget::Text::new(&buy) widget::Text::new(&buy)
.x_align_to(state.ids.item_frame, conrod_core::position::Align::Start) .x_align_to(state.ids.item_frame, conrod_core::position::Align::Start)
@ -1413,11 +1415,11 @@ impl<'a> Widget for ItemTooltip<'a> {
}; };
// Price // Price
let price_h: f64 = if let Some((buy, sell, _)) = util::price_desc( let price_h: f64 = if let Some((buy, sell, _)) = item
self.prices, .item_definition_id()
item.item_definition_id(), .raw()
self.localized_strings, .and_then(|id| util::price_desc(self.prices, id, self.localized_strings))
) { {
//Get localized tooltip strings (gotten here because these should only show if //Get localized tooltip strings (gotten here because these should only show if
// in a trade- aka if buy/sell prices are present) // in a trade- aka if buy/sell prices are present)
let tt_hint_1 = self.localized_strings.get("hud.trade.tooltip_hint_1"); let tt_hint_1 = self.localized_strings.get("hud.trade.tooltip_hint_1");