From ca879173be6e7c5ac9f5093485766f4cc199a4b0 Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 3 Aug 2022 21:31:39 -0400 Subject: [PATCH] Added a repair equipped and a repair all button. Cleaned up some hacks that used to exist. --- assets/voxygen/i18n/en/hud/crafting.ftl | 3 + common/src/comp/inventory/item/mod.rs | 12 +- common/src/comp/inventory/loadout.rs | 20 +++- common/src/comp/inventory/mod.rs | 16 ++- common/src/recipe.rs | 2 +- voxygen/src/hud/crafting.rs | 141 +++++++++++++++++++----- voxygen/src/hud/mod.rs | 9 +- voxygen/src/scene/terrain/watcher.rs | 2 +- 8 files changed, 158 insertions(+), 47 deletions(-) diff --git a/assets/voxygen/i18n/en/hud/crafting.ftl b/assets/voxygen/i18n/en/hud/crafting.ftl index 7c356bf048..0d61188054 100644 --- a/assets/voxygen/i18n/en/hud/crafting.ftl +++ b/assets/voxygen/i18n/en/hud/crafting.ftl @@ -3,6 +3,9 @@ hud-crafting-recipes = Recipes hud-crafting-ingredients = Ingredients: hud-crafting-craft = Craft hud-crafting-craft_all = Craft All +hud-crafting-repair = Repair +hud-crafting-repair_equipped = Repair Equipped +hud-crafting-repair_all = Repair All hud-crafting-tool_cata = Requires: hud-crafting-req_crafting_station = Requires: hud-crafting-anvil = Anvil diff --git a/common/src/comp/inventory/item/mod.rs b/common/src/comp/inventory/item/mod.rs index beb642bf50..b00bf7a19c 100644 --- a/common/src/comp/inventory/item/mod.rs +++ b/common/src/comp/inventory/item/mod.rs @@ -1222,10 +1222,13 @@ impl Item { } } - pub fn apply_durability(&mut self) { + pub fn apply_durability(&mut self, ability_map: &AbilityMap, msm: &MaterialStatManifest) { if let Some(durability) = &mut self.durability { *durability += 1; } + // Update item state after applying durability because stats have potential to + // change from different durability + self.update_item_state(ability_map, msm); } pub fn persistence_durability(&self) -> Option { @@ -1244,7 +1247,12 @@ impl Item { } } - pub fn reset_durability(&mut self) { self.durability = self.durability.map(|_| 0); } + pub fn reset_durability(&mut self, ability_map: &AbilityMap, msm: &MaterialStatManifest) { + self.durability = self.durability.map(|_| 0); + // Update item state after applying durability because stats have potential to + // change from different durability + self.update_item_state(ability_map, msm); + } #[cfg(test)] pub fn create_test_item_from_kind(kind: ItemKind) -> Self { diff --git a/common/src/comp/inventory/loadout.rs b/common/src/comp/inventory/loadout.rs index 25d45716f3..08192c8c18 100644 --- a/common/src/comp/inventory/loadout.rs +++ b/common/src/comp/inventory/loadout.rs @@ -311,6 +311,12 @@ impl Loadout { self.slots.iter().filter_map(|x| x.slot.as_ref()) } + pub(super) fn items_with_slot(&self) -> impl Iterator { + self.slots + .iter() + .filter_map(|x| x.slot.as_ref().map(|i| (x.equip_slot, i))) + } + /// Checks that a slot can hold a given item pub(super) fn slot_can_hold( &self, @@ -428,23 +434,25 @@ impl Loadout { .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(); - // Update item state after applying durability because stats have potential to - // change from different durability - item.update_item_state(ability_map, msm); + item.apply_durability(ability_map, msm); } }) } /// Resets durability of item in specified slot - pub(super) fn repair_item_at_slot(&mut self, equip_slot: EquipSlot) { + pub(super) fn repair_item_at_slot( + &mut self, + equip_slot: EquipSlot, + ability_map: &item::tool::AbilityMap, + msm: &item::MaterialStatManifest, + ) { if let Some(item) = self .slots .iter_mut() .find(|slot| slot.equip_slot == equip_slot) .and_then(|slot| slot.slot.as_mut()) { - item.reset_durability(); + item.reset_durability(ability_map, msm); } } } diff --git a/common/src/comp/inventory/mod.rs b/common/src/comp/inventory/mod.rs index f10ac9f4c4..1965786433 100644 --- a/common/src/comp/inventory/mod.rs +++ b/common/src/comp/inventory/mod.rs @@ -605,6 +605,10 @@ impl Inventory { pub fn equipped_items(&self) -> impl Iterator { self.loadout.items() } + pub fn equipped_items_with_slot(&self) -> impl Iterator { + self.loadout.items_with_slot() + } + /// Replaces the loadout item (if any) in the given EquipSlot with the /// provided item, returning the item that was previously in the slot. pub fn replace_loadout_item( @@ -889,15 +893,21 @@ impl Inventory { } /// Resets durability of item in specified slot - pub fn repair_item_at_slot(&mut self, slot: Slot) { + pub fn repair_item_at_slot( + &mut self, + slot: Slot, + ability_map: &item::tool::AbilityMap, + msm: &item::MaterialStatManifest, + ) { match slot { Slot::Inventory(invslot) => { if let Some(Some(item)) = self.slot_mut(invslot).map(Option::as_mut) { - item.reset_durability(); + item.reset_durability(ability_map, msm); } }, Slot::Equip(equip_slot) => { - self.loadout.repair_item_at_slot(equip_slot); + self.loadout + .repair_item_at_slot(equip_slot, ability_map, msm); }, } } diff --git a/common/src/recipe.rs b/common/src/recipe.rs index 060d257067..0c6727f82d 100644 --- a/common/src/recipe.rs +++ b/common/src/recipe.rs @@ -1048,7 +1048,7 @@ impl RepairRecipeBook { } } - inv.repair_item_at_slot(item); + inv.repair_item_at_slot(item, ability_map, msm); Ok(()) } else { diff --git a/voxygen/src/hud/crafting.rs b/voxygen/src/hud/crafting.rs index 15eb730e56..7585fca117 100644 --- a/voxygen/src/hud/crafting.rs +++ b/voxygen/src/hud/crafting.rs @@ -86,6 +86,7 @@ widget_ids! { dismantle_title, dismantle_img, dismantle_txt, + repair_buttons[], craft_slots[], modular_art, modular_desc_txt, @@ -124,7 +125,7 @@ pub struct CraftingShow { pub crafting_search_key: Option, pub craft_sprite: Option<(Vec3, SpriteKind)>, pub salvage: bool, - pub repair: bool, + pub initialize_repair: bool, // TODO: Maybe try to do something that doesn't need to allocate? pub recipe_inputs: HashMap, } @@ -136,7 +137,7 @@ impl Default for CraftingShow { crafting_search_key: None, craft_sprite: None, salvage: false, - repair: false, + initialize_repair: false, recipe_inputs: HashMap::new(), } } @@ -212,7 +213,6 @@ pub enum CraftingTab { Utility, Glider, Dismantle, - Repair, } impl CraftingTab { @@ -229,7 +229,6 @@ impl CraftingTab { CraftingTab::Bag => "hud-crafting-tabs-bag", CraftingTab::ProcessedMaterial => "hud-crafting-tabs-processed_material", CraftingTab::Dismantle => "hud-crafting-tabs-dismantle", - CraftingTab::Repair => "hud-crafting-tabs-repair", } } @@ -247,14 +246,13 @@ impl CraftingTab { CraftingTab::ProcessedMaterial => imgs.icon_processed_material, // These tabs are never shown, so using not found is fine CraftingTab::Dismantle => imgs.not_found, - CraftingTab::Repair => imgs.not_found, } } fn satisfies(self, recipe: &Recipe) -> bool { let (item, _count) = &recipe.output; match self { - CraftingTab::All | CraftingTab::Dismantle | CraftingTab::Repair => true, + CraftingTab::All | CraftingTab::Dismantle => true, CraftingTab::Food => item.tags().contains(&ItemTag::Food), CraftingTab::Armor => match &*item.kind() { ItemKind::Armor(_) => !item.tags().contains(&ItemTag::Bag), @@ -325,6 +323,16 @@ impl<'a> Widget for Crafting<'a> { let mut events = Vec::new(); + // Handle any initialization + // TODO: Replace with struct instead of making assorted booleans once there is + // more than 1 field. + if self.show.crafting_fields.initialize_repair { + state.update(|s| { + s.selected_recipe = Some(String::from("veloren.core.pseudo_recipe.repair")) + }); + } + self.show.crafting_fields.initialize_repair = false; + // Tooltips let item_tooltip = ItemTooltip::new( { @@ -570,7 +578,7 @@ impl<'a> Widget for Crafting<'a> { ); pseudo_entries.insert( String::from("veloren.core.pseudo_recipe.repair"), - (&repair_recipe, "Repair Equipment", CraftingTab::Repair), + (&repair_recipe, "Repair Equipment", CraftingTab::All), ); pseudo_entries }; @@ -1356,6 +1364,13 @@ impl<'a> Widget for Crafting<'a> { s.ids.craft_slots.resize(1, &mut ui.widget_id_generator()); }); } + if state.ids.repair_buttons.len() < 2 { + state.update(|s| { + s.ids + .repair_buttons + .resize(2, &mut ui.widget_id_generator()); + }); + } // Slot for item to be repaired let repair_slot = CraftSlot { @@ -1367,7 +1382,7 @@ impl<'a> Widget for Crafting<'a> { let repair_slot_widget = slot_maker .fabricate(repair_slot, [40.0; 2]) - .mid_top_with_margin_on(state.ids.align_ing, 20.0) + .top_left_with_margins_on(state.ids.align_ing, 20.0, 40.0) .parent(state.ids.align_ing); if let Some(item) = repair_slot.item(self.inventory) { @@ -1395,16 +1410,85 @@ impl<'a> Widget for Crafting<'a> { .set(state.ids.craft_slots[0], ui); } - let can_perform = repair_slot.item(self.inventory).map_or(false, |item| { - self.client.repair_recipe_book().repair_recipe(item).map_or( - false, - |recipe| { - recipe - .inventory_contains_ingredients(item, self.inventory) - .is_ok() - }, + 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) + && self.client.repair_recipe_book().repair_recipe(item).map_or( + false, + |recipe| { + recipe + .inventory_contains_ingredients(item, self.inventory) + .is_ok() + }, + ) + }; + + // Repair equipped button + if Button::image(self.imgs.button) + .w_h(105.0, 25.0) + .hover_image(self.imgs.button_hover) + .press_image(self.imgs.button_press) + .label( + &self + .localized_strings + .get_msg("hud-crafting-repair_equipped"), ) - }); + .label_y(conrod_core::position::Relative::Scalar(1.0)) + .label_color(TEXT_COLOR) + .label_font_size(self.fonts.cyri.scale(12)) + .label_font_id(self.fonts.cyri.conrod_id) + .image_color(TEXT_COLOR) + .top_right_with_margins_on(state.ids.align_ing, 20.0, 20.0) + .set(state.ids.repair_buttons[0], ui) + .was_clicked() + { + self.inventory + .equipped_items_with_slot() + .filter(|(_, item)| can_repair(item)) + .for_each(|(slot, _)| { + events.push(Event::RepairItem { + slot: Slot::Equip(slot), + }); + }) + } + + // Repair all button + if Button::image(self.imgs.button) + .w_h(105.0, 25.0) + .hover_image(self.imgs.button_hover) + .press_image(self.imgs.button_press) + .label(&self.localized_strings.get_msg("hud-crafting-repair_all")) + .label_y(conrod_core::position::Relative::Scalar(1.0)) + .label_color(TEXT_COLOR) + .label_font_size(self.fonts.cyri.scale(12)) + .label_font_id(self.fonts.cyri.conrod_id) + .image_color(TEXT_COLOR) + .mid_bottom_with_margin_on(state.ids.repair_buttons[0], -45.0) + .set(state.ids.repair_buttons[1], ui) + .was_clicked() + { + self.inventory + .equipped_items_with_slot() + .filter(|(_, item)| can_repair(item)) + .for_each(|(slot, _)| { + events.push(Event::RepairItem { + slot: Slot::Equip(slot), + }); + }); + self.inventory + .slots_with_id() + .filter(|(_, item)| item.as_ref().map_or(false, |i| can_repair(i))) + .for_each(|(slot, _)| { + events.push(Event::RepairItem { + slot: Slot::Inventory(slot), + }); + }); + } + + let can_perform = repair_slot + .item(self.inventory) + .map_or(false, |item| can_repair(item)); (repair_slot.slot, None, can_perform) }, @@ -1413,17 +1497,20 @@ impl<'a> Widget for Crafting<'a> { // Craft button if Button::image(self.imgs.button) .w_h(105.0, 25.0) - .hover_image(if can_perform { - self.imgs.button_hover - } else { - self.imgs.button + .hover_image( + can_perform + .then_some(self.imgs.button_hover) + .unwrap_or(self.imgs.button), + ) + .press_image( + can_perform + .then_some(self.imgs.button_press) + .unwrap_or(self.imgs.button), + ) + .label(&match recipe_kind { + RecipeKind::Repair => self.localized_strings.get_msg("hud-crafting-repair"), + _ => self.localized_strings.get_msg("hud-crafting-craft"), }) - .press_image(if can_perform { - self.imgs.button_press - } else { - self.imgs.button - }) - .label(&self.localized_strings.get_msg("hud-crafting-craft")) .label_y(conrod_core::position::Relative::Scalar(1.0)) .label_color(if can_perform { TEXT_COLOR diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index b9462d8580..21350db11e 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -922,7 +922,6 @@ impl Show { self.bag = open; self.map = false; self.crafting_fields.salvage = false; - self.crafting_fields.repair = false; if !open { self.crafting = false; @@ -947,7 +946,6 @@ impl Show { self.bag = false; self.crafting = false; self.crafting_fields.salvage = false; - self.crafting_fields.repair = false; self.social = false; self.quest = false; self.diary = false; @@ -984,7 +982,6 @@ impl Show { } self.crafting = open; self.crafting_fields.salvage = false; - self.crafting_fields.repair = false; self.crafting_fields.recipe_inputs = HashMap::new(); self.bag = open; self.map = false; @@ -1004,10 +1001,10 @@ impl Show { self.crafting_fields.craft_sprite, Some((_, SpriteKind::DismantlingBench)) ) && matches!(tab, CraftingTab::Dismantle); - self.crafting_fields.repair = matches!( + self.crafting_fields.initialize_repair = matches!( self.crafting_fields.craft_sprite, Some((_, SpriteKind::RepairBench)) - ) && matches!(tab, CraftingTab::Repair); + ); } fn diary(&mut self, open: bool) { @@ -1016,7 +1013,6 @@ impl Show { self.quest = false; self.crafting = false; self.crafting_fields.salvage = false; - self.crafting_fields.repair = false; self.bag = false; self.map = false; self.diary_fields = diary::DiaryShow::default(); @@ -1037,7 +1033,6 @@ impl Show { self.quest = false; self.crafting = false; self.crafting_fields.salvage = false; - self.crafting_fields.repair = false; self.diary = false; self.want_grab = !self.any_window_requires_cursor(); } diff --git a/voxygen/src/scene/terrain/watcher.rs b/voxygen/src/scene/terrain/watcher.rs index d73ee86a95..5b0ec09b90 100644 --- a/voxygen/src/scene/terrain/watcher.rs +++ b/voxygen/src/scene/terrain/watcher.rs @@ -166,7 +166,7 @@ impl BlocksOfInterest { interactables.push((pos, Interaction::Craft(CraftingTab::Dismantle))) }, Some(SpriteKind::RepairBench) => { - interactables.push((pos, Interaction::Craft(CraftingTab::Repair))) + interactables.push((pos, Interaction::Craft(CraftingTab::All))) }, _ => {}, },