Support modular weapon components made from a tagged material using the material as a multiplier.

This commit is contained in:
Avi Weinstock 2021-02-23 00:00:45 -05:00
parent af01c69456
commit 77aeb43247
14 changed files with 153 additions and 30 deletions

View File

@ -5,5 +5,5 @@ ItemDef(
kind: "BloodsteelIngot",
),
quality: Common,
tags: [],
)
tags: [MetalIngot(1.75)],
)

View File

@ -5,5 +5,5 @@ ItemDef(
kind: "BronzeIngot",
),
quality: Common,
tags: [],
)
tags: [MetalIngot(0.75)],
)

View File

@ -5,5 +5,5 @@ ItemDef(
kind: "CobaltIngot",
),
quality: Common,
tags: [],
)
tags: [MetalIngot(1.5)],
)

View File

@ -5,5 +5,5 @@ ItemDef(
kind: "CopperIngot",
),
quality: Common,
tags: [],
)
tags: [MetalIngot(0.4)],
)

View File

@ -5,5 +5,5 @@ ItemDef(
kind: "IronIngot",
),
quality: Common,
tags: [],
)
tags: [MetalIngot(1.0)],
)

View File

@ -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))],
)

View File

@ -5,5 +5,5 @@ ItemDef(
kind: "SteelIngot",
),
quality: Common,
tags: [],
)
tags: [MetalIngot(1.25)],
)

View File

@ -5,5 +5,5 @@ ItemDef(
kind: "TinIngot",
),
quality: Common,
tags: [],
)
tags: [MetalIngot(0.25)],
)

View File

@ -18,6 +18,5 @@ ItemDef(
],
),
quality: Common,
tags: [ClothItem],
tags: [],
)

View File

@ -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: [],
)

View File

@ -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),
]
),
}

View File

@ -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 {

View File

@ -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<Stats> 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<f32> 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<f32> = 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

View File

@ -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<Right-Click to use>", desc) }