Addressed review

This commit is contained in:
Sam 2023-03-19 21:28:09 -04:00
parent 79812a7326
commit 132f26dbc0
8 changed files with 82 additions and 79 deletions

View File

@ -27,20 +27,19 @@ pub enum ArmorKind {
impl ArmorKind {
pub fn has_durability(self) -> bool {
use ArmorKind::*;
match self {
Shoulder => true,
Chest => true,
Belt => true,
Hand => true,
Pants => true,
Foot => true,
Back => true,
Ring => false,
Neck => false,
Head => true,
Tabard => false,
Bag => false,
ArmorKind::Shoulder => true,
ArmorKind::Chest => true,
ArmorKind::Belt => true,
ArmorKind::Hand => true,
ArmorKind::Pants => true,
ArmorKind::Foot => true,
ArmorKind::Back => true,
ArmorKind::Ring => false,
ArmorKind::Neck => false,
ArmorKind::Head => true,
ArmorKind::Tabard => false,
ArmorKind::Bag => false,
}
}
}
@ -262,8 +261,7 @@ impl Armor {
set_stats * multiplier
},
};
let DurabilityMultiplier(mult) = durability_multiplier;
base_stats * mult
base_stats * durability_multiplier.0
}
#[cfg(test)]

View File

@ -399,7 +399,7 @@ pub struct Item {
/// Tracks how many deaths occurred while item was equipped, which is
/// converted into the items durability. Only tracked for tools and armor
/// currently.
durability: Option<u32>,
durability_lost: Option<u32>,
}
use std::hash::{Hash, Hasher};
@ -793,9 +793,9 @@ impl Item {
// These fields are updated immediately below
item_config: None,
hash: 0,
durability: None,
durability_lost: None,
};
item.durability = item.has_durability().then_some(0);
item.durability_lost = item.has_durability().then_some(0);
item.update_item_state(ability_map, msm);
item
}
@ -1198,16 +1198,17 @@ impl Item {
}
}
pub fn durability(&self) -> Option<u32> { self.durability.map(|x| x.min(Self::MAX_DURABILITY)) }
pub fn durability(&self) -> Option<u32> {
self.durability_lost.map(|x| x.min(Self::MAX_DURABILITY))
}
pub fn stats_durability_multiplier(&self) -> DurabilityMultiplier {
let durability = self
.durability
.map_or(0, |x| x.clamp(0, Self::MAX_DURABILITY));
let durability_lost = self.durability_lost.unwrap_or(0);
debug_assert!(durability_lost <= Self::MAX_DURABILITY);
const DURABILITY_THRESHOLD: u32 = 4;
const MIN_FRAC: f32 = 0.2;
let mult = (1.0
- durability.saturating_sub(DURABILITY_THRESHOLD) as f32
- durability_lost.saturating_sub(DURABILITY_THRESHOLD) as f32
/ (Self::MAX_DURABILITY - DURABILITY_THRESHOLD) as f32)
* (1.0 - MIN_FRAC)
+ MIN_FRAC;
@ -1218,13 +1219,22 @@ impl Item {
match &*self.kind() {
ItemKind::Tool(_) => true,
ItemKind::Armor(armor) => armor.kind.has_durability(),
_ => false,
ItemKind::ModularComponent(_)
| ItemKind::Lantern(_)
| ItemKind::Glider
| ItemKind::Consumable { .. }
| ItemKind::Throwable { .. }
| ItemKind::Utility { .. }
| ItemKind::Ingredient { .. }
| ItemKind::TagExamples { .. } => false,
}
}
pub fn apply_durability(&mut self, ability_map: &AbilityMap, msm: &MaterialStatManifest) {
if let Some(durability) = &mut self.durability {
*durability += 1;
pub fn increment_damage(&mut self, ability_map: &AbilityMap, msm: &MaterialStatManifest) {
if let Some(durability_lost) = &mut self.durability_lost {
if *durability_lost < Self::MAX_DURABILITY {
*durability_lost += 1;
}
}
// Update item state after applying durability because stats have potential to
// change from different durability
@ -1232,23 +1242,23 @@ impl Item {
}
pub fn persistence_durability(&self) -> Option<NonZeroU32> {
self.durability.and_then(NonZeroU32::new)
self.durability_lost.and_then(NonZeroU32::new)
}
pub fn persistence_set_durability(&mut self, value: Option<NonZeroU32>) {
// If changes have been made so that item no longer needs to track durability,
// set to None
if !self.has_durability() {
self.durability = None;
self.durability_lost = None;
} else {
// Set durability to persisted value, and if item previously had no durability,
// set to Some(0) so that durability will be tracked
self.durability = Some(value.map_or(0, NonZeroU32::get));
self.durability_lost = Some(value.map_or(0, NonZeroU32::get));
}
}
pub fn reset_durability(&mut self, ability_map: &AbilityMap, msm: &MaterialStatManifest) {
self.durability = self.durability.map(|_| 0);
self.durability_lost = self.has_durability().then_some(0);
// Update item state after applying durability because stats have potential to
// change from different durability
self.update_item_state(ability_map, msm);

View File

@ -146,6 +146,20 @@ impl Stats {
let diminished = (self.buff_strength - base + 1.0).log(5.0);
base + diminished
}
pub fn with_durability_mult(&self, dur_mult: DurabilityMultiplier) -> Self {
let less_scaled = dur_mult.0 * 0.5 + 0.5;
Self {
equip_time_secs: self.equip_time_secs / less_scaled.max(0.01),
power: self.power * dur_mult.0,
effect_power: self.effect_power * dur_mult.0,
speed: self.speed * less_scaled,
crit_chance: self.crit_chance * dur_mult.0,
range: self.range * dur_mult.0,
energy_efficiency: self.energy_efficiency * dur_mult.0,
buff_strength: self.buff_strength * dur_mult.0,
}
}
}
impl Asset for Stats {
@ -213,25 +227,6 @@ impl MulAssign<Stats> for Stats {
fn mul_assign(&mut self, other: Stats) { *self = *self * other; }
}
impl Mul<DurabilityMultiplier> for Stats {
type Output = Self;
fn mul(self, value: DurabilityMultiplier) -> Self {
let DurabilityMultiplier(value) = value;
let less_scaled = value * 0.5 + 0.5;
Self {
equip_time_secs: self.equip_time_secs / less_scaled.max(0.01),
power: self.power * value,
effect_power: self.effect_power * value,
speed: self.speed * less_scaled,
crit_chance: self.crit_chance * value,
range: self.range * value,
energy_efficiency: self.energy_efficiency * value,
buff_strength: self.buff_strength * value,
}
}
}
impl Div<f32> for Stats {
type Output = Self;
@ -249,6 +244,12 @@ impl Div<f32> for Stats {
}
}
impl Mul<DurabilityMultiplier> for Stats {
type Output = Self;
fn mul(self, value: DurabilityMultiplier) -> Self { self.with_durability_mult(value) }
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Tool {
pub kind: ToolKind,

View File

@ -424,19 +424,16 @@ impl Loadout {
}
/// Increments durability by 1 of all valid items
pub(super) fn apply_durability(
pub(super) fn damage_items(
&mut self,
ability_map: &item::tool::AbilityMap,
msm: &item::MaterialStatManifest,
) {
self.slots
.iter_mut()
.filter(|slot| slot.slot.as_ref().map_or(false, |i| i.has_durability()))
.for_each(|slot| {
if let Some(item) = &mut slot.slot {
item.apply_durability(ability_map, msm);
}
})
.filter_map(|slot| slot.slot.as_mut())
.filter(|item| item.has_durability())
.for_each(|item| item.increment_damage(ability_map, msm));
}
/// Resets durability of item in specified slot

View File

@ -884,12 +884,12 @@ impl Inventory {
}
/// Increments durability of all valid items equipped in loaodut by 1
pub fn apply_durability(
pub fn damage_items(
&mut self,
ability_map: &item::tool::AbilityMap,
msm: &item::MaterialStatManifest,
) {
self.loadout.apply_durability(ability_map, msm)
self.loadout.damage_items(ability_map, msm)
}
/// Resets durability of item in specified slot
@ -901,7 +901,7 @@ impl Inventory {
) {
match slot {
Slot::Inventory(invslot) => {
if let Some(Some(item)) = self.slot_mut(invslot).map(Option::as_mut) {
if let Some(Some(item)) = self.slot_mut(invslot) {
item.reset_durability(ability_map, msm);
}
},

View File

@ -14,7 +14,7 @@ use crate::{
};
use hashbrown::HashMap;
use serde::{Deserialize, Serialize};
use std::{borrow::Cow, ops::Mul, sync::Arc};
use std::{borrow::Cow, sync::Arc};
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum RecipeInput {
@ -517,13 +517,11 @@ impl assets::Compound for RecipeBook {
cache: assets::AnyCache,
specifier: &assets::SharedString,
) -> Result<Self, assets::BoxedError> {
#[inline]
fn load_item_def(spec: &(String, u32)) -> Result<(Arc<ItemDef>, u32), assets::Error> {
let def = Arc::<ItemDef>::load_cloned(&spec.0)?;
Ok((def, spec.1))
}
#[inline]
fn load_recipe_input(
(input, amount, is_mod_comp): &(RawRecipeInput, u32, bool),
) -> Result<(RecipeInput, u32, bool), assets::Error> {
@ -835,7 +833,6 @@ impl assets::Compound for ComponentRecipeBook {
cache: assets::AnyCache,
specifier: &assets::SharedString,
) -> Result<Self, assets::BoxedError> {
#[inline]
fn create_recipe_key(raw_recipe: &RawComponentRecipe) -> ComponentKey {
match &raw_recipe.output {
RawComponentOutput::ToolPrimaryComponent { toolkind, item: _ } => {
@ -853,7 +850,6 @@ impl assets::Compound for ComponentRecipeBook {
}
}
#[inline]
fn load_recipe(raw_recipe: &RawComponentRecipe) -> Result<ComponentRecipe, assets::Error> {
let output = match &raw_recipe.output {
RawComponentOutput::ToolPrimaryComponent { toolkind: _, item } => {
@ -979,18 +975,20 @@ impl RepairRecipe {
inventory_contains_ingredients(self.inputs(item), inv, 1)
}
pub fn inputs(&self, item: &Item) -> impl ExactSizeIterator<Item = (&RecipeInput, u32)> {
pub fn inputs(&self, item: &Item) -> impl Iterator<Item = (&RecipeInput, u32)> {
let item_durability = item.durability().unwrap_or(0);
// TODO: Figure out how to avoid vec collection to maintain exact size iterator
self.inputs.iter().filter_map(move |(input, original_amount)| {
let amount = original_amount.mul(item_durability).div_floor(Item::MAX_DURABILITY);
// If original repair recipe consumed ingredients, but item not damaged enough to actually need to consume item, remove item as requirement.
if *original_amount > 0 && amount == 0 {
None
} else {
Some((input, amount))
}
}).collect::<Vec<_>>().into_iter()
self.inputs
.iter()
.filter_map(move |(input, original_amount)| {
let amount = (original_amount * item_durability) / Item::MAX_DURABILITY;
// If original repair recipe consumed ingredients, but item not damaged enough
// to actually need to consume item, remove item as requirement.
if *original_amount > 0 && amount == 0 {
None
} else {
Some((input, amount))
}
})
}
}
@ -1068,7 +1066,6 @@ impl assets::Compound for RepairRecipeBook {
cache: assets::AnyCache,
specifier: &assets::SharedString,
) -> Result<Self, assets::BoxedError> {
#[inline]
fn load_recipe_input(
(input, amount): &(RawRecipeInput, u32),
) -> Result<(RecipeInput, u32), assets::Error> {

View File

@ -515,7 +515,7 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, last_change: Healt
if let Some(mut inventory) = state.ecs().write_storage::<Inventory>().get_mut(entity) {
let ability_map = state.ecs().read_resource::<AbilityMap>();
let msm = state.ecs().read_resource::<MaterialStatManifest>();
inventory.apply_durability(&ability_map, &msm);
inventory.damage_items(&ability_map, &msm);
}
if should_delete {

View File

@ -1747,7 +1747,7 @@ impl<'a> Widget for Crafting<'a> {
None => None,
} {
if let Some(recipe) = self.client.repair_recipe_book().repair_recipe(item) {
iter_d = recipe.inputs(item);
iter_d = recipe.inputs(item).collect::<Vec<_>>().into_iter();
&mut iter_d as &mut dyn ExactSizeIterator<Item = _>
} else {
iter_b = core::iter::empty();