Addressed review

This commit is contained in:
Sam 2023-04-16 17:31:39 -04:00
parent d0a902d918
commit 7fdfc0e71b
8 changed files with 95 additions and 66 deletions

View File

@ -1213,7 +1213,7 @@ impl Item {
} }
} }
pub fn durability(&self) -> Option<u32> { pub fn durability_lost(&self) -> Option<u32> {
self.durability_lost.map(|x| x.min(Self::MAX_DURABILITY)) self.durability_lost.map(|x| x.min(Self::MAX_DURABILITY))
} }
@ -1294,7 +1294,7 @@ pub trait ItemDesc {
fn is_modular(&self) -> bool; fn is_modular(&self) -> bool;
fn components(&self) -> &[Item]; fn components(&self) -> &[Item];
fn has_durability(&self) -> bool; fn has_durability(&self) -> bool;
fn durability(&self) -> Option<u32>; fn durability_lost(&self) -> Option<u32>;
fn stats_durability_multiplier(&self) -> DurabilityMultiplier; fn stats_durability_multiplier(&self) -> DurabilityMultiplier;
fn tool_info(&self) -> Option<ToolKind> { fn tool_info(&self) -> Option<ToolKind> {
@ -1327,7 +1327,7 @@ impl ItemDesc for Item {
fn has_durability(&self) -> bool { self.has_durability() } fn has_durability(&self) -> bool { self.has_durability() }
fn durability(&self) -> Option<u32> { self.durability() } fn durability_lost(&self) -> Option<u32> { self.durability_lost() }
fn stats_durability_multiplier(&self) -> DurabilityMultiplier { fn stats_durability_multiplier(&self) -> DurabilityMultiplier {
self.stats_durability_multiplier() self.stats_durability_multiplier()
@ -1359,7 +1359,7 @@ impl ItemDesc for ItemDef {
self.kind().has_durability() && self.quality != Quality::Debug self.kind().has_durability() && self.quality != Quality::Debug
} }
fn durability(&self) -> Option<u32> { None } fn durability_lost(&self) -> Option<u32> { None }
fn stats_durability_multiplier(&self) -> DurabilityMultiplier { DurabilityMultiplier(1.0) } fn stats_durability_multiplier(&self) -> DurabilityMultiplier { DurabilityMultiplier(1.0) }
} }
@ -1399,7 +1399,7 @@ impl<'a, T: ItemDesc + ?Sized> ItemDesc for &'a T {
fn has_durability(&self) -> bool { (*self).has_durability() } fn has_durability(&self) -> bool { (*self).has_durability() }
fn durability(&self) -> Option<u32> { (*self).durability() } fn durability_lost(&self) -> Option<u32> { (*self).durability_lost() }
fn stats_durability_multiplier(&self) -> DurabilityMultiplier { fn stats_durability_multiplier(&self) -> DurabilityMultiplier {
(*self).stats_durability_multiplier() (*self).stats_durability_multiplier()

View File

@ -20,7 +20,10 @@ pub(super) const UNEQUIP_TRACKING_DURATION: f64 = 60.0;
pub struct Loadout { pub struct Loadout {
slots: Vec<LoadoutSlot>, slots: Vec<LoadoutSlot>,
// Includes time that item was unequipped at // Includes time that item was unequipped at
pub(super) recently_unequipped_items: HashMap<ItemDefinitionIdOwned, Time>, #[serde(skip)]
// Tracks time unequipped at and number that have been unequipped (for things like dual
// wielding, rings, or other future cases)
pub(super) recently_unequipped_items: HashMap<ItemDefinitionIdOwned, (Time, u8)>,
} }
/// NOTE: Please don't derive a PartialEq Instance for this; that's broken! /// NOTE: Please don't derive a PartialEq Instance for this; that's broken!
@ -103,22 +106,26 @@ impl Loadout {
item: Option<Item>, item: Option<Item>,
time: Time, time: Time,
) -> Option<Item> { ) -> Option<Item> {
self.recently_unequipped_items.retain(|def, unequip_time| { if let Some(item_def_id) = item.as_ref().map(|item| item.item_definition_id()) {
let item_reequipped = item if let Some((_unequip_time, count)) = self
.as_ref() .recently_unequipped_items
.map_or(false, |i| def.as_ref() == i.item_definition_id()); .get_mut(&item_def_id.to_owned())
let old_unequip = time.0 - unequip_time.0 > UNEQUIP_TRACKING_DURATION; {
// Stop tracking item if it is re-equipped or if was unequipped a while ago *count = count.saturating_sub(1);
!(item_reequipped || old_unequip) }
}); }
self.cull_recently_unequipped_items(time);
let unequipped_item = self let unequipped_item = self
.slots .slots
.iter_mut() .iter_mut()
.find(|x| x.equip_slot == equip_slot) .find(|x| x.equip_slot == equip_slot)
.and_then(|x| core::mem::replace(&mut x.slot, item)); .and_then(|x| core::mem::replace(&mut x.slot, item));
if let Some(unequipped_item) = unequipped_item.as_ref() { if let Some(unequipped_item) = unequipped_item.as_ref() {
self.recently_unequipped_items let entry = self
.insert(unequipped_item.item_definition_id().to_owned(), time); .recently_unequipped_items
.entry(unequipped_item.item_definition_id().to_owned())
.or_insert((time, 0));
*entry = (time, entry.1 + 1);
} }
unequipped_item unequipped_item
} }
@ -485,6 +492,20 @@ impl Loadout {
item.reset_durability(ability_map, msm); item.reset_durability(ability_map, msm);
} }
} }
pub(super) fn cull_recently_unequipped_items(&mut self, time: Time) {
for (unequip_time, _count) in self.recently_unequipped_items.values_mut() {
// If somehow time went backwards or faulty unequip time supplied, set unequip
// time to minimum of current time and unequip time
if time.0 < unequip_time.0 {
*unequip_time = time;
}
}
self.recently_unequipped_items
.retain(|_def, (unequip_time, count)| {
(time.0 - unequip_time.0 < UNEQUIP_TRACKING_DURATION) || *count > 0
});
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -899,39 +899,47 @@ impl Inventory {
time: Time, time: Time,
) { ) {
self.loadout.damage_items(ability_map, msm); self.loadout.damage_items(ability_map, msm);
self.loadout self.loadout.cull_recently_unequipped_items(time);
.recently_unequipped_items
.retain(|_item, unequip_time| { let slots = self
time.0 - unequip_time.0 <= loadout::UNEQUIP_TRACKING_DURATION .slots_with_id()
}); .filter(|(_slot, item)| {
let inv_slots = self item.as_ref().map_or(false, |item| {
.loadout item.durability_lost()
.recently_unequipped_items .map_or(false, |dur| dur < Item::MAX_DURABILITY)
.keys() && self
.filter_map(|item_def_id| { .loadout
self.slots_with_id() .recently_unequipped_items
.find(|&(_, item)| { .contains_key(&item.item_definition_id().to_owned())
if let Some(item) = item { })
// Find an item with the matching item definition id and that is not yet
// at maximum durability lost
item.item_definition_id() == *item_def_id
&& item
.durability()
.map_or(true, |dur| dur < Item::MAX_DURABILITY)
} else {
false
}
})
.map(|(slot, _)| slot)
}) })
.map(|(slot, _item)| slot)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
for inv_slot in inv_slots.iter() { slots.into_iter().for_each(|slot| {
if let Some(Some(item)) = self.slot_mut(*inv_slot) { let slot = if let Some(Some(item)) = self.slot(slot) {
if item.has_durability() { if let Some((_unequip_time, count)) = self
.loadout
.recently_unequipped_items
.get_mut(&item.item_definition_id().to_owned())
{
if *count > 0 {
*count -= 1;
Some(slot)
} else {
None
}
} else {
None
}
} else {
None
};
if let Some(slot) = slot {
if let Some(Some(item)) = self.slot_mut(slot) {
item.increment_damage(ability_map, msm); item.increment_damage(ability_map, msm);
} }
} }
} });
} }
/// Resets durability of item in specified slot /// Resets durability of item in specified slot

View File

@ -976,7 +976,7 @@ impl RepairRecipe {
} }
pub fn inputs(&self, item: &Item) -> impl Iterator<Item = (&RecipeInput, u32)> { pub fn inputs(&self, item: &Item) -> impl Iterator<Item = (&RecipeInput, u32)> {
let item_durability = item.durability().unwrap_or(0); let item_durability = item.durability_lost().unwrap_or(0);
self.inputs self.inputs
.iter() .iter()
.filter_map(move |(input, original_amount)| { .filter_map(move |(input, original_amount)| {

View File

@ -1414,28 +1414,28 @@ pub fn handle_entity_attacked_hook(server: &Server, entity: EcsEntity) {
// If entity was in an active trade, cancel it // If entity was in an active trade, cancel it
let mut trades = ecs.write_resource::<Trades>(); let mut trades = ecs.write_resource::<Trades>();
let uids = ecs.read_storage::<Uid>(); let uids = ecs.read_storage::<Uid>();
let clients = ecs.read_storage::<Client>();
let mut agents = ecs.write_storage::<Agent>();
let mut notify_trade_party = |entity| {
if let Some(client) = clients.get(entity) {
client.send_fallible(ServerGeneral::FinishedTrade(TradeResult::Declined));
}
if let Some(agent) = agents.get_mut(entity) {
agent
.inbox
.push_back(AgentEvent::FinishedTrade(TradeResult::Declined));
}
};
if let Some(uid) = uids.get(entity) { if let Some(uid) = uids.get(entity) {
// Notify attacked entity
notify_trade_party(entity);
if let Some(trade) = trades.entity_trades.get(uid).copied() { if let Some(trade) = trades.entity_trades.get(uid).copied() {
trades trades
.decline_trade(trade, *uid) .decline_trade(trade, *uid)
.and_then(|uid| ecs.entity_from_uid(uid.0)) .and_then(|uid| ecs.entity_from_uid(uid.0))
.map(|entity| { .map(|entity_b| {
// Notify person trading with attacked person // Notify both parties that the trade ended
notify_trade_party(entity) let clients = ecs.read_storage::<Client>();
let mut agents = ecs.write_storage::<Agent>();
let mut notify_trade_party = |entity| {
if let Some(client) = clients.get(entity) {
client
.send_fallible(ServerGeneral::FinishedTrade(TradeResult::Declined));
}
if let Some(agent) = agents.get_mut(entity) {
agent
.inbox
.push_back(AgentEvent::FinishedTrade(TradeResult::Declined));
}
};
notify_trade_party(entity);
notify_trade_party(entity_b);
}); });
} }
} }

View File

@ -1376,7 +1376,7 @@ impl<'a> Widget for Crafting<'a> {
let repair_slot = CraftSlot { let repair_slot = CraftSlot {
index: 0, index: 0,
slot: self.show.crafting_fields.recipe_inputs.get(&0).copied(), slot: self.show.crafting_fields.recipe_inputs.get(&0).copied(),
requirement: |item, _, _| item.durability().map_or(false, |d| d > 0), requirement: |item, _, _| item.durability_lost().map_or(false, |d| d > 0),
info: None, info: None,
}; };
@ -1413,7 +1413,7 @@ impl<'a> Widget for Crafting<'a> {
let can_repair = |item: &Item| { let can_repair = |item: &Item| {
// Check that item needs to be repaired, and that inventory has sufficient // Check that item needs to be repaired, and that inventory has sufficient
// materials to repair // materials to repair
item.durability().map_or(false, |d| d > 0) item.durability_lost().map_or(false, |d| d > 0)
&& self.client.repair_recipe_book().repair_recipe(item).map_or( && self.client.repair_recipe_book().repair_recipe(item).map_or(
false, false,
|recipe| { |recipe| {

View File

@ -359,7 +359,7 @@ pub fn protec2string(stat: Protection) -> String {
/// Gets the durability of an item in a format more intuitive for UI /// Gets the durability of an item in a format more intuitive for UI
pub fn item_durability(item: &dyn ItemDesc) -> Option<u32> { pub fn item_durability(item: &dyn ItemDesc) -> Option<u32> {
let durability = item let durability = item
.durability() .durability_lost()
.or_else(|| item.has_durability().then_some(0)); .or_else(|| item.has_durability().then_some(0));
durability.map(|d| Item::MAX_DURABILITY - d) durability.map(|d| Item::MAX_DURABILITY - d)
} }

View File

@ -670,7 +670,7 @@ impl<'a> Widget for ItemTooltip<'a> {
); );
if item.has_durability() { if item.has_durability() {
let durability = Item::MAX_DURABILITY - item.durability().unwrap_or(0); let durability = Item::MAX_DURABILITY - item.durability_lost().unwrap_or(0);
stat_text( stat_text(
format!( format!(
"{} : {}/{}", "{} : {}/{}",