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

View File

@ -379,7 +379,7 @@ pub enum ItemName {
#[derive(Clone, Debug)]
pub enum ItemBase {
Raw(Arc<ItemDef>),
Simple(Arc<ItemDef>),
Modular(modular::ModularBase),
}
@ -392,7 +392,7 @@ impl Serialize for ItemBase {
S: Serializer,
{
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(),
})
}
@ -422,7 +422,7 @@ impl<'de> Deserialize<'de> for ItemBase {
if serialized_item_base.starts_with(crate::modular_item_id_prefix!()) {
ItemBase::Modular(ModularBase::load_from_pseudo_id(serialized_item_base))
} 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 {
fn num_slots(&self) -> u16 {
match self {
ItemBase::Raw(item_def) => item_def.num_slots(),
ItemBase::Simple(item_def) => item_def.num_slots(),
ItemBase::Modular(_) => 0,
}
}
}
fn item_definition_id(&self) -> &str {
match &self {
ItemBase::Raw(item_def) => &item_def.item_definition_id,
ItemBase::Modular(mod_base) => mod_base.pseudo_item_id(),
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum ItemDefinitionId<'a> {
Simple(&'a str),
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)]
pub struct ItemDef {
#[serde(default)]
@ -580,7 +632,7 @@ impl ItemDef {
/// please don't rely on this for anything!
impl PartialEq for Item {
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_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") {
ItemBase::Modular(ModularBase::load_from_pseudo_id(asset))
} else {
ItemBase::Raw(Arc::<ItemDef>::load_cloned(asset)?)
ItemBase::Simple(Arc::<ItemDef>::load_cloned(asset)?)
};
// TODO: Get msm and ability_map less hackily
let msm = &MaterialStatManifest::load().read();
@ -729,7 +781,7 @@ impl Item {
.collect();
let mut new_item = Item::new_from_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()),
},
duplicated_components,
@ -844,10 +896,22 @@ impl Item {
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 {
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
} else {
false
@ -885,7 +949,7 @@ impl Item {
pub fn name(&self) -> Cow<str> {
match &self.item_base {
ItemBase::Raw(item_def) => {
ItemBase::Simple(item_def) => {
if self.components.is_empty() {
Cow::Borrowed(&item_def.name)
} else {
@ -898,7 +962,7 @@ impl Item {
pub fn description(&self) -> &str {
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
ItemBase::Modular(_) => "",
}
@ -906,7 +970,7 @@ impl Item {
pub fn kind(&self) -> Cow<ItemKind> {
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) => {
// TODO: Try to move further upward
let msm = MaterialStatManifest::load().read();
@ -919,7 +983,7 @@ impl Item {
pub fn is_stackable(&self) -> bool {
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
ItemBase::Modular(_) => false,
}
@ -933,7 +997,7 @@ impl Item {
pub fn quality(&self) -> Quality {
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()),
}
}
@ -964,7 +1028,7 @@ impl Item {
pub fn ability_spec(&self) -> Option<Cow<AbilitySpec>> {
match &self.item_base {
ItemBase::Raw(item_def) => {
ItemBase::Simple(item_def) => {
item_def.ability_spec.as_ref().map(Cow::Borrowed).or({
// If no custom ability set is specified, fall back to abilityset of tool
// kind.
@ -983,7 +1047,7 @@ impl Item {
// iterator?
pub fn tags(&self) -> Vec<ItemTag> {
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.
ItemBase::Modular(mod_base) => mod_base.generate_tags(self.components()),
}
@ -991,7 +1055,7 @@ impl Item {
pub fn is_modular(&self) -> bool {
match &self.item_base {
ItemBase::Raw(_) => false,
ItemBase::Simple(_) => false,
ItemBase::Modular(_) => true,
}
}
@ -1003,7 +1067,7 @@ impl Item {
let ability_map = &AbilityMap::load().read();
let msm = &MaterialStatManifest::load().read();
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(),
ability_map,
msm,
@ -1019,7 +1083,7 @@ pub trait ItemDesc {
fn kind(&self) -> Cow<ItemKind>;
fn quality(&self) -> Quality;
fn num_slots(&self) -> u16;
fn item_definition_id(&self) -> &str;
fn item_definition_id(&self) -> ItemDefinitionId<'_>;
fn tags(&self) -> Vec<ItemTag>;
fn is_modular(&self) -> bool;
@ -1046,7 +1110,7 @@ impl ItemDesc for Item {
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() }
@ -1066,7 +1130,9 @@ impl ItemDesc for ItemDef {
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() }
@ -1097,7 +1163,7 @@ impl<'a, T: ItemDesc + ?Sized> ItemDesc for &'a T {
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() }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -177,16 +177,15 @@ pub fn handle_mine_block(
if let (Some(tool), Some(uid), Some(exp_reward)) = (
tool,
state.ecs().uid_from_entity(entity),
RESOURCE_EXPERIENCE_MANIFEST
.read()
.0
.get(item.item_definition_id()),
item.item_definition_id()
.raw()
.and_then(|id| RESOURCE_EXPERIENCE_MANIFEST.read().0.get(id).copied()),
) {
let skill_group = SkillGroupKind::Weapon(tool);
let outcome_bus = state.ecs().read_resource::<EventBus<Outcome>>();
let positions = state.ecs().read_component::<comp::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),
) {
outcome_bus.emit_now(Outcome::SkillPointGain {
@ -198,7 +197,7 @@ pub fn handle_mine_block(
}
outcome_bus.emit_now(Outcome::ExpChange {
uid,
exp: *exp_reward,
exp: exp_reward,
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))
};
let double_gain = (item.item_definition_id().contains("mineral.ore.")
&& need_double_ore(&mut rng))
|| (item.item_definition_id().contains("mineral.gem.")
&& need_double_gem(&mut rng));
let double_gain = item.item_definition_id().raw().map_or(false, |id| {
(id.contains("mineral.ore.") && need_double_ore(&mut rng))
|| (id.contains("mineral.gem.") && need_double_gem(&mut rng))
});
if double_gain {
// Ignore non-stackable errors

View File

@ -678,13 +678,17 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
slots,
} => {
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) {
component_recipes
.get(&ComponentKey {
toolkind,
material: String::from(material_item_id),
modifier: modifier.and_then(item_id).map(String::from),
material: material_item_id,
modifier: modifier.and_then(item_id),
})
.filter(|r| {
if let Some(needed_sprite) = r.craft_sprite {

View File

@ -3,7 +3,7 @@ use common::{
comp::{
agent::{Agent, AgentEvent},
inventory::{
item::{tool::AbilityMap, MaterialStatManifest},
item::{tool::AbilityMap, ItemDefinitionIdOwned, MaterialStatManifest},
Inventory,
},
},
@ -239,7 +239,7 @@ fn commit_trade(ecs: &specs::World, trade: &PendingTrade) -> TradeResult {
// Hashmap to compute merged stackable stacks, including overflow checks
// 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 (slot, quantity) in trade.offers[who].iter() {
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,
trade_quantities,
} = stackable_items
.entry(item.item_definition_id().to_string())
.entry(item.item_definition_id().to_owned())
.or_insert_with(|| TradeQuantities::new(item.max_amount()));
trade_quantities[who].full_stacks += 1;
@ -295,7 +295,7 @@ fn commit_trade(ecs: &specs::World, trade: &PendingTrade) -> TradeResult {
max_stack_size: _,
trade_quantities,
} = stackable_items
.entry(item.item_definition_id().to_string())
.entry(item.item_definition_id().to_owned())
.or_insert_with(|| TradeQuantities::new(item.max_amount()));
trade_quantities[who].quantity_sold += *quantity as u128;
@ -338,7 +338,7 @@ fn commit_trade(ecs: &specs::World, trade: &PendingTrade) -> TradeResult {
.slots()
.flatten()
.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)
} else {
None

View File

@ -11,7 +11,7 @@ use common::{
character::CharacterId,
comp::{
inventory::{
item::{tool::AbilityMap, Item as VelorenItem, MaterialStatManifest},
item::{tool::AbilityMap, Item as VelorenItem, ItemDefinitionId, MaterialStatManifest},
loadout::{Loadout, LoadoutError},
loadout_builder::LoadoutBuilder,
slot::InvSlotId,
@ -165,9 +165,14 @@ pub fn convert_items_to_database_items(
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 {
model: Item {
item_definition_id: item.item_definition_id().to_owned(),
item_definition_id: persistence_item_id,
position,
parent_container_item_id,
item_id,

View File

@ -977,8 +977,9 @@ impl<'a> Widget for Crafting<'a> {
// }) },
RecipeKind::Component(_) => |item| {
item.map_or(false, |item| {
item.item_definition_id()
.starts_with("common.items.crafting_ing.animal_misc")
item.item_definition_id().raw().map_or(false, |id| {
id.starts_with("common.items.crafting_ing.animal_misc")
})
})
},
RecipeKind::Simple => |_| unreachable!(),
@ -1099,7 +1100,7 @@ impl<'a> Widget for Crafting<'a> {
if let Some(material) = primary_slot
.invslot
.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 {
toolkind,
@ -1107,7 +1108,9 @@ impl<'a> Widget for Crafting<'a> {
modifier: secondary_slot
.invslot
.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(
|component_recipe| {
@ -1405,14 +1408,14 @@ impl<'a> Widget for Crafting<'a> {
RecipeKind::Component(toolkind) => {
if let Some(material) = modular_primary_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 {
toolkind,
material,
modifier: modular_secondary_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) =
self.client.component_recipe_book().get(&component_key)
@ -1491,42 +1494,54 @@ impl<'a> Widget for Crafting<'a> {
for (i, (recipe_input, amount)) in ingredients.enumerate() {
let item_def = match recipe_input {
RecipeInput::Item(item_def) => Arc::clone(item_def),
RecipeInput::Tag(tag) | RecipeInput::TagSameItem(tag) => {
Arc::<ItemDef>::load_expect_cloned(
self.inventory
.slots()
.find_map(|slot| {
slot.as_ref().and_then(|item| {
if item.matches_recipe_input(recipe_input, amount) {
Some(item.item_definition_id())
} else {
None
}
})
})
.or_else(|| tag.exemplar_identifier())
.unwrap_or("common.items.weapons.empty.empty"),
)
},
RecipeInput::ListSameItem(item_defs) => Arc::<ItemDef>::load_expect_cloned(
self.inventory
.slots()
.find_map(|slot| {
slot.as_ref().and_then(|item| {
if item.matches_recipe_input(recipe_input, amount) {
Some(item.item_definition_id())
} else {
None
}
})
RecipeInput::Tag(tag) | RecipeInput::TagSameItem(tag) => self
.inventory
.slots()
.find_map(|slot| {
slot.as_ref().and_then(|item| {
if item.matches_recipe_input(recipe_input, amount) {
item.item_definition_id()
.raw()
.map(Arc::<ItemDef>::load_expect_cloned)
} else {
None
}
})
.unwrap_or_else(|| {
item_defs
.first()
.map(|i| i.item_definition_id())
.unwrap_or_else(|| "common.items.weapons.empty.empty")
}),
),
})
.unwrap_or_else(|| {
Arc::<ItemDef>::load_expect_cloned(
tag.exemplar_identifier()
.unwrap_or("common.items.weapons.empty.empty"),
)
}),
RecipeInput::ListSameItem(item_defs) => self
.inventory
.slots()
.find_map(|slot| {
slot.as_ref().and_then(|item| {
if item.matches_recipe_input(recipe_input, amount) {
item.item_definition_id()
.raw()
.map(Arc::<ItemDef>::load_expect_cloned)
} else {
None
}
})
})
.unwrap_or_else(|| {
item_defs
.first()
.and_then(|i| {
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

View File

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

View File

@ -15,7 +15,7 @@ use common::{
item::{
armor::{Armor, ArmorKind},
item_key::ItemKey,
modular, Item, ItemKind,
modular, Item, ItemDefinitionId, ItemKind,
},
CharacterState,
},
@ -221,12 +221,11 @@ impl CharacterCacheKey {
})
},
tool: if are_tools_visible {
let tool_key_from_item = |item: &Item| {
if item.is_modular() {
let tool_key_from_item = |item: &Item| match item.item_definition_id() {
ItemDefinitionId::Simple(id) => ToolKey::Tool(String::from(id)),
ItemDefinitionId::Modular { .. } => {
ToolKey::Modular(modular::weapon_to_key(item))
} else {
ToolKey::Tool(item.item_definition_id().to_owned())
}
},
};
Some(CharacterToolKey {
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 ordered_float::OrderedFloat;
use specs::{Join, WorldExt};
use tracing::{error, info, warn};
use tracing::{error, info, trace, warn};
use vek::*;
use client::{self, Client};
@ -18,7 +18,7 @@ use common::{
comp::{
inventory::slot::{EquipSlot, Slot},
invite::InviteKind,
item::{tool::ToolKind, ItemDef, ItemDesc},
item::{tool::ToolKind, ItemDef, ItemDefinitionId, ItemDesc},
ChatMsg, ChatType, InputKind, InventoryUpdateEvent, Pos, Stats, UtteranceKind, Vel,
},
consts::MAX_MOUNT_RANGE,
@ -250,8 +250,8 @@ impl SessionState {
self.hud.add_failed_entity_pickup(entity);
}
},
InventoryUpdateEvent::Collected(item) => {
match Arc::<ItemDef>::load_cloned(item.item_definition_id()) {
InventoryUpdateEvent::Collected(item) => match item.item_definition_id() {
ItemDefinitionId::Simple(id) => match Arc::<ItemDef>::load_cloned(id) {
Result::Ok(item_def) => {
self.hud.new_loot_message(LootMessage {
item: item_def,
@ -261,11 +261,14 @@ impl SessionState {
Result::Err(e) => {
warn!(
?e,
"Item not present on client: {}",
"Item not present on client: {:?}",
item.item_definition_id()
);
},
}
},
ItemDefinitionId::Modular { .. } => {
trace!("Modular items not currently supported for loot messages.")
},
},
_ => {},
};
@ -1445,7 +1448,9 @@ impl PlayState for SessionState {
.inventories()
.get(client.entity())
.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) {
let key = recipe::ComponentKey {

View File

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