Addressed more comments on MR.

This commit is contained in:
Sam 2021-11-22 19:47:24 -05:00
parent 259db56ca6
commit 524845b661
17 changed files with 344 additions and 229 deletions

View File

@ -12,12 +12,36 @@
(1, Asset("common.loadout.world.traveler0.rawhide")), (1, Asset("common.loadout.world.traveler0.rawhide")),
]), ]),
active_hands: ([ active_hands: ([
(1, "common.items.weapons.sword.bronze-0"), (1, (
(1, "common.items.weapons.axe.bronze_axe-0"), tool: Sword,
(1, "common.items.weapons.hammer.bronze_hammer-0"), material: Bronze,
(1, "common.items.weapons.bow.bone-0"), hands: None,
(1, "common.items.weapons.staff.fiery_wishing_rod"), )),
(1, "common.items.weapons.sceptre.root_green0"), (1, (
tool: Axe,
material: Bronze,
hands: None,
)),
(1, (
tool: Hammer,
material: Bronze,
hands: None,
)),
(1, (
tool: Bow,
material: Wood,
hands: None,
)),
(1, (
tool: Staff,
material: Wood,
hands: None,
)),
(1, (
tool: Sceptre,
material: Wood,
hands: None,
)),
], None), ], None),
)), )),
items: [ items: [

View File

@ -12,12 +12,36 @@
(1, Asset("common.loadout.world.traveler1.wool")), (1, Asset("common.loadout.world.traveler1.wool")),
]), ]),
active_hands: ([ active_hands: ([
(1, "common.items.weapons.sword.iron-0"), (1, (
(1, "common.items.weapons.axe.iron_axe-0"), tool: Sword,
(1, "common.items.weapons.hammer.iron_hammer-0"), material: Iron,
(1, "common.items.weapons.bow.hardwood-0"), hands: None,
(1, "common.items.weapons.staff.heated_arm"), )),
(1, "common.items.weapons.sceptre.staff_nature"), (1, (
tool: Axe,
material: Iron,
hands: None,
)),
(1, (
tool: Hammer,
material: Iron,
hands: None,
)),
(1, (
tool: Bow,
material: Bamboo,
hands: None,
)),
(1, (
tool: Staff,
material: Bamboo,
hands: None,
)),
(1, (
tool: Sceptre,
material: Bamboo,
hands: None,
)),
], None), ], None),
)), )),
items: [ items: [

View File

@ -17,18 +17,66 @@
(1, Asset("common.loadout.world.traveler2.carapace")), (1, Asset("common.loadout.world.traveler2.carapace")),
]), ]),
active_hands: ([ active_hands: ([
(1, "common.items.weapons.sword.steel-0"), (1, (
(1, "common.items.weapons.axe.steel_axe-0"), tool: Sword,
(1, "common.items.weapons.hammer.steel_hammer-0"), material: Steel,
(1, "common.items.weapons.bow.metal-0"), hands: None,
(1, "common.items.weapons.staff.golden_khakkara"), )),
(1, "common.items.weapons.sceptre.totem_green"), (1, (
(1, "common.items.weapons.sword.cobalt-0"), tool: Axe,
(1, "common.items.weapons.axe.cobalt_axe-0"), material: Steel,
(1, "common.items.weapons.hammer.cobalt_hammer-0"), hands: None,
(1, "common.items.weapons.bow.frostwood-0"), )),
(1, "common.items.weapons.staff.aurora"), (1, (
(1, "common.items.weapons.sceptre.loops0"), tool: Hammer,
material: Steel,
hands: None,
)),
(1, (
tool: Bow,
material: Hardwood,
hands: None,
)),
(1, (
tool: Staff,
material: Hardwood,
hands: None,
)),
(1, (
tool: Sceptre,
material: Hardwood,
hands: None,
)),
(1, (
tool: Sword,
material: Cobalt,
hands: None,
)),
(1, (
tool: Axe,
material: Cobalt,
hands: None,
)),
(1, (
tool: Hammer,
material: Cobalt,
hands: None,
)),
(1, (
tool: Bow,
material: Ironwood,
hands: None,
)),
(1, (
tool: Staff,
material: Ironwood,
hands: None,
)),
(1, (
tool: Sceptre,
material: Ironwood,
hands: None,
)),
], None), ], None),
)), )),
items: [ items: [

View File

@ -17,18 +17,41 @@
(1, Asset("common.loadout.world.traveler3.dragonscale")), (1, Asset("common.loadout.world.traveler3.dragonscale")),
]), ]),
active_hands: ([ active_hands: ([
(2, "common.items.weapons.sword.bloodsteel-0"), (2, (
(2, "common.items.weapons.axe.bloodsteel_axe-0"), tool: Sword,
(2, "common.items.weapons.hammer.runic_hammer"), material: Orichalcum,
(2, "common.items.weapons.bow.eldwood-0"), hands: None,
(2, "common.items.weapons.staff.ruby_rod"), )),
(2, "common.items.weapons.sceptre.emerald"), (2, (
tool: Axe,
material: Orichalcum,
hands: None,
)),
(2, (
tool: Hammer,
material: Orichalcum,
hands: None,
)),
(2, (
tool: Bow,
material: Eldwood,
hands: None,
)),
(2, (
tool: Staff,
material: Eldwood,
hands: None,
)),
(2, (
tool: Sceptre,
material: Eldwood,
hands: None,
)),
(2, "common.items.weapons.sword.caladbolg"), (2, "common.items.weapons.sword.caladbolg"),
(2, "common.items.weapons.hammer.mjolnir"), (2, "common.items.weapons.hammer.mjolnir"),
(2, "common.items.weapons.axe.parashu"), (2, "common.items.weapons.axe.parashu"),
(2, "common.items.weapons.bow.sagitta"), (2, "common.items.weapons.bow.sagitta"),
(1, "common.items.weapons.staff.phoenix"), (2, "common.items.weapons.staff.laevateinn"),
(1, "common.items.weapons.staff.laevateinn"),
(1, "common.items.weapons.sceptre.root_evil"), (1, "common.items.weapons.sceptre.root_evil"),
(1, "common.items.weapons.sceptre.caduceus"), (1, "common.items.weapons.sceptre.caduceus"),
], None), ], None),

