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))
}
@ -1294,7 +1294,7 @@ pub trait ItemDesc {
fn is_modular(&self) -> bool;
fn components(&self) -> &[Item];
fn has_durability(&self) -> bool;
fn durability(&self) -> Option<u32>;
fn durability_lost(&self) -> Option<u32>;
fn stats_durability_multiplier(&self) -> DurabilityMultiplier;
fn tool_info(&self) -> Option<ToolKind> {
@ -1327,7 +1327,7 @@ impl ItemDesc for Item {
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 {
self.stats_durability_multiplier()
@ -1359,7 +1359,7 @@ impl ItemDesc for ItemDef {
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) }
}
@ -1399,7 +1399,7 @@ impl<'a, T: ItemDesc + ?Sized> ItemDesc for &'a T {
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 {
(*self).stats_durability_multiplier()

View File

@ -20,7 +20,10 @@ pub(super) const UNEQUIP_TRACKING_DURATION: f64 = 60.0;
pub struct Loadout {
slots: Vec<LoadoutSlot>,
// 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!
@ -103,22 +106,26 @@ impl Loadout {
item: Option<Item>,
time: Time,
) -> Option<Item> {
self.recently_unequipped_items.retain(|def, unequip_time| {
let item_reequipped = item
.as_ref()
.map_or(false, |i| def.as_ref() == i.item_definition_id());
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
!(item_reequipped || old_unequip)
});
if let Some(item_def_id) = item.as_ref().map(|item| item.item_definition_id()) {
if let Some((_unequip_time, count)) = self
.recently_unequipped_items
.get_mut(&item_def_id.to_owned())
{
*count = count.saturating_sub(1);
}
}
self.cull_recently_unequipped_items(time);
let unequipped_item = self
.slots
.iter_mut()
.find(|x| x.equip_slot == equip_slot)
.and_then(|x| core::mem::replace(&mut x.slot, item));
if let Some(unequipped_item) = unequipped_item.as_ref() {
self.recently_unequipped_items
.insert(unequipped_item.item_definition_id().to_owned(), time);
let entry = self
.recently_unequipped_items
.entry(unequipped_item.item_definition_id().to_owned())
.or_insert((time, 0));
*entry = (time, entry.1 + 1);
}
unequipped_item
}
@ -485,6 +492,20 @@ impl Loadout {
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)]

View File

@ -899,39 +899,47 @@ impl Inventory {
time: Time,
) {
self.loadout.damage_items(ability_map, msm);
self.loadout
.recently_unequipped_items
.retain(|_item, unequip_time| {
time.0 - unequip_time.0 <= loadout::UNEQUIP_TRACKING_DURATION
});
let inv_slots = self
.loadout
.recently_unequipped_items
.keys()
.filter_map(|item_def_id| {
self.slots_with_id()
.find(|&(_, item)| {
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)
self.loadout.cull_recently_unequipped_items(time);
let slots = self
.slots_with_id()
.filter(|(_slot, item)| {
item.as_ref().map_or(false, |item| {
item.durability_lost()
.map_or(false, |dur| dur < Item::MAX_DURABILITY)
&& self
.loadout
.recently_unequipped_items
.contains_key(&item.item_definition_id().to_owned())
})
})
.map(|(slot, _item)| slot)
.collect::<Vec<_>>();
for inv_slot in inv_slots.iter() {
if let Some(Some(item)) = self.slot_mut(*inv_slot) {
if item.has_durability() {
slots.into_iter().for_each(|slot| {
let slot = if let Some(Some(item)) = self.slot(slot) {
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);
}
}
}
});
}
/// 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)> {
let item_durability = item.durability().unwrap_or(0);
let item_durability = item.durability_lost().unwrap_or(0);
self.inputs
.iter()
.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
let mut trades = ecs.write_resource::<Trades>();
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) {
// Notify attacked entity
notify_trade_party(entity);
if let Some(trade) = trades.entity_trades.get(uid).copied() {
trades
.decline_trade(trade, *uid)
.and_then(|uid| ecs.entity_from_uid(uid.0))
.map(|entity| {
// Notify person trading with attacked person
notify_trade_party(entity)
.map(|entity_b| {
// Notify both parties that the trade ended
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 {
index: 0,
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,
};
@ -1413,7 +1413,7 @@ impl<'a> Widget for Crafting<'a> {
let can_repair = |item: &Item| {
// Check that item needs to be repaired, and that inventory has sufficient
// 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(
false,
|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
pub fn item_durability(item: &dyn ItemDesc) -> Option<u32> {
let durability = item
.durability()
.durability_lost()
.or_else(|| item.has_durability().then_some(0));
durability.map(|d| Item::MAX_DURABILITY - d)
}

View File

@ -670,7 +670,7 @@ impl<'a> Widget for ItemTooltip<'a> {
);
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(
format!(
"{} : {}/{}",