Added a repair equipped and a repair all button. Cleaned up some hacks that used to exist.

This commit is contained in:
Sam 2022-08-03 21:31:39 -04:00
parent 658de93c59
commit ca879173be
8 changed files with 158 additions and 47 deletions

View File

@ -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

View File

@ -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<NonZeroU32> {
@ -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 {

View File

@ -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<Item = (EquipSlot, &Item)> {
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);
}
}
}

View File

@ -605,6 +605,10 @@ impl Inventory {
pub fn equipped_items(&self) -> impl Iterator<Item = &Item> { self.loadout.items() }
pub fn equipped_items_with_slot(&self) -> impl Iterator<Item = (EquipSlot, &Item)> {
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);
},
}
}

View File

@ -1048,7 +1048,7 @@ impl RepairRecipeBook {
}
}
inv.repair_item_at_slot(item);
inv.repair_item_at_slot(item, ability_map, msm);
Ok(())
} else {

View File

@ -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<String>,
pub craft_sprite: Option<(Vec3<i32>, 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<u32, Slot>,
}
@ -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

View File

@ -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();
}

View File

@ -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)))
},
_ => {},
},