View File

@ -1864,7 +1864,7 @@
craft_sprite: Some(CraftingBench), craft_sprite: Some(CraftingBench),
), ),
"winged coronet": ( "winged coronet": (
output: ("common.items.armor.misc.head.winged_coronet", 1, false), output: ("common.items.armor.misc.head.winged_coronet", 1),
inputs: [ inputs: [
(Item("common.items.mineral.gem.emerald"), 1, false), (Item("common.items.mineral.gem.emerald"), 1, false),
(Item("common.items.mineral.ingot.gold"), 4, false), (Item("common.items.mineral.ingot.gold"), 4, false),

View File

@ -36,11 +36,22 @@ impl<T: ItemDesc> From<&T> for ItemKey {
ItemKey::Tool(item_definition_id.to_owned()) ItemKey::Tool(item_definition_id.to_owned())
} }
}, },
ItemKind::ModularComponent(_) => { ItemKind::ModularComponent(mod_comp) => {
match modular::weapon_component_to_key(item_desc) { use modular::ModularComponent;
Ok(key) => ItemKey::ModularWeaponComponent(key), match mod_comp {
// TODO: Maybe use a different ItemKey? ModularComponent::ToolPrimaryComponent { .. } => {
Err(_) => ItemKey::Tool(item_definition_id.to_owned()), 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()),
}
},
ModularComponent::ToolSecondaryComponent { .. } => {
ItemKey::Tool(item_definition_id.to_owned())
},
} }
}, },
ItemKind::Lantern(Lantern { kind, .. }) => ItemKey::Lantern(kind.clone()), ItemKind::Lantern(Lantern { kind, .. }) => ItemKey::Lantern(kind.clone()),

View File

