mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Refactored crafting to use ItemDef instead of Item
This commit is contained in:
parent
98c8240879
commit
11fc74642e
@ -119,13 +119,17 @@ pub struct Item {
|
|||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct ItemDef {
|
pub struct ItemDef {
|
||||||
#[serde(skip)]
|
#[serde(default)]
|
||||||
item_definition_id: String,
|
item_definition_id: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub description: String,
|
pub description: String,
|
||||||
pub kind: ItemKind,
|
pub kind: ItemKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialEq for ItemDef {
|
||||||
|
fn eq(&self, other: &Self) -> bool { self.item_definition_id == other.item_definition_id }
|
||||||
|
}
|
||||||
|
|
||||||
impl ItemDef {
|
impl ItemDef {
|
||||||
pub fn is_stackable(&self) -> bool {
|
pub fn is_stackable(&self) -> bool {
|
||||||
matches!(self.kind, ItemKind::Consumable { .. }
|
matches!(self.kind, ItemKind::Consumable { .. }
|
||||||
@ -264,6 +268,10 @@ impl Item {
|
|||||||
|
|
||||||
pub fn item_definition_id(&self) -> &str { &self.item_def.item_definition_id }
|
pub fn item_definition_id(&self) -> &str { &self.item_def.item_definition_id }
|
||||||
|
|
||||||
|
pub fn is_same_item_def(&self, item_def: &ItemDef) -> bool {
|
||||||
|
self.item_def.item_definition_id == item_def.item_definition_id
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_stackable(&self) -> bool { self.item_def.is_stackable() }
|
pub fn is_stackable(&self) -> bool { self.item_def.is_stackable() }
|
||||||
|
|
||||||
pub fn name(&self) -> &str { &self.item_def.name }
|
pub fn name(&self) -> &str { &self.item_def.name }
|
||||||
@ -314,24 +322,30 @@ impl Item {
|
|||||||
_ => return None,
|
_ => return None,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Determines whether two items are superficially equivalent to one another
|
/// Provides common methods providing details about an item definition
|
||||||
/// (i.e: one may be substituted for the other in crafting recipes or
|
/// for either an `Item` containing the definition, or the actual `ItemDef`
|
||||||
/// item possession checks).
|
pub trait ItemDesc {
|
||||||
pub fn superficially_eq(&self, other: &Self) -> bool {
|
fn description(&self) -> &str;
|
||||||
match (&self.kind(), &other.kind()) {
|
fn name(&self) -> &str;
|
||||||
(ItemKind::Tool(a), ItemKind::Tool(b)) => a.superficially_eq(b),
|
fn kind(&self) -> &ItemKind;
|
||||||
// TODO: Differentiate between lantern colors?
|
}
|
||||||
(ItemKind::Lantern(_), ItemKind::Lantern(_)) => true,
|
|
||||||
(ItemKind::Glider(_), ItemKind::Glider(_)) => true,
|
impl ItemDesc for Item {
|
||||||
(ItemKind::Armor(a), ItemKind::Armor(b)) => a.superficially_eq(b),
|
fn description(&self) -> &str { &self.item_def.description }
|
||||||
(ItemKind::Consumable { kind: a, .. }, ItemKind::Consumable { kind: b, .. }) => a == b,
|
|
||||||
(ItemKind::Throwable { kind: a, .. }, ItemKind::Throwable { kind: b, .. }) => a == b,
|
fn name(&self) -> &str { &self.item_def.name }
|
||||||
(ItemKind::Utility { kind: a, .. }, ItemKind::Utility { kind: b, .. }) => a == b,
|
|
||||||
(ItemKind::Ingredient { kind: a, .. }, ItemKind::Ingredient { kind: b, .. }) => a == b,
|
fn kind(&self) -> &ItemKind { &self.item_def.kind }
|
||||||
_ => false,
|
}
|
||||||
}
|
|
||||||
}
|
impl ItemDesc for ItemDef {
|
||||||
|
fn description(&self) -> &str { &self.description }
|
||||||
|
|
||||||
|
fn name(&self) -> &str { &self.name }
|
||||||
|
|
||||||
|
fn kind(&self) -> &ItemKind { &self.kind }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for Item {
|
impl Component for Item {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
pub mod item;
|
pub mod item;
|
||||||
pub mod slot;
|
pub mod slot;
|
||||||
|
|
||||||
use crate::recipe::Recipe;
|
use crate::{comp::inventory::item::ItemDef, recipe::Recipe};
|
||||||
use core::ops::Not;
|
use core::ops::Not;
|
||||||
use item::Item;
|
use item::Item;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -211,11 +211,11 @@ impl Inventory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Determine how many of a particular item there is in the inventory.
|
/// Determine how many of a particular item there is in the inventory.
|
||||||
pub fn item_count(&self, item: &Item) -> u64 {
|
pub fn item_count(&self, item_def: &ItemDef) -> u64 {
|
||||||
self.slots()
|
self.slots()
|
||||||
.iter()
|
.iter()
|
||||||
.flatten()
|
.flatten()
|
||||||
.filter(|it| it.superficially_eq(item))
|
.filter(|it| it.is_same_item_def(item_def))
|
||||||
.map(|it| u64::from(it.amount()))
|
.map(|it| u64::from(it.amount()))
|
||||||
.sum()
|
.sum()
|
||||||
}
|
}
|
||||||
@ -228,15 +228,15 @@ impl Inventory {
|
|||||||
pub fn contains_ingredients<'a>(
|
pub fn contains_ingredients<'a>(
|
||||||
&self,
|
&self,
|
||||||
recipe: &'a Recipe,
|
recipe: &'a Recipe,
|
||||||
) -> Result<Vec<u32>, Vec<(&'a Item, u32)>> {
|
) -> Result<Vec<u32>, Vec<(&'a ItemDef, u32)>> {
|
||||||
let mut slot_claims = vec![0; self.slots.len()];
|
let mut slot_claims = vec![0; self.slots.len()];
|
||||||
let mut missing = Vec::new();
|
let mut missing = Vec::<(&ItemDef, u32)>::new();
|
||||||
|
|
||||||
for (input, mut needed) in recipe.inputs() {
|
for (input, mut needed) in recipe.inputs() {
|
||||||
let mut contains_any = false;
|
let mut contains_any = false;
|
||||||
|
|
||||||
for (i, slot) in self.slots().iter().enumerate() {
|
for (i, slot) in self.slots().iter().enumerate() {
|
||||||
if let Some(item) = slot.as_ref().filter(|item| item.superficially_eq(input)) {
|
if let Some(item) = slot.as_ref().filter(|item| item.is_same_item_def(&*input)) {
|
||||||
let can_claim = (item.amount() - slot_claims[i]).min(needed);
|
let can_claim = (item.amount() - slot_claims[i]).min(needed);
|
||||||
slot_claims[i] += can_claim;
|
slot_claims[i] += can_claim;
|
||||||
needed -= can_claim;
|
needed -= can_claim;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
assets::{self, Asset},
|
assets::{self, Asset},
|
||||||
comp::{Inventory, Item},
|
comp::{item::ItemDef, Inventory, Item},
|
||||||
};
|
};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -8,14 +8,17 @@ use std::{fs::File, io::BufReader, sync::Arc};
|
|||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct Recipe {
|
pub struct Recipe {
|
||||||
pub output: (Item, u32),
|
pub output: (Arc<ItemDef>, u32),
|
||||||
pub inputs: Vec<(Item, u32)>,
|
pub inputs: Vec<(Arc<ItemDef>, u32)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
impl Recipe {
|
impl Recipe {
|
||||||
/// Perform a recipe, returning a list of missing items on failure
|
/// Perform a recipe, returning a list of missing items on failure
|
||||||
pub fn perform(&self, inv: &mut Inventory) -> Result<Option<(Item, u32)>, Vec<(&Item, u32)>> {
|
pub fn perform(
|
||||||
|
&self,
|
||||||
|
inv: &mut Inventory,
|
||||||
|
) -> Result<Option<(Item, u32)>, Vec<(&ItemDef, u32)>> {
|
||||||
// Get ingredient cells from inventory,
|
// Get ingredient cells from inventory,
|
||||||
inv.contains_ingredients(self)?
|
inv.contains_ingredients(self)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -27,7 +30,8 @@ impl Recipe {
|
|||||||
});
|
});
|
||||||
|
|
||||||
for i in 0..self.output.1 {
|
for i in 0..self.output.1 {
|
||||||
if let Some(item) = inv.push(self.output.0.duplicate()) {
|
let crafted_item = Item::new(Arc::clone(&self.output.0));
|
||||||
|
if let Some(item) = inv.push(crafted_item) {
|
||||||
return Ok(Some((item, self.output.1 - i)));
|
return Ok(Some((item, self.output.1 - i)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -35,8 +39,10 @@ impl Recipe {
|
|||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inputs(&self) -> impl ExactSizeIterator<Item = (&Item, u32)> {
|
pub fn inputs(&self) -> impl ExactSizeIterator<Item = (&Arc<ItemDef>, u32)> {
|
||||||
self.inputs.iter().map(|(item, amount)| (item, *amount))
|
self.inputs
|
||||||
|
.iter()
|
||||||
|
.map(|(item_def, amount)| (item_def, *amount))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,11 +81,11 @@ impl Asset for RecipeBook {
|
|||||||
.map::<Result<(String, Recipe), assets::Error>, _>(
|
.map::<Result<(String, Recipe), assets::Error>, _>(
|
||||||
|(name, ((output, amount), inputs))| {
|
|(name, ((output, amount), inputs))| {
|
||||||
Ok((name, Recipe {
|
Ok((name, Recipe {
|
||||||
output: (Item::new_from_asset(&output)?, amount),
|
output: (ItemDef::load(&output)?, amount),
|
||||||
inputs: inputs
|
inputs: inputs
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map::<Result<(Item, u32), assets::Error>, _>(
|
.map::<Result<(Arc<ItemDef>, u32), assets::Error>, _>(
|
||||||
|(name, amount)| Ok((Item::new_from_asset(&name)?, amount)),
|
|(name, amount)| Ok((ItemDef::load(&name)?, amount)),
|
||||||
)
|
)
|
||||||
.collect::<Result<_, _>>()?,
|
.collect::<Result<_, _>>()?,
|
||||||
}))
|
}))
|
||||||
|
@ -8,7 +8,7 @@ use crate::{
|
|||||||
ui::{fonts::ConrodVoxygenFonts, ImageFrame, Tooltip, TooltipManager, Tooltipable},
|
ui::{fonts::ConrodVoxygenFonts, ImageFrame, Tooltip, TooltipManager, Tooltipable},
|
||||||
};
|
};
|
||||||
use client::{self, Client};
|
use client::{self, Client};
|
||||||
use common::comp::Inventory;
|
use common::comp::{item::ItemDesc, Inventory};
|
||||||
use conrod_core::{
|
use conrod_core::{
|
||||||
color,
|
color,
|
||||||
widget::{self, Button, Image, Rectangle, Scrollbar, Text},
|
widget::{self, Button, Image, Rectangle, Scrollbar, Text},
|
||||||
@ -247,10 +247,10 @@ impl<'a> Widget for Crafting<'a> {
|
|||||||
{
|
{
|
||||||
let output_text = format!("x{}", &recipe.output.1.to_string());
|
let output_text = format!("x{}", &recipe.output.1.to_string());
|
||||||
// Output Image
|
// Output Image
|
||||||
let (title, desc) = super::util::item_text(&recipe.output.0);
|
let (title, desc) = super::util::item_text(&*recipe.output.0);
|
||||||
Button::image(
|
Button::image(
|
||||||
self.item_imgs
|
self.item_imgs
|
||||||
.img_id_or_not_found_img((&recipe.output.0).into()),
|
.img_id_or_not_found_img((&*recipe.output.0.kind()).into()),
|
||||||
)
|
)
|
||||||
.w_h(55.0, 55.0)
|
.w_h(55.0, 55.0)
|
||||||
.middle_of(state.ids.output_img_frame)
|
.middle_of(state.ids.output_img_frame)
|
||||||
@ -362,9 +362,9 @@ impl<'a> Widget for Crafting<'a> {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
// Widget generation for every ingredient
|
// Widget generation for every ingredient
|
||||||
for (i, (item, amount)) in recipe.inputs.iter().enumerate() {
|
for (i, (item_def, amount)) in recipe.inputs.iter().enumerate() {
|
||||||
// Grey color for images and text if their amount is too low to craft the item
|
// Grey color for images and text if their amount is too low to craft the item
|
||||||
let item_count_in_inventory = self.inventory.item_count(item);
|
let item_count_in_inventory = self.inventory.item_count(item_def);
|
||||||
let col = if item_count_in_inventory >= u64::from(*amount) {
|
let col = if item_count_in_inventory >= u64::from(*amount) {
|
||||||
TEXT_COLOR
|
TEXT_COLOR
|
||||||
} else {
|
} else {
|
||||||
@ -393,8 +393,8 @@ impl<'a> Widget for Crafting<'a> {
|
|||||||
};
|
};
|
||||||
frame.set(state.ids.ingredient_frame[i], ui);
|
frame.set(state.ids.ingredient_frame[i], ui);
|
||||||
//Item Image
|
//Item Image
|
||||||
let (title, desc) = super::util::item_text(&item);
|
let (title, desc) = super::util::item_text(&**item_def);
|
||||||
Button::image(self.item_imgs.img_id_or_not_found_img(item.into()))
|
Button::image(self.item_imgs.img_id_or_not_found_img((&*item_def.kind()).into()))
|
||||||
.w_h(22.0, 22.0)
|
.w_h(22.0, 22.0)
|
||||||
.middle_of(state.ids.ingredient_frame[i])
|
.middle_of(state.ids.ingredient_frame[i])
|
||||||
//.image_color(col)
|
//.image_color(col)
|
||||||
@ -414,7 +414,7 @@ impl<'a> Widget for Crafting<'a> {
|
|||||||
.font_size(self.fonts.cyri.scale(14))
|
.font_size(self.fonts.cyri.scale(14))
|
||||||
.color(col)
|
.color(col)
|
||||||
.set(state.ids.req_text[i], ui);
|
.set(state.ids.req_text[i], ui);
|
||||||
Text::new(&item.name())
|
Text::new(&item_def.name())
|
||||||
.right_from(state.ids.ingredient_frame[i], 10.0)
|
.right_from(state.ids.ingredient_frame[i], 10.0)
|
||||||
.font_id(self.fonts.cyri.conrod_id)
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
.font_size(self.fonts.cyri.scale(14))
|
.font_size(self.fonts.cyri.scale(14))
|
||||||
@ -425,7 +425,7 @@ impl<'a> Widget for Crafting<'a> {
|
|||||||
let input = format!(
|
let input = format!(
|
||||||
"{}x {} ({})",
|
"{}x {} ({})",
|
||||||
amount,
|
amount,
|
||||||
&item.name(),
|
&item_def.name(),
|
||||||
if item_count_in_inventory > 99 {
|
if item_count_in_inventory > 99 {
|
||||||
over9k
|
over9k
|
||||||
} else {
|
} else {
|
||||||
|
@ -4,7 +4,7 @@ use common::{
|
|||||||
comp::item::{
|
comp::item::{
|
||||||
armor::{Armor, ArmorKind},
|
armor::{Armor, ArmorKind},
|
||||||
tool::{Tool, ToolKind},
|
tool::{Tool, ToolKind},
|
||||||
Glider, Item, ItemKind, Lantern, Throwable, Utility,
|
Glider, ItemKind, Lantern, Throwable, Utility,
|
||||||
},
|
},
|
||||||
figure::Segment,
|
figure::Segment,
|
||||||
};
|
};
|
||||||
@ -29,9 +29,10 @@ pub enum ItemKey {
|
|||||||
Ingredient(String),
|
Ingredient(String),
|
||||||
Empty,
|
Empty,
|
||||||
}
|
}
|
||||||
impl From<&Item> for ItemKey {
|
|
||||||
fn from(item: &Item) -> Self {
|
impl From<&ItemKind> for ItemKey {
|
||||||
match &item.kind() {
|
fn from(item_kind: &ItemKind) -> Self {
|
||||||
|
match item_kind {
|
||||||
ItemKind::Tool(Tool { kind, .. }) => ItemKey::Tool(kind.clone()),
|
ItemKind::Tool(Tool { kind, .. }) => ItemKey::Tool(kind.clone()),
|
||||||
ItemKind::Lantern(Lantern { kind, .. }) => ItemKey::Lantern(kind.clone()),
|
ItemKind::Lantern(Lantern { kind, .. }) => ItemKey::Lantern(kind.clone()),
|
||||||
ItemKind::Glider(Glider { kind, .. }) => ItemKey::Glider(kind.clone()),
|
ItemKind::Glider(Glider { kind, .. }) => ItemKey::Glider(kind.clone()),
|
||||||
|
@ -32,7 +32,7 @@ impl SlotKey<Inventory, ItemImgs> for InventorySlot {
|
|||||||
type ImageKey = ItemKey;
|
type ImageKey = ItemKey;
|
||||||
|
|
||||||
fn image_key(&self, source: &Inventory) -> Option<(Self::ImageKey, Option<Color>)> {
|
fn image_key(&self, source: &Inventory) -> Option<(Self::ImageKey, Option<Color>)> {
|
||||||
source.get(self.0).map(|i| (i.into(), None))
|
source.get(self.0).map(|i| (i.kind().into(), None))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn amount(&self, source: &Inventory) -> Option<u32> {
|
fn amount(&self, source: &Inventory) -> Option<u32> {
|
||||||
@ -69,7 +69,7 @@ impl SlotKey<Loadout, ItemImgs> for EquipSlot {
|
|||||||
EquipSlot::Glider => source.glider.as_ref(),
|
EquipSlot::Glider => source.glider.as_ref(),
|
||||||
};
|
};
|
||||||
|
|
||||||
item.map(|i| (i.into(), None))
|
item.map(|i| (i.kind().into(), None))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn amount(&self, _: &Loadout) -> Option<u32> { None }
|
fn amount(&self, _: &Loadout) -> Option<u32> { None }
|
||||||
@ -100,7 +100,7 @@ impl<'a> SlotKey<HotbarSource<'a>, HotbarImageSource<'a>> for HotbarSlot {
|
|||||||
hotbar.get(*self).and_then(|contents| match contents {
|
hotbar.get(*self).and_then(|contents| match contents {
|
||||||
hotbar::SlotContents::Inventory(idx) => inventory
|
hotbar::SlotContents::Inventory(idx) => inventory
|
||||||
.get(idx)
|
.get(idx)
|
||||||
.map(|item| HotbarImage::Item(item.into()))
|
.map(|item| HotbarImage::Item(item.kind().into()))
|
||||||
.map(|i| (i, None)),
|
.map(|i| (i, None)),
|
||||||
hotbar::SlotContents::Ability3 => loadout
|
hotbar::SlotContents::Ability3 => loadout
|
||||||
.active_item
|
.active_item
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
use common::comp::item::{
|
use common::comp::item::{
|
||||||
armor::{Armor, ArmorKind, Protection},
|
armor::{Armor, ArmorKind, Protection},
|
||||||
tool::{Tool, ToolKind},
|
tool::{Tool, ToolKind},
|
||||||
Item, ItemKind,
|
ItemDesc, ItemKind,
|
||||||
};
|
};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
pub fn loadout_slot_text<'a>(
|
pub fn loadout_slot_text<'a>(
|
||||||
item: Option<&'a Item>,
|
item: Option<&'a impl ItemDesc>,
|
||||||
mut empty: impl FnMut() -> (&'a str, &'a str),
|
mut empty: impl FnMut() -> (&'a str, &'a str),
|
||||||
) -> (&'a str, Cow<'a, str>) {
|
) -> (&'a str, Cow<'a, str>) {
|
||||||
item.map_or_else(
|
item.map_or_else(
|
||||||
@ -18,7 +18,7 @@ pub fn loadout_slot_text<'a>(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn item_text<'a>(item: &'a Item) -> (&'_ str, Cow<'a, str>) {
|
pub fn item_text<'a>(item: &'a impl ItemDesc) -> (&'_ str, Cow<'a, str>) {
|
||||||
let desc: Cow<str> = match item.kind() {
|
let desc: Cow<str> = match item.kind() {
|
||||||
ItemKind::Armor(armor) => Cow::Owned(armor_desc(&armor, item.description())),
|
ItemKind::Armor(armor) => Cow::Owned(armor_desc(&armor, item.description())),
|
||||||
ItemKind::Tool(tool) => Cow::Owned(tool_desc(&tool, item.description())),
|
ItemKind::Tool(tool) => Cow::Owned(tool_desc(&tool, item.description())),
|
||||||
@ -34,6 +34,7 @@ pub fn item_text<'a>(item: &'a Item) -> (&'_ str, Cow<'a, str>) {
|
|||||||
|
|
||||||
(item.name(), desc)
|
(item.name(), desc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Armor Description
|
// Armor Description
|
||||||
fn armor_desc(armor: &Armor, desc: &str) -> String {
|
fn armor_desc(armor: &Armor, desc: &str) -> String {
|
||||||
// TODO: localization
|
// TODO: localization
|
||||||
|
Loading…
Reference in New Issue
Block a user