Items with components can now have their name modified by the components. Also addressed more review. NO ASSETS

This commit is contained in:
Sam 2021-11-23 12:45:19 -05:00
parent 524845b661
commit 81c83c5e83
9 changed files with 85 additions and 61 deletions

View File

@ -50,6 +50,7 @@ impl<T: ItemDesc> From<&T> for ItemKey {
}
},
ModularComponent::ToolSecondaryComponent { .. } => {
// TODO: Maybe use a different ItemKey?
ItemKey::Tool(item_definition_id.to_owned())
},
}

View File

@ -219,7 +219,6 @@ impl TagExampleInfo for Material {
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub enum ItemTag {
Leather,
Material(Material),
MaterialKind(MaterialKind),
Cultist,
@ -237,7 +236,6 @@ impl TagExampleInfo for ItemTag {
match self {
ItemTag::Material(material) => material.name(),
ItemTag::MaterialKind(material_kind) => material_kind.into(),
ItemTag::Leather => "leather",
ItemTag::Cultist => "cultist",
ItemTag::Potion => "potion",
ItemTag::Food => "food",
@ -254,7 +252,6 @@ impl TagExampleInfo for ItemTag {
match self {
ItemTag::Material(_) => "common.items.tag_examples.placeholder",
ItemTag::MaterialKind(_) => "common.items.tag_examples.placeholder",
ItemTag::Leather => "common.items.tag_examples.leather",
ItemTag::Cultist => "common.items.tag_examples.cultist",
ItemTag::Potion => "common.items.tag_examples.placeholder",
ItemTag::Food => "common.items.tag_examples.placeholder",
@ -715,8 +712,8 @@ impl Item {
ItemBase::Raw(Arc::<ItemDef>::load_cloned(asset)?)
};
// TODO: Get msm and ability_map less hackily
let msm = MaterialStatManifest::default();
let ability_map = AbilityMap::default();
let msm = MaterialStatManifest::load().read();
let ability_map = AbilityMap::load().read();
Ok(Item::new_from_item_base(
inner_item,
Vec::new(),
@ -862,15 +859,15 @@ impl Item {
}
}
pub fn matches_recipe_input(&self, recipe_input: &RecipeInput) -> bool {
pub fn matches_recipe_input(&self, recipe_input: &RecipeInput, amount: u32) -> bool {
match recipe_input {
RecipeInput::Item(item_def) => self.is_same_item_def(item_def),
RecipeInput::Tag(tag) => self.tags().contains(tag),
RecipeInput::TagSameItem(tag, amount) => {
self.tags().contains(tag) && u32::from(self.amount) >= *amount
RecipeInput::TagSameItem(tag) => {
self.tags().contains(tag) && u32::from(self.amount) >= amount
},
RecipeInput::ListSameItem(item_defs, amount) => item_defs.iter().any(|item_def| {
self.is_same_item_def(item_def) && u32::from(self.amount) >= *amount
RecipeInput::ListSameItem(item_defs) => item_defs.iter().any(|item_def| {
self.is_same_item_def(item_def) && u32::from(self.amount) >= amount
}),
}
}
@ -893,7 +890,13 @@ impl Item {
pub fn name(&self) -> Cow<str> {
match &self.item_base {
ItemBase::Raw(item_def) => Cow::Borrowed(&item_def.name),
ItemBase::Raw(item_def) => {
if self.components.is_empty() {
Cow::Borrowed(&item_def.name)
} else {
modular::modify_name(&item_def.name, self)
}
},
ItemBase::Modular(mod_base) => mod_base.generate_name(self.components()),
}
}
@ -911,7 +914,7 @@ impl Item {
ItemBase::Raw(item_def) => Cow::Borrowed(&item_def.kind),
ItemBase::Modular(mod_base) => {
// TODO: Try to move further upward
let msm = MaterialStatManifest::default();
let msm = MaterialStatManifest::load().read();
mod_base.kind(self.components(), &msm)
},

View File

@ -1,5 +1,5 @@
use super::{
tool::{self, AbilitySpec, Hands, MaterialStatManifest},
tool::{self, AbilityMap, AbilitySpec, Hands, MaterialStatManifest},
Item, ItemBase, ItemDef, ItemDesc, ItemKind, Material, Quality, ToolKind,
};
use crate::{assets::AssetExt, recipe};
@ -287,8 +287,8 @@ pub fn random_weapon(
) -> Result<Item, ModularWeaponCreationError> {
if let Some(material_id) = material.asset_identifier() {
// Loads default ability map and material stat manifest for later use
let ability_map = Default::default();
let msm = Default::default();
let ability_map = AbilityMap::load().read();
let msm = MaterialStatManifest::load().read();
let mut rng = thread_rng();
@ -351,6 +351,25 @@ pub fn random_weapon(
}
}
pub fn modify_name<'a>(item_name: &'a str, item: &'a Item) -> Cow<'a, str> {
if let ItemKind::ModularComponent(_) = &*item.kind() {
if let Some(material_name) = item
.components()
.iter()
.find_map(|comp| match &*comp.kind() {
ItemKind::Ingredient { descriptor, .. } => Some(descriptor.to_owned()),
_ => None,
})
{
Cow::Owned(format!("{} {}", material_name, item_name))
} else {
Cow::Borrowed(item_name)
}
} else {
Cow::Borrowed(item_name)
}
}
/// This is used as a key to uniquely identify the modular weapon in asset
/// manifests in voxygen (Main component, material, hands)
pub type ModularWeaponKey = (String, String, Hands);

View File

@ -2,7 +2,7 @@
// version in voxygen\src\meta.rs in order to reset save files to being empty
use crate::{
assets::{self, Asset, AssetExt},
assets::{self, Asset, AssetExt, AssetHandle},
comp::{skills::Skill, CharacterAbility},
};
use hashbrown::HashMap;
@ -217,6 +217,10 @@ impl DivAssign<usize> for Stats {
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct MaterialStatManifest(pub HashMap<String, Stats>);
impl MaterialStatManifest {
pub fn load() -> AssetHandle<Self> { Self::load_expect("common.material_stats_manifest") }
}
// This could be a Compound that also loads the keys, but the RecipeBook
// Compound impl already does that, so checking for existence here is redundant.
impl Asset for MaterialStatManifest {
@ -225,13 +229,6 @@ impl Asset for MaterialStatManifest {
const EXTENSION: &'static str = "ron";
}
impl Default for MaterialStatManifest {
fn default() -> MaterialStatManifest {
// TODO: Don't do this, loading a default should have no ability to panic
MaterialStatManifest::load_expect_cloned("common.material_stats_manifest")
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Tool {
pub kind: ToolKind,
@ -348,16 +345,9 @@ pub struct AbilityItem {
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AbilityMap<T = AbilityItem>(HashMap<AbilitySpec, AbilitySet<T>>);
impl Default for AbilityMap {
fn default() -> Self {
// TODO: Revert to old default
if let Ok(map) = Self::load_cloned("common.abilities.ability_set_manifest") {
map
} else {
let mut map = HashMap::new();
map.insert(AbilitySpec::Tool(ToolKind::Empty), AbilitySet::default());
AbilityMap(map)
}
impl AbilityMap {
pub fn load() -> AssetHandle<Self> {
Self::load_expect("common.abilities.ability_set_manifest")
}
}

View File

@ -22,13 +22,19 @@ pub enum RecipeInput {
/// Similar to RecipeInput::Tag(_), but all items must be the same.
/// Specifically this means that a mix of different items with the tag
/// cannot be used.
TagSameItem(ItemTag, u32),
/// TODO: Currently requires that all items must be in the same slot.
/// Eventually should be reworked so that items can be spread over multiple
/// slots.
TagSameItem(ItemTag),
/// List is similar to tag, but has items defined in centralized file
/// Similar to RecipeInput::TagSameItem(_), all items must be the same, they
/// cannot be a mix of different items defined in the list.
// Intent of using List over Tag is to make it harder for tag to be innocuously added to an
// item breaking a recipe
ListSameItem(Vec<Arc<ItemDef>>, u32),
/// TODO: Currently requires that all items must be in the same slot.
/// Eventually should be reworked so that items can be spread over multiple
/// slots.
ListSameItem(Vec<Arc<ItemDef>>),
}
#[derive(Clone, Debug, Serialize, Deserialize)]
@ -62,7 +68,8 @@ impl Recipe {
self.inputs
.iter()
.enumerate()
.for_each(|(i, (input, mut required, mut is_component))| {
.for_each(|(i, (input, amount, mut is_component))| {
let mut required = *amount;
// Check used for recipes that have an input that is not consumed, e.g.
// craftsman hammer
let mut contains_any = false;
@ -75,7 +82,7 @@ impl Recipe {
// 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))
.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
@ -165,14 +172,15 @@ impl Recipe {
// The inputs to a recipe that have missing items, and the amount missing
let mut missing = Vec::<(&RecipeInput, u32)>::new();
for (i, (input, mut needed, _)) in self.inputs().enumerate() {
for (i, (input, amount, _)) in self.inputs().enumerate() {
let mut needed = amount;
let mut contains_any = false;
// Checks through every slot, filtering to only those that contain items that
// can satisfy the input
for (inv_slot_id, slot) in inv.slots_with_id() {
if let Some(item) = slot
.as_ref()
.filter(|item| item.matches_recipe_input(&*input))
.filter(|item| item.matches_recipe_input(&*input, amount))
{
let claim = slot_claims.entry(inv_slot_id).or_insert(0);
slots.push((i as u32, inv_slot_id));
@ -390,14 +398,14 @@ impl assets::Compound for RecipeBook {
let def = match &input {
RawRecipeInput::Item(name) => RecipeInput::Item(Arc::<ItemDef>::load_cloned(name)?),
RawRecipeInput::Tag(tag) => RecipeInput::Tag(*tag),
RawRecipeInput::TagSameItem(tag) => RecipeInput::TagSameItem(*tag, *amount),
RawRecipeInput::TagSameItem(tag) => RecipeInput::TagSameItem(*tag),
RawRecipeInput::ListSameItem(list) => {
let assets = &ItemList::load_expect(list).read().0;
let items = assets
.iter()
.map(|asset| Arc::<ItemDef>::load_expect_cloned(asset))
.collect();
RecipeInput::ListSameItem(items, *amount)
RecipeInput::ListSameItem(items)
},
};
Ok((def, *amount, *is_mod_comp))

View File

@ -43,7 +43,7 @@ pub struct ReadData<'a> {
stats: ReadStorage<'a, Stats>,
skill_sets: ReadStorage<'a, SkillSet>,
active_abilities: ReadStorage<'a, ActiveAbilities>,
msm: Read<'a, MaterialStatManifest>,
msm: ReadExpect<'a, MaterialStatManifest>,
combos: ReadStorage<'a, Combo>,
alignments: ReadStorage<'a, comp::Alignment>,
terrain: ReadExpect<'a, TerrainGrid>,

View File

@ -73,7 +73,7 @@ use common::{
calendar::Calendar,
character::CharacterId,
cmd::ChatCommand,
comp::{self, item::MaterialStatManifest},
comp,
event::{EventBus, ServerEvent},
recipe::default_recipe_book,
resources::{BattleMode, Time, TimeOfDay},
@ -310,7 +310,7 @@ impl Server {
);
state.ecs_mut().insert(ability_map);
let msm = comp::inventory::item::MaterialStatManifest::default();
let msm = comp::inventory::item::MaterialStatManifest::load().cloned();
state.ecs_mut().insert(msm);
state.ecs_mut().insert(CharacterLoader::new(
@ -1087,7 +1087,11 @@ impl Server {
client_timeout: self.settings().client_timeout,
world_map: self.map.clone(),
recipe_book: default_recipe_book().cloned(),
material_stats: MaterialStatManifest::default(),
material_stats: (&*self
.state
.ecs()
.read_resource::<comp::item::tool::MaterialStatManifest>())
.clone(),
ability_map: (&*self
.state
.ecs()

View File

@ -36,8 +36,9 @@ pub struct ItemModelPair {
// shouldn't matter unless someone's hot-reloading material stats on the live
// server
lazy_static! {
pub static ref MATERIAL_STATS_MANIFEST: MaterialStatManifest = MaterialStatManifest::default();
pub static ref ABILITY_MAP: AbilityMap = AbilityMap::default();
pub static ref MATERIAL_STATS_MANIFEST: MaterialStatManifest =
MaterialStatManifest::load().cloned();
pub static ref ABILITY_MAP: AbilityMap = AbilityMap::load().cloned();
}
/// Returns a vector that contains all item rows to upsert; parent is

View File

@ -198,12 +198,10 @@ impl CraftingTab {
},
CraftingTab::Glider => matches!(&*item.kind(), ItemKind::Glider(_)),
CraftingTab::Potion => item.tags().contains(&ItemTag::Potion),
CraftingTab::ProcessedMaterial => item.tags().iter().any(|tag| {
matches!(
tag,
&ItemTag::MaterialKind(_) | &ItemTag::Leather | &ItemTag::BaseMaterial
)
}),
CraftingTab::ProcessedMaterial => item
.tags()
.iter()
.any(|tag| matches!(tag, &ItemTag::MaterialKind(_) | &ItemTag::BaseMaterial)),
CraftingTab::Bag => item.tags().contains(&ItemTag::Bag),
CraftingTab::Tool => item.tags().contains(&ItemTag::CraftingTool),
CraftingTab::Utility => item.tags().contains(&ItemTag::Utility),
@ -469,10 +467,10 @@ impl<'a> Widget for Crafting<'a> {
let input_name = match input {
RecipeInput::Item(def) => def.name(),
RecipeInput::Tag(tag) => Cow::Borrowed(tag.name()),
RecipeInput::TagSameItem(tag, _) => Cow::Borrowed(tag.name()),
RecipeInput::TagSameItem(tag) => Cow::Borrowed(tag.name()),
// Works, but probably will have some...interesting false positives
// Code reviewers probably required to do magic to make not hacky
RecipeInput::ListSameItem(defs, _) => {
RecipeInput::ListSameItem(defs) => {
Cow::Owned(defs.iter().map(|def| def.name()).collect())
},
}
@ -914,13 +912,13 @@ impl<'a> Widget for Crafting<'a> {
for (i, (recipe_input, amount, _)) in recipe.inputs.iter().enumerate() {
let item_def = match recipe_input {
RecipeInput::Item(item_def) => Arc::clone(item_def),
RecipeInput::Tag(tag) | RecipeInput::TagSameItem(tag, _) => {
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) {
if item.matches_recipe_input(recipe_input, *amount) {
Some(item.item_definition_id())
} else {
None
@ -930,12 +928,12 @@ impl<'a> Widget for Crafting<'a> {
.unwrap_or_else(|| tag.exemplar_identifier()),
)
},
RecipeInput::ListSameItem(item_defs, _) => Arc::<ItemDef>::load_expect_cloned(
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) {
if item.matches_recipe_input(recipe_input, *amount) {
Some(item.item_definition_id())
} else {
None
@ -1047,10 +1045,10 @@ impl<'a> Widget for Crafting<'a> {
// Ingredients
let name = match recipe_input {
RecipeInput::Item(_) => item_def.name().to_string(),
RecipeInput::Tag(tag) | RecipeInput::TagSameItem(tag, _) => {
RecipeInput::Tag(tag) | RecipeInput::TagSameItem(tag) => {
format!("Any {} item", tag.name())
},
RecipeInput::ListSameItem(item_defs, _) => {
RecipeInput::ListSameItem(item_defs) => {
format!(
"Any of {}",
item_defs.iter().map(|def| def.name()).collect::<String>()