@ -82,6 +82,10 @@ pub enum Quality {
Debug, // Red Debug, // Red
} }
impl Quality {
pub const MIN: Self = Self::Low;
}
pub trait TagExampleInfo { pub trait TagExampleInfo {
fn name(&self) -> &str; fn name(&self) -> &str;
/// What item to show in the crafting hud if the player has nothing with the /// What item to show in the crafting hud if the player has nothing with the
@ -379,20 +383,19 @@ where
{ {
serializer.serialize_str(match field { serializer.serialize_str(match field {
ItemBase::Raw(item_def) => &item_def.item_definition_id, ItemBase::Raw(item_def) => &item_def.item_definition_id,
// TODO: Encode this data somehow
ItemBase::Modular(mod_base) => mod_base.pseudo_item_id(), ItemBase::Modular(mod_base) => mod_base.pseudo_item_id(),
}) })
} }
// Custom de-serialization for ItemDef to retrieve the ItemDef from assets using // Custom de-serialization for ItemBase to retrieve the ItemBase from assets
// its asset specifier (item_definition_id) // using its asset specifier (item_definition_id)
fn deserialize_item_base<'de, D>(deserializer: D) -> Result<ItemBase, D::Error> fn deserialize_item_base<'de, D>(deserializer: D) -> Result<ItemBase, D::Error>
where where
D: de::Deserializer<'de>, D: de::Deserializer<'de>,
{ {
struct ItemDefStringVisitor; struct ItemBaseStringVisitor;
impl<'de> de::Visitor<'de> for ItemDefStringVisitor { impl<'de> de::Visitor<'de> for ItemBaseStringVisitor {
type Value = ItemBase; type Value = ItemBase;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
@ -413,7 +416,7 @@ where
} }
} }
deserializer.deserialize_str(ItemDefStringVisitor) deserializer.deserialize_str(ItemBaseStringVisitor)
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
@ -581,6 +584,7 @@ impl PartialEq for Item {
(&self.item_base, &other.item_base) (&self.item_base, &other.item_base)
{ {
self_def.item_definition_id == other_def.item_definition_id self_def.item_definition_id == other_def.item_definition_id
&& self.components == other.components
} else { } else {
false false
} }
@ -592,6 +596,15 @@ impl assets::Compound for ItemDef {
cache: &assets::AssetCache<S>, cache: &assets::AssetCache<S>,
specifier: &str, specifier: &str,
) -> Result<Self, BoxedError> { ) -> Result<Self, BoxedError> {
if specifier.starts_with("veloren.core.") {
return Err(format!(
"Attempted to load an asset from a specifier reserved for core veloren functions. \
Specifier: {}",
specifier
)
.into());
}
let RawItemDef { let RawItemDef {
name, name,
description, description,
@ -650,17 +663,10 @@ impl Item {
pub fn new_from_item_base( pub fn new_from_item_base(
inner_item: ItemBase, inner_item: ItemBase,
input_components: &[Item], components: Vec<Item>,
ability_map: &AbilityMap, ability_map: &AbilityMap,
msm: &MaterialStatManifest, msm: &MaterialStatManifest,
) -> Self { ) -> Self {
let mut components = Vec::new();
components.extend(
input_components
.iter()
.map(|comp| comp.duplicate(ability_map, msm)),
);
let item_hash = { let item_hash = {
let mut s = DefaultHasher::new(); let mut s = DefaultHasher::new();
inner_item.item_definition_id().hash(&mut s); inner_item.item_definition_id().hash(&mut s);
@ -684,8 +690,12 @@ impl Item {
/// Creates a new instance of an `Item` from the provided asset identifier /// Creates a new instance of an `Item` from the provided asset identifier
/// Panics if the asset does not exist. /// Panics if the asset does not exist.
pub fn new_from_asset_expect(asset_specifier: &str) -> Self { pub fn new_from_asset_expect(asset_specifier: &str) -> Self {
let err_string = format!("Expected asset to exist: {}", asset_specifier); Item::new_from_asset(asset_specifier).unwrap_or_else(|err| {
Item::new_from_asset(asset_specifier).expect(&err_string) panic!(
"Expected asset to exist: {}, instead got error {:?}",
asset_specifier, err
);
})
} }
/// Creates a Vec containing one of each item that matches the provided /// Creates a Vec containing one of each item that matches the provided
@ -709,7 +719,7 @@ impl Item {
let ability_map = AbilityMap::default(); let ability_map = AbilityMap::default();
Ok(Item::new_from_item_base( Ok(Item::new_from_item_base(
inner_item, inner_item,
&[], Vec::new(),
&ability_map, &ability_map,
&msm, &msm,
)) ))
@ -718,12 +728,17 @@ impl Item {
/// Duplicates an item, creating an exact copy but with a new item ID /// Duplicates an item, creating an exact copy but with a new item ID
#[must_use] #[must_use]
pub fn duplicate(&self, ability_map: &AbilityMap, msm: &MaterialStatManifest) -> Self { pub fn duplicate(&self, ability_map: &AbilityMap, msm: &MaterialStatManifest) -> Self {
let duplicated_components = self
.components
.iter()
.map(|comp| comp.duplicate(ability_map, msm))
.collect();
let mut new_item = Item::new_from_item_base( let mut new_item = Item::new_from_item_base(
match &self.item_base { match &self.item_base {
ItemBase::Raw(item_def) => ItemBase::Raw(Arc::clone(item_def)), ItemBase::Raw(item_def) => ItemBase::Raw(Arc::clone(item_def)),
ItemBase::Modular(mod_base) => ItemBase::Modular(mod_base.duplicate()), ItemBase::Modular(mod_base) => ItemBase::Modular(mod_base.clone()),
}, },
&self.components, duplicated_components,
ability_map, ability_map,
msm, msm,
); );
@ -812,9 +827,6 @@ impl Item {
ability_map: &AbilityMap, ability_map: &AbilityMap,
msm: &MaterialStatManifest, msm: &MaterialStatManifest,
) { ) {
// TODO: hook for typechecking (not needed atm if this is only used by DB
// persistence, but will definitely be needed once enhancement slots are
// added to prevent putting a sword into another sword)
self.components.push(component); self.components.push(component);
// adding a component changes the stats, so recalculate the ItemConfig // adding a component changes the stats, so recalculate the ItemConfig
self.update_item_config(ability_map, msm); self.update_item_config(ability_map, msm);
@ -981,7 +993,7 @@ impl Item {
pub fn create_test_item_from_kind(kind: ItemKind) -> Self { pub fn create_test_item_from_kind(kind: ItemKind) -> Self {
Self::new_from_item_base( Self::new_from_item_base(
ItemBase::Raw(Arc::new(ItemDef::create_test_itemdef_from_kind(kind))), ItemBase::Raw(Arc::new(ItemDef::create_test_itemdef_from_kind(kind))),
&[], Vec::new(),
&Default::default(), &Default::default(),
&Default::default(), &Default::default(),
) )
@ -1056,7 +1068,7 @@ impl Component for Item {
type Storage = DerefFlaggedStorage<Self, IdvStorage<Self>>; type Storage = DerefFlaggedStorage<Self, IdvStorage<Self>>;
} }
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ItemDrop(pub Item); pub struct ItemDrop(pub Item);
impl Component for ItemDrop { impl Component for ItemDrop {

View File

@ -15,12 +15,6 @@ pub enum ModularBase {
} }
impl ModularBase { impl ModularBase {
pub(super) fn duplicate(&self) -> Self {
match self {
ModularBase::Tool => ModularBase::Tool,
}
}
// DO NOT CHANGE. THIS IS A PERSISTENCE RELATED FUNCTION. MUST MATCH THE // DO NOT CHANGE. THIS IS A PERSISTENCE RELATED FUNCTION. MUST MATCH THE
// FUNCTION BELOW. // FUNCTION BELOW.
pub fn pseudo_item_id(&self) -> &str { pub fn pseudo_item_id(&self) -> &str {
@ -106,13 +100,16 @@ impl ModularBase {
let material_name = comp let material_name = comp
.components() .components()
.iter() .iter()
.find_map(|mat| match &*mat.kind() { .find_map(|mat| match mat.kind() {
ItemKind::Ingredient { descriptor, .. } => { Cow::Owned(ItemKind::Ingredient { descriptor, .. }) => {
Some(descriptor.to_owned()) Some(Cow::Owned(descriptor))
},
Cow::Borrowed(ItemKind::Ingredient { descriptor, .. }) => {
Some(Cow::Borrowed(descriptor.as_str()))
}, },
_ => None, _ => None,
}) })
.unwrap_or_else(|| "Modular".to_owned()); .unwrap_or_else(|| "Modular".into());
Some(format!("{} {}", material_name, weapon_name)) Some(format!("{} {}", material_name, weapon_name))
}, },
_ => None, _ => None,
@ -126,7 +123,7 @@ impl ModularBase {
pub fn compute_quality(&self, components: &[Item]) -> Quality { pub fn compute_quality(&self, components: &[Item]) -> Quality {
components components
.iter() .iter()
.fold(Quality::Low, |a, b| a.max(b.quality())) .fold(Quality::MIN, |a, b| a.max(b.quality()))
} }
pub fn ability_spec(&self, components: &[Item]) -> Option<Cow<AbilitySpec>> { pub fn ability_spec(&self, components: &[Item]) -> Option<Cow<AbilitySpec>> {
@ -165,26 +162,15 @@ impl ModularComponent {
) -> Option<tool::Stats> { ) -> Option<tool::Stats> {
match self { match self {
Self::ToolPrimaryComponent { stats, .. } => { Self::ToolPrimaryComponent { stats, .. } => {
let mut material_multipliers = Vec::new(); let average_material_mult = components
for component in components.iter() { .iter()
if let Some(tool_stats) = msm.0.get(component.item_definition_id()) { .filter_map(|comp| msm.0.get(comp.item_definition_id()).copied().zip(Some(1)))
material_multipliers.push(*tool_stats); .reduce(|(stats_a, count_a), (stats_b, count_b)| {
} (stats_a + stats_b, count_a + count_b)
} })
.map_or_else(tool::Stats::one, |(stats_sum, count)| stats_sum / count);
// Take the average of the material multipliers Some(*stats * average_material_mult)
let material_mult = if !material_multipliers.is_empty() {
let mut average_mult = tool::Stats::zero();
for stat in material_multipliers.iter() {
average_mult += *stat;
}
average_mult /= material_multipliers.len();
average_mult
} else {
tool::Stats::one()
};
Some(*stats * material_mult)
}, },
Self::ToolSecondaryComponent { stats, .. } => Some(*stats), Self::ToolSecondaryComponent { stats, .. } => Some(*stats),
} }
@ -208,8 +194,8 @@ lazy_static! {
let mut component_pool = HashMap::new(); let mut component_pool = HashMap::new();
// Load recipe book (done to check that material is valid for a particular component) // Load recipe book (done to check that material is valid for a particular component)
let recipe::RawRecipeBook(recipes) = let recipe_book = recipe::RawRecipeBook::load_expect("common.recipe_book");
recipe::RawRecipeBook::load_expect_cloned("common.recipe_book"); let recipes = &recipe_book.read().0;
const ASSET_PREFIX: &str = "common.items.crafting_ing.modular.primary"; const ASSET_PREFIX: &str = "common.items.crafting_ing.modular.primary";
@ -309,59 +295,54 @@ pub fn random_weapon(
let material = Item::new_from_asset_expect(material_id); let material = Item::new_from_asset_expect(material_id);
let primary_components = PRIMARY_COMPONENT_POOL let primary_components = PRIMARY_COMPONENT_POOL
.get(&(tool, material_id.to_owned())) .get(&(tool, material_id.to_owned()))
.map_or(Vec::new(), |components| { .into_iter()
components .flatten()
.iter() .filter(|(_def, hand)| match (hand_restriction, hand) {
.filter(|(_def, hand)| match (hand_restriction, hand) { (Some(restriction), Some(hand)) => restriction == *hand,
(Some(restriction), Some(hand)) => restriction == *hand, (None, _) | (_, None) => true,
(None, _) | (_, None) => true, })
}) .collect::<Vec<_>>();
.map(|entry| (1.0, entry))
.collect::<Vec<_>>()
});
let (primary_component, hand_restriction) = { let (primary_component, hand_restriction) = {
let (def, hand) = primary_components let (def, hand) = primary_components
.choose(&mut rng) .choose(&mut rng)
.ok_or(ModularWeaponCreationError::PrimaryComponentNotFound)? .ok_or(ModularWeaponCreationError::PrimaryComponentNotFound)?;
.1;
let comp = Item::new_from_item_base( let comp = Item::new_from_item_base(
ItemBase::Raw(Arc::clone(def)), ItemBase::Raw(Arc::clone(def)),
&[material], vec![material],
&ability_map, &ability_map,
&msm, &msm,
); );
(comp, hand_restriction.or(*hand)) (comp, hand_restriction.or(*hand))
}; };
let secondary_components = let secondary_components = SECONDARY_COMPONENT_POOL
SECONDARY_COMPONENT_POOL .get(&tool)
.get(&tool) .into_iter()
.map_or(Vec::new(), |components| { .flatten()
components .filter(|(_def, hand)| match (hand_restriction, hand) {
.iter() (Some(restriction), Some(hand)) => restriction == *hand,
.filter(|(_def, hand)| match (hand_restriction, hand) { (None, _) | (_, None) => true,
(Some(restriction), Some(hand)) => restriction == *hand, })
(None, _) | (_, None) => true, .collect::<Vec<_>>();
})
.map(|entry| (1.0, entry))
.collect::<Vec<_>>()
});
let secondary_component = { let secondary_component = {
let def = &secondary_components let def = &secondary_components
.choose(&mut rng) .choose(&mut rng)
.ok_or(ModularWeaponCreationError::SecondaryComponentNotFound)? .ok_or(ModularWeaponCreationError::SecondaryComponentNotFound)?
.1
.0; .0;
Item::new_from_item_base(ItemBase::Raw(Arc::clone(def)), &[], &ability_map, &msm) Item::new_from_item_base(
ItemBase::Raw(Arc::clone(def)),
Vec::new(),
&ability_map,
&msm,
)
}; };
// Create modular weapon // Create modular weapon
let components = vec![primary_component, secondary_component];
Ok(Item::new_from_item_base( Ok(Item::new_from_item_base(
ItemBase::Modular(ModularBase::Tool), ItemBase::Modular(ModularBase::Tool),
&components, vec![primary_component, secondary_component],
&ability_map, &ability_map,
&msm, &msm,
)) ))
@ -407,29 +388,17 @@ pub type ModularWeaponComponentKey = (String, String);
pub enum ModularWeaponComponentKeyError { pub enum ModularWeaponComponentKeyError {
MaterialNotFound, MaterialNotFound,
NotMainComponent,
} }
pub fn weapon_component_to_key( pub fn weapon_component_to_key(
mod_weap_comp: &dyn ItemDesc, item_def_id: &str,
components: &[Item],
) -> Result<ModularWeaponComponentKey, ModularWeaponComponentKeyError> { ) -> Result<ModularWeaponComponentKey, ModularWeaponComponentKeyError> {
match if let ItemKind::ModularComponent(ModularComponent::ToolPrimaryComponent { .. }) = match components.iter().find_map(|mat| match &*mat.kind() {
&*mod_weap_comp.kind() ItemKind::Ingredient { .. } => Some(mat.item_definition_id().to_owned()),
{ _ => None,
let component_id = mod_weap_comp.item_definition_id().to_owned(); }) {
let material_id = mod_weap_comp Some(material_id) => Ok((item_def_id.to_owned(), material_id)),
.components() None => Err(ModularWeaponComponentKeyError::MaterialNotFound),
.iter()
.find_map(|mat| match &*mat.kind() {
ItemKind::Ingredient { .. } => Some(mat.item_definition_id().to_owned()),
_ => None,
});
Some((component_id, material_id))
} else {
None
} {
Some((component_id, Some(material_id))) => Ok((component_id, material_id)),
Some((_component_id, None)) => Err(ModularWeaponComponentKeyError::MaterialNotFound),
None => Err(ModularWeaponComponentKeyError::NotMainComponent),
} }
} }

View File

@ -8,7 +8,7 @@ use crate::{
use hashbrown::HashMap; use hashbrown::HashMap;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{ use std::{
ops::{AddAssign, DivAssign, Mul, MulAssign, Sub}, ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub},
time::Duration, time::Duration,
}; };
@ -139,28 +139,39 @@ impl Asset for Stats {
const EXTENSION: &'static str = "ron"; const EXTENSION: &'static str = "ron";
} }
impl AddAssign<Stats> for Stats { impl Add<Stats> for Stats {
fn add_assign(&mut self, other: Stats) { type Output = Self;
self.equip_time_secs += other.equip_time_secs;
self.power += other.power; fn add(self, other: Self) -> Self {
self.effect_power += other.effect_power; Self {
self.speed += other.speed; equip_time_secs: self.equip_time_secs + other.equip_time_secs,
self.crit_chance += other.crit_chance; power: self.power + other.power,
self.range += other.range; effect_power: self.effect_power + other.effect_power,
self.energy_efficiency += other.energy_efficiency; speed: self.speed + other.speed,
self.buff_strength += other.buff_strength; crit_chance: self.crit_chance + other.crit_chance,
range: self.range + other.range,
energy_efficiency: self.energy_efficiency + other.energy_efficiency,
buff_strength: self.buff_strength + other.buff_strength,
}
} }
} }
impl MulAssign<Stats> for Stats { impl AddAssign<Stats> for Stats {
fn mul_assign(&mut self, other: Stats) { fn add_assign(&mut self, other: Stats) { *self = *self + other; }
self.equip_time_secs *= other.equip_time_secs; }
self.power *= other.power; impl Sub<Stats> for Stats {
self.effect_power *= other.effect_power; type Output = Self;
self.speed *= other.speed;
self.crit_chance *= other.crit_chance; fn sub(self, other: Self) -> Self::Output {
self.range *= other.range; Self {
self.energy_efficiency *= other.energy_efficiency; equip_time_secs: self.equip_time_secs - other.equip_time_secs,
self.buff_strength *= other.buff_strength; power: self.power - other.power,
effect_power: self.effect_power - other.effect_power,
speed: self.speed - other.speed,
crit_chance: self.crit_chance - other.crit_chance,
range: self.range - other.range,
energy_efficiency: self.range - other.energy_efficiency,
buff_strength: self.buff_strength - other.buff_strength,
}
} }
} }
impl Mul<Stats> for Stats { impl Mul<Stats> for Stats {
@ -179,35 +190,29 @@ impl Mul<Stats> for Stats {
} }
} }
} }
impl DivAssign<usize> for Stats { impl MulAssign<Stats> for Stats {
fn div_assign(&mut self, scalar: usize) { fn mul_assign(&mut self, other: Stats) { *self = *self * other; }
self.equip_time_secs /= scalar as f32;
self.power /= scalar as f32;
self.effect_power /= scalar as f32;
self.speed /= scalar as f32;
self.crit_chance /= scalar as f32;
self.range /= scalar as f32;
self.energy_efficiency /= scalar as f32;
self.buff_strength /= scalar as f32;
}
} }
impl Div<usize> for Stats {
impl Sub<Stats> for Stats {
type Output = Self; type Output = Self;
fn sub(self, other: Self) -> Self::Output { fn div(self, scalar: usize) -> Self {
let scalar = scalar as f32;
Self { Self {
equip_time_secs: self.equip_time_secs - other.equip_time_secs, equip_time_secs: self.equip_time_secs / scalar,
power: self.power - other.power, power: self.power / scalar,
effect_power: self.effect_power - other.effect_power, effect_power: self.effect_power / scalar,
speed: self.speed - other.speed, speed: self.speed / scalar,
crit_chance: self.crit_chance - other.crit_chance, crit_chance: self.crit_chance / scalar,
range: self.range - other.range, range: self.range / scalar,
energy_efficiency: self.range - other.energy_efficiency, energy_efficiency: self.energy_efficiency / scalar,
buff_strength: self.buff_strength - other.buff_strength, buff_strength: self.buff_strength / scalar,
} }
} }
} }
impl DivAssign<usize> for Stats {
fn div_assign(&mut self, scalar: usize) { *self = *self / scalar; }
}
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct MaterialStatManifest(pub HashMap<String, Stats>); pub struct MaterialStatManifest(pub HashMap<String, Stats>);
@ -262,11 +267,9 @@ impl Tool {
pub fn base_effect_power(&self) -> f32 { self.stats.effect_power } pub fn base_effect_power(&self) -> f32 { self.stats.effect_power }
pub fn base_speed(&self) -> f32 { /// Has floor to prevent infinite durations being created later down due to
// Has floor to prevent infinite durations being created later down due to a /// a divide by zero
// divide by zero pub fn base_speed(&self) -> f32 { self.stats.speed.max(0.1) }
self.stats.speed.max(0.1)
}
pub fn base_crit_chance(&self) -> f32 { self.stats.crit_chance } pub fn base_crit_chance(&self) -> f32 { self.stats.crit_chance }

View File

@ -205,7 +205,7 @@ impl Loadout {
/// Returns all items currently equipped that an item of the given ItemKind /// Returns all items currently equipped that an item of the given ItemKind
/// could replace /// could replace
pub(super) fn equipped_items_of_kind<'a>( pub(super) fn equipped_items_replaceable_by<'a>(
&'a self, &'a self,
item_kind: &'a ItemKind, item_kind: &'a ItemKind,
) -> impl Iterator<Item = &'a Item> { ) -> impl Iterator<Item = &'a Item> {

View File

@ -72,6 +72,13 @@ impl ItemSpec {
}; };
Ok(item) Ok(item)
}, },
ItemSpec::ModularWeapon {
tool,
material,
hands,
} => {
item::modular::random_weapon(*tool, *material, *hands)
},
} }
} }
@ -157,20 +164,6 @@ impl Hands {
} }
Ok(()) Ok(())
}, },
ItemSpec::ModularWeapon {
tool,
material,
hands,
} => {
let item = item::modular::random_weapon(*tool, *material, *hands)
.expect("Invalid modular weapon");
if !equip_slot.can_hold(&*item.kind()) {
panic!(
"Tried to place {:?} handed {:?} {:?} into {:?}",
hands, material, tool, equip_slot
);
}
},
} }
} }
} }

View File

@ -775,11 +775,11 @@ impl Inventory {
true true
} }
pub fn equipped_items_of_kind<'a>( pub fn equipped_items_replaceable_by<'a>(
&'a self, &'a self,
item_kind: &'a ItemKind, item_kind: &'a ItemKind,
) -> impl Iterator<Item = &'a Item> { ) -> impl Iterator<Item = &'a Item> {
self.loadout.equipped_items_of_kind(item_kind) self.loadout.equipped_items_replaceable_by(item_kind)
} }
pub fn swap_equipped_weapons(&mut self) { self.loadout.swap_equipped_weapons() } pub fn swap_equipped_weapons(&mut self) { self.loadout.swap_equipped_weapons() }

View File

@ -24,7 +24,7 @@ pub(super) fn get_test_bag(slots: u16) -> Item {
Item::new_from_item_base( Item::new_from_item_base(
ItemBase::Raw(Arc::new(item_def)), ItemBase::Raw(Arc::new(item_def)),
&[], Vec::new(),
&AbilityMap::default(), &AbilityMap::default(),
&MaterialStatManifest::default(), &MaterialStatManifest::default(),
) )

View File

@ -130,7 +130,20 @@ impl<T: AsRef<str>> LootSpec<T> {
tool, tool,
material, material,
hands, hands,
} => item::modular::random_weapon(*tool, *material, *hands).ok(), } => item::modular::random_weapon(*tool, *material, *hands).map_or_else(
|e| {
warn!(
?e,
"error while creating modular weapon. Toolkind: {:?}, Material: {:?}, \
Hands: {:?}",
tool,
material,
hands
);
None
},
Option::Some,
),
} }
} }
} }

View File

@ -11,7 +11,7 @@ use crate::{
}; };
use hashbrown::HashMap; use hashbrown::HashMap;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::sync::Arc; use std::{borrow::Cow, sync::Arc};
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub enum RecipeInput { pub enum RecipeInput {
@ -127,7 +127,7 @@ impl Recipe {
let crafted_item = Item::new_from_item_base( let crafted_item = Item::new_from_item_base(
ItemBase::Raw(Arc::clone(item_def)), ItemBase::Raw(Arc::clone(item_def)),
&components, components,
ability_map, ability_map,
msm, msm,
); );
@ -247,15 +247,12 @@ pub fn modular_weapon(
) -> Result<Item, ModularWeaponError> { ) -> Result<Item, ModularWeaponError> {
use modular::ModularComponent; use modular::ModularComponent;
// Closure to get inner modular component info from item in a given slot // Closure to get inner modular component info from item in a given slot
fn unwrap_modular(inv: &Inventory, slot: InvSlotId) -> Option<ModularComponent> { fn unwrap_modular(inv: &Inventory, slot: InvSlotId) -> Option<Cow<ModularComponent>> {
if let Some(ItemKind::ModularComponent(mod_comp)) = inv.get(slot).and_then(|item| match item.kind() {
inv.get(slot).map(|item| item.kind()).as_deref() Cow::Owned(ItemKind::ModularComponent(mod_comp)) => Some(Cow::Owned(mod_comp)),
{ Cow::Borrowed(ItemKind::ModularComponent(mod_comp)) => Some(Cow::Borrowed(mod_comp)),
// TODO: Remove _ => None,
Some(mod_comp.clone()) })
} else {
None
}
} }
// Checks if both components are comptabile, and if so returns the toolkind to // Checks if both components are comptabile, and if so returns the toolkind to
@ -277,13 +274,12 @@ pub fn modular_weapon(
hand_restriction: hands_b, hand_restriction: hands_b,
.. ..
}, },
) = (primary_component, secondary_component) ) = (&*primary_component, &*secondary_component)
{ {
// Checks that both components are of the same tool kind // Checks that both components are of the same tool kind
if tool_a == tool_b { if tool_a == tool_b {
// Checks that if both components have a hand restriction, they are the same // Checks that if both components have a hand restriction, they are the same
let hands_check = let hands_check = hands_a.zip(*hands_b).map_or(true, |(a, b)| a == b);
hands_a.map_or(true, |hands| hands_b.map_or(true, |hands2| hands == hands2));
if hands_check { if hands_check {
Ok(()) Ok(())
} else { } else {
@ -310,10 +306,9 @@ pub fn modular_weapon(
.expect("Expected component to exist"); .expect("Expected component to exist");
// Create modular weapon // Create modular weapon
let components = vec![primary_component, secondary_component];
Ok(Item::new_from_item_base( Ok(Item::new_from_item_base(
ItemBase::Modular(modular::ModularBase::Tool), ItemBase::Modular(modular::ModularBase::Tool),
&components, vec![primary_component, secondary_component],
ability_map, ability_map,
msm, msm,
)) ))

View File

@ -942,7 +942,7 @@ impl<'a> Widget for Crafting<'a> {
} }
}) })
}) })
.unwrap_or({ .unwrap_or_else(|| {
item_defs item_defs
.first() .first()
.map(|i| i.item_definition_id()) .map(|i| i.item_definition_id())

View File

@ -464,7 +464,7 @@ impl<'a> Widget for ItemTooltip<'a> {
let item_kind = &*item.kind(); let item_kind = &*item.kind();
let equip_slot = inventory.equipped_items_of_kind(item_kind); let equip_slot = inventory.equipped_items_replaceable_by(item_kind);
let (title, desc) = (item.name().to_string(), item.description().to_string()); let (title, desc) = (item.name().to_string(), item.description().to_string());