From 77aeb432477fbd0c24a94040fa2202606ddaeb35 Mon Sep 17 00:00:00 2001 From: Avi Weinstock Date: Tue, 23 Feb 2021 00:00:45 -0500 Subject: [PATCH] Support modular weapon components made from a tagged material using the material as a multiplier. --- .../items/crafting_ing/bloodsteel_ingot.ron | 4 +- .../items/crafting_ing/bronze_ingot.ron | 4 +- .../items/crafting_ing/cobalt_ingot.ron | 4 +- .../items/crafting_ing/copper_ingot.ron | 4 +- .../common/items/crafting_ing/iron_ingot.ron | 4 +- .../modular/damage/sword/metal_blade.ron | 17 ++++++ .../common/items/crafting_ing/steel_ingot.ron | 4 +- .../common/items/crafting_ing/tin_ingot.ron | 4 +- .../common/items/tag_examples/cloth_item.ron | 3 +- .../common/items/tag_examples/metal_ingot.ron | 19 ++++++ assets/common/recipe_book.ron | 7 +++ common/src/comp/inventory/item/mod.rs | 26 ++++++++- common/src/comp/inventory/item/tool.rs | 58 ++++++++++++++++--- voxygen/src/hud/util.rs | 25 ++++++-- 14 files changed, 153 insertions(+), 30 deletions(-) create mode 100644 assets/common/items/crafting_ing/modular/damage/sword/metal_blade.ron create mode 100644 assets/common/items/tag_examples/metal_ingot.ron diff --git a/assets/common/items/crafting_ing/bloodsteel_ingot.ron b/assets/common/items/crafting_ing/bloodsteel_ingot.ron index 7498e625b7..3b210deb0b 100644 --- a/assets/common/items/crafting_ing/bloodsteel_ingot.ron +++ b/assets/common/items/crafting_ing/bloodsteel_ingot.ron @@ -5,5 +5,5 @@ ItemDef( kind: "BloodsteelIngot", ), quality: Common, - tags: [], -) \ No newline at end of file + tags: [MetalIngot(1.75)], +) diff --git a/assets/common/items/crafting_ing/bronze_ingot.ron b/assets/common/items/crafting_ing/bronze_ingot.ron index 065e2e1773..e3bce07b04 100644 --- a/assets/common/items/crafting_ing/bronze_ingot.ron +++ b/assets/common/items/crafting_ing/bronze_ingot.ron @@ -5,5 +5,5 @@ ItemDef( kind: "BronzeIngot", ), quality: Common, - tags: [], -) \ No newline at end of file + tags: [MetalIngot(0.75)], +) diff --git a/assets/common/items/crafting_ing/cobalt_ingot.ron b/assets/common/items/crafting_ing/cobalt_ingot.ron index 7e981db9e4..13d11cfa82 100644 --- a/assets/common/items/crafting_ing/cobalt_ingot.ron +++ b/assets/common/items/crafting_ing/cobalt_ingot.ron @@ -5,5 +5,5 @@ ItemDef( kind: "CobaltIngot", ), quality: Common, - tags: [], -) \ No newline at end of file + tags: [MetalIngot(1.5)], +) diff --git a/assets/common/items/crafting_ing/copper_ingot.ron b/assets/common/items/crafting_ing/copper_ingot.ron index b1509eaa6b..52c825363b 100644 --- a/assets/common/items/crafting_ing/copper_ingot.ron +++ b/assets/common/items/crafting_ing/copper_ingot.ron @@ -5,5 +5,5 @@ ItemDef( kind: "CopperIngot", ), quality: Common, - tags: [], -) \ No newline at end of file + tags: [MetalIngot(0.4)], +) diff --git a/assets/common/items/crafting_ing/iron_ingot.ron b/assets/common/items/crafting_ing/iron_ingot.ron index 1bdc4a3fea..c4f4d517fa 100644 --- a/assets/common/items/crafting_ing/iron_ingot.ron +++ b/assets/common/items/crafting_ing/iron_ingot.ron @@ -5,5 +5,5 @@ ItemDef( kind: "IronIngot", ), quality: Common, - tags: [], -) \ No newline at end of file + tags: [MetalIngot(1.0)], +) diff --git a/assets/common/items/crafting_ing/modular/damage/sword/metal_blade.ron b/assets/common/items/crafting_ing/modular/damage/sword/metal_blade.ron new file mode 100644 index 0000000000..5f28c9e674 --- /dev/null +++ b/assets/common/items/crafting_ing/modular/damage/sword/metal_blade.ron @@ -0,0 +1,17 @@ +ItemDef( + name: "Metal sword blade", + description: "A sword blade made of metal.", + kind: ModularComponent(( + toolkind: Sword, + modkind: Damage, + stats: ( + equip_time_millis: 250, + power: 1.0, + poise_strength: 0.75, + speed: 0.0, + ), + )), + quality: Common, + tags: [ModularComponent((toolkind: Sword, modkind: Damage))], +) + diff --git a/assets/common/items/crafting_ing/steel_ingot.ron b/assets/common/items/crafting_ing/steel_ingot.ron index f6086246ec..5f607b3578 100644 --- a/assets/common/items/crafting_ing/steel_ingot.ron +++ b/assets/common/items/crafting_ing/steel_ingot.ron @@ -5,5 +5,5 @@ ItemDef( kind: "SteelIngot", ), quality: Common, - tags: [], -) \ No newline at end of file + tags: [MetalIngot(1.25)], +) diff --git a/assets/common/items/crafting_ing/tin_ingot.ron b/assets/common/items/crafting_ing/tin_ingot.ron index 6950f38ff3..8995168457 100644 --- a/assets/common/items/crafting_ing/tin_ingot.ron +++ b/assets/common/items/crafting_ing/tin_ingot.ron @@ -5,5 +5,5 @@ ItemDef( kind: "TinIngot", ), quality: Common, - tags: [], -) \ No newline at end of file + tags: [MetalIngot(0.25)], +) diff --git a/assets/common/items/tag_examples/cloth_item.ron b/assets/common/items/tag_examples/cloth_item.ron index dd4e975ad1..04560f9956 100644 --- a/assets/common/items/tag_examples/cloth_item.ron +++ b/assets/common/items/tag_examples/cloth_item.ron @@ -18,6 +18,5 @@ ItemDef( ], ), quality: Common, - tags: [ClothItem], + tags: [], ) - diff --git a/assets/common/items/tag_examples/metal_ingot.ron b/assets/common/items/tag_examples/metal_ingot.ron new file mode 100644 index 0000000000..fe90564b6f --- /dev/null +++ b/assets/common/items/tag_examples/metal_ingot.ron @@ -0,0 +1,19 @@ +ItemDef( + name: "Any metal ingot", + description: "Ingots made from various metals.", + kind: TagExamples( + item_ids: [ + "common.items.crafting_ing.bloodsteel_ingot", + "common.items.crafting_ing.bronze_ingot", + "common.items.crafting_ing.cobalt_ingot", + "common.items.crafting_ing.copper_ingot", + "common.items.crafting_ing.iron_ingot", + "common.items.crafting_ing.steel_ingot", + "common.items.crafting_ing.tin_ingot", + ], + ), + quality: Common, + tags: [], +) + + diff --git a/assets/common/recipe_book.ron b/assets/common/recipe_book.ron index 974602a42a..a0a6989dc4 100644 --- a/assets/common/recipe_book.ron +++ b/assets/common/recipe_book.ron @@ -363,4 +363,11 @@ (Item("common.items.crafting_tools.sewing_set"), 0), ] ), + "metal_blade": ( + ("common.items.crafting_ing.modular.damage.sword.metal_blade", 1), + [ + (Tag(MetalIngot(0.0)), 5), + (Item("common.items.crafting_tools.craftsman_hammer"), 0), + ] + ), } diff --git a/common/src/comp/inventory/item/mod.rs b/common/src/comp/inventory/item/mod.rs index 2a5b6399a8..2ae1a987d1 100644 --- a/common/src/comp/inventory/item/mod.rs +++ b/common/src/comp/inventory/item/mod.rs @@ -87,10 +87,25 @@ pub trait TagExampleInfo { fn exemplar_identifier(&self) -> &'static str; } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] pub enum ItemTag { ClothItem, ModularComponent(ModularComponentTag), + MetalIngot(f32), +} + +// The PartialEq implementation for ItemTag is used for determining whether a +// RecipeInput matches +impl PartialEq for ItemTag { + fn eq(&self, other: &Self) -> bool { + use ItemTag::*; + match (self, other) { + (ClothItem, ClothItem) => true, + (ModularComponent(a), ModularComponent(b)) => a == b, + (MetalIngot(_), MetalIngot(_)) => true, + _ => false, + } + } } impl TagExampleInfo for ItemTag { @@ -98,6 +113,7 @@ impl TagExampleInfo for ItemTag { match self { ItemTag::ClothItem => "cloth item", ItemTag::ModularComponent(kind) => kind.name(), + ItemTag::MetalIngot(_) => "metal ingot", } } @@ -105,6 +121,7 @@ impl TagExampleInfo for ItemTag { match self { ItemTag::ClothItem => "common.items.tag_examples.cloth_item", ItemTag::ModularComponent(tag) => tag.exemplar_identifier(), + ItemTag::MetalIngot(_) => "common.items.tag_examples.metal_ingot", } } } @@ -245,7 +262,7 @@ impl ItemDef { ItemKind::Tool(tool::Tool { stats: tool::StatKind::Modular, .. - }) + }) | ItemKind::ModularComponent(_) ) } @@ -644,6 +661,7 @@ pub trait ItemDesc { fn num_slots(&self) -> u16; fn item_definition_id(&self) -> &str; fn components(&self) -> &[Item]; + fn tags(&self) -> &[ItemTag]; } impl ItemDesc for Item { @@ -660,6 +678,8 @@ impl ItemDesc for Item { fn item_definition_id(&self) -> &str { &self.item_def.item_definition_id } fn components(&self) -> &[Item] { &self.components } + + fn tags(&self) -> &[ItemTag] { &self.item_def.tags } } impl ItemDesc for ItemDef { @@ -676,6 +696,8 @@ impl ItemDesc for ItemDef { fn item_definition_id(&self) -> &str { &self.item_definition_id } fn components(&self) -> &[Item] { &[] } + + fn tags(&self) -> &[ItemTag] { &self.tags } } impl Component for Item { diff --git a/common/src/comp/inventory/item/tool.rs b/common/src/comp/inventory/item/tool.rs index c7eefbb07a..001ee5c6fc 100644 --- a/common/src/comp/inventory/item/tool.rs +++ b/common/src/comp/inventory/item/tool.rs @@ -3,11 +3,18 @@ use crate::{ assets::{self, Asset}, - comp::{item::ItemKind, skills::Skill, CharacterAbility, Item}, + comp::{ + item::{ItemDesc, ItemKind, ItemTag}, + skills::Skill, + CharacterAbility, Item, + }, }; use hashbrown::HashMap; use serde::{Deserialize, Serialize}; -use std::time::Duration; +use std::{ + ops::{AddAssign, MulAssign}, + time::Duration, +}; use tracing::error; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] @@ -71,6 +78,22 @@ impl Stats { } } +impl AddAssign for Stats { + fn add_assign(&mut self, other: Stats) { + self.equip_time_millis += other.equip_time_millis; + self.power += other.power; + self.poise_strength += other.poise_strength; + self.speed += other.speed; + } +} +impl MulAssign for Stats { + fn mul_assign(&mut self, scalar: f32) { + self.power *= scalar; + self.poise_strength *= scalar; + self.speed *= scalar; + } +} + #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] pub enum StatKind { Direct(Stats), @@ -83,14 +106,33 @@ impl StatKind { StatKind::Direct(stats) => *stats, StatKind::Modular => Stats::zeroed(), }; + let mut best_multiplier: Option = None; for item in components.iter() { - if let ItemKind::ModularComponent(mc) = item.kind() { - stats.equip_time_millis += mc.stats.equip_time_millis; - stats.power += mc.stats.power; - stats.poise_strength += mc.stats.poise_strength; - stats.speed += mc.stats.speed; + match item.kind() { + ItemKind::ModularComponent(mc) => { + let inner_stats = StatKind::Direct(mc.stats).resolve_stats(item.components()); + stats += inner_stats; + }, + ItemKind::Ingredient { .. } => { + for tag in item.tags() { + // exhaustive match to ensure that new tags get stats counted + match tag { + ItemTag::ClothItem => {}, + ItemTag::ModularComponent(_) => {}, + ItemTag::MetalIngot(multiplier) => { + best_multiplier = + Some(best_multiplier.unwrap_or(*multiplier).max(*multiplier)); + }, + } + } + }, + // TODO: add stats from enhancement slots, unless those end up as tagged + // ingredients + _ => (), } - // TODO: add stats from enhancement slots + } + if let Some(multiplier) = best_multiplier { + stats *= multiplier; } // if an item has 0.0 speed, that panics due to being infinite duration, so // enforce speed >= 0.1 diff --git a/voxygen/src/hud/util.rs b/voxygen/src/hud/util.rs index 028250bf49..e6c55fd565 100644 --- a/voxygen/src/hud/util.rs +++ b/voxygen/src/hud/util.rs @@ -1,6 +1,6 @@ use common::comp::item::{ armor::{Armor, ArmorKind, Protection}, - tool::{Hands, Tool, ToolKind}, + tool::{Hands, StatKind, Tool, ToolKind}, Item, ItemDesc, ItemKind, ModularComponent, }; use std::{borrow::Cow, fmt::Write}; @@ -24,7 +24,11 @@ pub fn item_text<'a>(item: &'a impl ItemDesc) -> (&'_ str, Cow<'a, str>) { Cow::Owned(armor_desc(armor, item.description(), item.num_slots())) }, ItemKind::Tool(tool) => Cow::Owned(tool_desc(&tool, item.components(), item.description())), - ItemKind::ModularComponent(mc) => Cow::Owned(modular_component_desc(mc)), + ItemKind::ModularComponent(mc) => Cow::Owned(modular_component_desc( + mc, + item.components(), + item.description(), + )), ItemKind::Glider(_glider) => Cow::Owned(glider_desc(item.description())), ItemKind::Consumable { .. } => Cow::Owned(consumable_desc(item.description())), ItemKind::Throwable { .. } => Cow::Owned(throwable_desc(item.description())), @@ -39,8 +43,21 @@ pub fn item_text<'a>(item: &'a impl ItemDesc) -> (&'_ str, Cow<'a, str>) { } // TODO: localization -fn modular_component_desc(mc: &ModularComponent) -> String { - format!("Modular Component\n\n{:?}", mc) +fn modular_component_desc(mc: &ModularComponent, components: &[Item], description: &str) -> String { + let mut result = format!( + "Modular Component\n\n{:?}\n\n{}", + StatKind::Direct(mc.stats).resolve_stats(components), + description + ); + if !components.is_empty() { + result += "Made from:\n"; + for component in components { + result += component.name(); + result += "\n" + } + result += "\n"; + } + result } fn glider_desc(desc: &str) -> String { format!("Glider\n\n{}\n\n", desc) }