diff --git a/CHANGELOG.md b/CHANGELOG.md index 18874df232..6c542ab699 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,8 +10,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added a setting to always show health and energy bars +- Added a crafting station icon to the crafting menu sidebar for items that could be crafted at a crafting station ### Changed + - Made dungeon tiers 3, 4, and 5 more common ### Removed @@ -659,8 +661,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The `/object` command can create any object in comp::object::Body - The `/help` command takes an optional argument. `/help /sudo` will show you information about only the sudo command. -### Removed - ## [0.5.0] - 2020-01-31 ### Added @@ -799,7 +799,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Only send physics updates for entities within view distance - Fix for headphones and invalid device parameters - Fixed asset names for consistancy -- Fixes animals jumping after their target no matter how far\ +- Fixes animals jumping after their target no matter how far - Improved SFX in caves - Better combat, movement, and animations - Many performance optimizations diff --git a/assets/voxygen/element/ui/crafting/crafting.png b/assets/voxygen/element/ui/crafting/crafting.png index 495d9ab25d..43b9ce735e 100644 Binary files a/assets/voxygen/element/ui/crafting/crafting.png and b/assets/voxygen/element/ui/crafting/crafting.png differ diff --git a/assets/voxygen/element/ui/crafting/crafting_frame.png b/assets/voxygen/element/ui/crafting/crafting_frame.png index 93ae9d51ff..71fdd6e1c6 100644 Binary files a/assets/voxygen/element/ui/crafting/crafting_frame.png and b/assets/voxygen/element/ui/crafting/crafting_frame.png differ diff --git a/voxygen/src/hud/crafting.rs b/voxygen/src/hud/crafting.rs index dd2e917b9c..32907a42b0 100644 --- a/voxygen/src/hud/crafting.rs +++ b/voxygen/src/hud/crafting.rs @@ -55,6 +55,7 @@ widget_ids! { recipe_list_btns[], recipe_list_labels[], recipe_list_quality_indicators[], + recipe_list_materials_indicators[], recipe_img_frame[], recipe_img[], ingredients[], @@ -285,7 +286,7 @@ impl<'a> Widget for Crafting<'a> { Image::new(self.imgs.crafting_window) .bottom_right_with_margins_on(ui.window, 308.0, 450.0) .color(Some(UI_MAIN)) - .w_h(456.0, 460.0) + .w_h(470.0, 460.0) .set(state.ids.window, ui); // Window Image::new(self.imgs.crafting_frame) @@ -321,7 +322,7 @@ impl<'a> Widget for Crafting<'a> { .set(state.ids.title_main, ui); // Alignment - Rectangle::fill_with([170.0, 378.0], color::TRANSPARENT) + Rectangle::fill_with([184.0, 378.0], color::TRANSPARENT) .top_left_with_margins_on(state.ids.window_frame, 74.0, 5.0) .scroll_kids_vertically() .set(state.ids.align_rec, ui); @@ -406,8 +407,9 @@ impl<'a> Widget for Crafting<'a> { .set(state.ids.category_imgs[i], ui); } - // First available recipes, then unavailable ones, each alphabetically - // In the triples, "name" is the recipe book key, and "recipe.output.0.name()" + // First available recipes, then ones with available materials, + // then unavailable ones, each sorted by quality and then alphabetically + // In the tuple, "name" is the recipe book key, and "recipe.output.0.name()" // is the display name (as stored in the item descriptors) let mut ordered_recipes: Vec<_> = self .client @@ -425,6 +427,7 @@ impl<'a> Widget for Crafting<'a> { } }) .map(|(name, recipe)| { + let has_materials = self.client.available_recipes().get(name.as_str()).is_some(); let is_craftable = self.client .available_recipes() @@ -434,11 +437,16 @@ impl<'a> Widget for Crafting<'a> { Some(cs) == self.show.craft_sprite.map(|(_, s)| s) }) }); - (name, recipe, is_craftable) + (name, recipe, is_craftable, has_materials) }) .collect(); - ordered_recipes.sort_by_key(|(_, recipe, state)| { - (!state, recipe.output.0.quality(), recipe.output.0.name()) + ordered_recipes.sort_by_key(|(_, recipe, is_craftable, has_materials)| { + ( + !is_craftable, + !has_materials, + recipe.output.0.quality(), + recipe.output.0.name(), + ) }); // Recipe list @@ -466,9 +474,18 @@ impl<'a> Widget for Crafting<'a> { ) }); } - for (i, (name, recipe, is_craftable)) in ordered_recipes + if state.ids.recipe_list_materials_indicators.len() < self.client.recipe_book().iter().len() + { + state.update(|state| { + state.ids.recipe_list_materials_indicators.resize( + self.client.recipe_book().iter().len(), + &mut ui.widget_id_generator(), + ) + }); + } + for (i, (name, recipe, is_craftable, has_materials)) in ordered_recipes .into_iter() - .filter(|(_, recipe, _)| self.show.crafting_tab.satisfies(recipe)) + .filter(|(_, recipe, _, _)| self.show.crafting_tab.satisfies(recipe)) .enumerate() { let button = Button::image(if state.selected_recipe.as_ref() == Some(name) { @@ -483,7 +500,7 @@ impl<'a> Widget for Crafting<'a> { button.down_from(state.ids.recipe_list_btns[i - 1], 5.0) } }) - .w(157.0) + .w(171.0) .hover_image(self.imgs.selection_hover) .press_image(self.imgs.selection_press) .image_color(color::rgba(1.0, 0.82, 0.27, 1.0)); @@ -496,7 +513,7 @@ impl<'a> Widget for Crafting<'a> { }) .font_size(self.fonts.cyri.scale(12)) .font_id(self.fonts.cyri.conrod_id) - .w(149.0) + .w(163.0) .mid_top_with_margin_on(state.ids.recipe_list_btns[i], 3.0) .graphics_for(state.ids.recipe_list_btns[i]) .center_justify(); @@ -505,9 +522,10 @@ impl<'a> Widget for Crafting<'a> { Dimension::Absolute(y) => y, _ => 0.0, }; + let button_height = (text_height + 7.0).max(20.0); if button - .h((text_height + 7.0).max(20.0)) + .h(button_height) .set(state.ids.recipe_list_btns[i], ui) .was_clicked() { @@ -536,11 +554,39 @@ impl<'a> Widget for Crafting<'a> { Button::image(self.imgs.quality_indicator) .image_color(quality_col) - .h_of(state.ids.recipe_list_btns[i]) - .w(4.0) + .w_h(4.0, button_height) .left_from(state.ids.recipe_list_btns[i], 1.0) .graphics_for(state.ids.recipe_list_btns[i]) .set(state.ids.recipe_list_quality_indicators[i], ui); + + // Sidebar crafting tool icon + if has_materials && !is_craftable { + let station_img = match recipe.craft_sprite { + Some(SpriteKind::Anvil) => Some("Anvil"), + Some(SpriteKind::Cauldron) => Some("Cauldron"), + Some(SpriteKind::CookingPot) => Some("CookingPot"), + Some(SpriteKind::CraftingBench) => Some("CraftingBench"), + Some(SpriteKind::Forge) => Some("Forge"), + Some(SpriteKind::Loom) => Some("Loom"), + Some(SpriteKind::SpinningWheel) => Some("SpinningWheel"), + Some(SpriteKind::TanningRack) => Some("TanningRack"), + _ => None, + }; + + if let Some(station_img_str) = station_img { + Button::image(animate_by_pulse( + &self + .item_imgs + .img_ids_or_not_found_img(Tool(station_img_str.to_string())), + self.pulse, + )) + .image_color(color::LIGHT_RED) + .w_h(button_height - 8.0, button_height - 8.0) + .top_left_with_margins_on(state.ids.recipe_list_btns[i], 4.0, 4.0) + .graphics_for(state.ids.recipe_list_btns[i]) + .set(state.ids.recipe_list_materials_indicators[i], ui); + } + } } // Selected Recipe @@ -913,7 +959,7 @@ impl<'a> Widget for Crafting<'a> { { events.push(Event::SearchRecipe(None)); } - Rectangle::fill([148.0, 20.0]) + Rectangle::fill([162.0, 20.0]) .top_left_with_margins_on(state.ids.btn_close_search, -2.0, 16.0) .hsla(0.0, 0.0, 0.0, 0.7) .depth(1.0) @@ -921,7 +967,7 @@ impl<'a> Widget for Crafting<'a> { .set(state.ids.input_bg_search, ui); if let Some(string) = TextEdit::new(key.as_str()) .top_left_with_margins_on(state.ids.btn_close_search, -2.0, 18.0) - .w_h(124.0, 20.0) + .w_h(138.0, 20.0) .font_id(self.fonts.cyri.conrod_id) .font_size(self.fonts.cyri.scale(14)) .color(TEXT_COLOR)