mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Initial crafting UI for modular weapons.
This commit is contained in:
parent
bf348b7f43
commit
8fc0138e84
@ -1081,7 +1081,7 @@ impl Client {
|
||||
&mut self,
|
||||
slot_a: InvSlotId,
|
||||
slot_b: InvSlotId,
|
||||
sprite_pos: Vec3<i32>,
|
||||
sprite_pos: Option<Vec3<i32>>,
|
||||
) -> bool {
|
||||
let inventories = self.inventories();
|
||||
let inventory = inventories.get(self.entity());
|
||||
@ -1122,7 +1122,7 @@ impl Client {
|
||||
primary_component,
|
||||
secondary_component,
|
||||
},
|
||||
craft_sprite: Some(sprite_pos),
|
||||
craft_sprite: sprite_pos,
|
||||
},
|
||||
)));
|
||||
true
|
||||
|
@ -760,7 +760,7 @@ impl<'a> Widget for Bag<'a> {
|
||||
true,
|
||||
inventory,
|
||||
&state.bg_ids,
|
||||
self.show.salvage,
|
||||
self.show.crafting_fields.salvage,
|
||||
)
|
||||
.set(state.ids.inventory_scroller, ui);
|
||||
|
||||
|
@ -2,20 +2,24 @@ use super::{
|
||||
get_quality_col,
|
||||
img_ids::{Imgs, ImgsRot},
|
||||
item_imgs::{animate_by_pulse, ItemImgs},
|
||||
slots::{CraftSlot, SlotManager},
|
||||
Show, TEXT_COLOR, TEXT_DULL_RED_COLOR, TEXT_GRAY_COLOR, UI_HIGHLIGHT_0, UI_MAIN,
|
||||
};
|
||||
use crate::ui::{
|
||||
fonts::Fonts, ImageFrame, ItemTooltip, ItemTooltipManager, ItemTooltipable, Tooltip,
|
||||
TooltipManager, Tooltipable,
|
||||
fonts::Fonts,
|
||||
slot::{ContentSize, SlotMaker},
|
||||
ImageFrame, ItemTooltip, ItemTooltipManager, ItemTooltipable, Tooltip, TooltipManager,
|
||||
Tooltipable,
|
||||
};
|
||||
use client::{self, Client};
|
||||
use common::{
|
||||
assets::AssetExt,
|
||||
comp::{
|
||||
comp::inventory::{
|
||||
item::{
|
||||
item_key::ItemKey, ItemDef, ItemDesc, ItemKind, ItemTag, MaterialStatManifest, Quality,
|
||||
TagExampleInfo,
|
||||
item_key::ItemKey, modular::ModularComponent, ItemDef, ItemDesc, ItemKind, ItemTag,
|
||||
MaterialStatManifest, Quality, TagExampleInfo,
|
||||
},
|
||||
slot::InvSlotId,
|
||||
Inventory,
|
||||
},
|
||||
recipe::{Recipe, RecipeInput},
|
||||
@ -27,10 +31,12 @@ use conrod_core::{
|
||||
widget::{self, Button, Image, Rectangle, Scrollbar, Text, TextEdit},
|
||||
widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon,
|
||||
};
|
||||
use hashbrown::HashMap;
|
||||
use i18n::Localization;
|
||||
use std::sync::Arc;
|
||||
use std::{borrow::Cow, sync::Arc};
|
||||
|
||||
use strum::{EnumIter, IntoEnumIterator};
|
||||
use vek::*;
|
||||
|
||||
widget_ids! {
|
||||
pub struct Ids {
|
||||
@ -77,17 +83,43 @@ widget_ids! {
|
||||
dismantle_img,
|
||||
dismantle_txt,
|
||||
dismantle_highlight_txt,
|
||||
modular_inputs[],
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Event {
|
||||
CraftRecipe(String),
|
||||
CraftModularWeapon {
|
||||
primary_slot: InvSlotId,
|
||||
secondary_slot: InvSlotId,
|
||||
},
|
||||
ChangeCraftingTab(CraftingTab),
|
||||
Close,
|
||||
Focus(widget::Id),
|
||||
SearchRecipe(Option<String>),
|
||||
}
|
||||
|
||||
pub struct CraftingShow {
|
||||
pub crafting_tab: CraftingTab,
|
||||
pub crafting_search_key: Option<String>,
|
||||
pub craft_sprite: Option<(Vec3<i32>, SpriteKind)>,
|
||||
pub salvage: bool,
|
||||
// TODO: Maybe try to do something that doesn't need to allocate?
|
||||
pub recipe_inputs: HashMap<u32, InvSlotId>,
|
||||
}
|
||||
|
||||
impl Default for CraftingShow {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
crafting_tab: CraftingTab::All,
|
||||
crafting_search_key: None,
|
||||
craft_sprite: None,
|
||||
salvage: false,
|
||||
recipe_inputs: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WidgetCommon)]
|
||||
pub struct Crafting<'a> {
|
||||
client: &'a Client,
|
||||
@ -97,6 +129,7 @@ pub struct Crafting<'a> {
|
||||
pulse: f32,
|
||||
rot_imgs: &'a ImgsRot,
|
||||
item_tooltip_manager: &'a mut ItemTooltipManager,
|
||||
slot_manager: &'a mut SlotManager,
|
||||
item_imgs: &'a ItemImgs,
|
||||
inventory: &'a Inventory,
|
||||
msm: &'a MaterialStatManifest,
|
||||
@ -115,6 +148,7 @@ impl<'a> Crafting<'a> {
|
||||
pulse: f32,
|
||||
rot_imgs: &'a ImgsRot,
|
||||
item_tooltip_manager: &'a mut ItemTooltipManager,
|
||||
slot_manager: &'a mut SlotManager,
|
||||
item_imgs: &'a ItemImgs,
|
||||
inventory: &'a Inventory,
|
||||
msm: &'a MaterialStatManifest,
|
||||
@ -129,6 +163,7 @@ impl<'a> Crafting<'a> {
|
||||
pulse,
|
||||
rot_imgs,
|
||||
item_tooltip_manager,
|
||||
slot_manager,
|
||||
tooltip_manager,
|
||||
item_imgs,
|
||||
inventory,
|
||||
@ -372,7 +407,7 @@ impl<'a> Widget for Crafting<'a> {
|
||||
)
|
||||
})
|
||||
};
|
||||
let sel_crafting_tab = &self.show.crafting_tab;
|
||||
let sel_crafting_tab = &self.show.crafting_fields.crafting_tab;
|
||||
for (i, crafting_tab) in CraftingTab::iter().enumerate() {
|
||||
if crafting_tab != CraftingTab::Dismantle {
|
||||
let tab_img = crafting_tab.img_id(self.imgs);
|
||||
@ -429,7 +464,7 @@ impl<'a> Widget for Crafting<'a> {
|
||||
// more filters gets added
|
||||
let mut _lower_case_search = String::new();
|
||||
let (search_filter, search_keys) = {
|
||||
if let Some(key) = &self.show.crafting_search_key {
|
||||
if let Some(key) = &self.show.crafting_fields.crafting_search_key {
|
||||
_lower_case_search = key.as_str().to_lowercase();
|
||||
_lower_case_search
|
||||
.split_once(':')
|
||||
@ -448,6 +483,21 @@ impl<'a> Widget for Crafting<'a> {
|
||||
}
|
||||
};
|
||||
|
||||
let (modular_entry_name, modular_entry_recipe) = {
|
||||
let recipe = Recipe {
|
||||
output: (
|
||||
Arc::<ItemDef>::load_expect_cloned("common.items.weapons.empty.empty"),
|
||||
0,
|
||||
),
|
||||
inputs: Vec::new(),
|
||||
craft_sprite: Some(SpriteKind::CraftingBench),
|
||||
};
|
||||
(
|
||||
String::from("veloren.core.pseudo_recipe.modular_weapon"),
|
||||
recipe,
|
||||
)
|
||||
};
|
||||
|
||||
// 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()"
|
||||
@ -490,11 +540,22 @@ impl<'a> Widget for Crafting<'a> {
|
||||
.get(name.as_str())
|
||||
.map_or(false, |cs| {
|
||||
cs.map_or(true, |cs| {
|
||||
Some(cs) == self.show.craft_sprite.map(|(_, s)| s)
|
||||
Some(cs) == self.show.crafting_fields.craft_sprite.map(|(_, s)| s)
|
||||
})
|
||||
});
|
||||
(name, recipe, is_craftable, has_materials)
|
||||
})
|
||||
.chain(
|
||||
matches!(sel_crafting_tab, CraftingTab::Weapon | CraftingTab::All)
|
||||
.then_some((
|
||||
&modular_entry_name,
|
||||
&modular_entry_recipe,
|
||||
self.show.crafting_fields.craft_sprite.map(|(_, s)| s)
|
||||
== modular_entry_recipe.craft_sprite,
|
||||
true,
|
||||
))
|
||||
.into_iter(),
|
||||
)
|
||||
.collect();
|
||||
ordered_recipes.sort_by_key(|(_, recipe, is_craftable, has_materials)| {
|
||||
(
|
||||
@ -506,42 +567,44 @@ impl<'a> Widget for Crafting<'a> {
|
||||
});
|
||||
|
||||
// Recipe list
|
||||
if state.ids.recipe_list_btns.len() < self.client.recipe_book().iter().len() {
|
||||
// 1 added for modular weapon fake recipe entry
|
||||
// TODO: Some better automated way of doing this
|
||||
let recipe_list_length = self.client.recipe_book().iter().len() + 1;
|
||||
if state.ids.recipe_list_btns.len() < recipe_list_length {
|
||||
state.update(|state| {
|
||||
state.ids.recipe_list_btns.resize(
|
||||
self.client.recipe_book().iter().len(),
|
||||
&mut ui.widget_id_generator(),
|
||||
)
|
||||
state
|
||||
.ids
|
||||
.recipe_list_btns
|
||||
.resize(recipe_list_length, &mut ui.widget_id_generator())
|
||||
});
|
||||
}
|
||||
if state.ids.recipe_list_labels.len() < self.client.recipe_book().iter().len() {
|
||||
if state.ids.recipe_list_labels.len() < recipe_list_length {
|
||||
state.update(|state| {
|
||||
state.ids.recipe_list_labels.resize(
|
||||
self.client.recipe_book().iter().len(),
|
||||
&mut ui.widget_id_generator(),
|
||||
)
|
||||
state
|
||||
.ids
|
||||
.recipe_list_labels
|
||||
.resize(recipe_list_length, &mut ui.widget_id_generator())
|
||||
});
|
||||
}
|
||||
if state.ids.recipe_list_quality_indicators.len() < self.client.recipe_book().iter().len() {
|
||||
if state.ids.recipe_list_quality_indicators.len() < recipe_list_length {
|
||||
state.update(|state| {
|
||||
state.ids.recipe_list_quality_indicators.resize(
|
||||
self.client.recipe_book().iter().len(),
|
||||
&mut ui.widget_id_generator(),
|
||||
)
|
||||
state
|
||||
.ids
|
||||
.recipe_list_quality_indicators
|
||||
.resize(recipe_list_length, &mut ui.widget_id_generator())
|
||||
});
|
||||
}
|
||||
if state.ids.recipe_list_materials_indicators.len() < self.client.recipe_book().iter().len()
|
||||
{
|
||||
if state.ids.recipe_list_materials_indicators.len() < recipe_list_length {
|
||||
state.update(|state| {
|
||||
state.ids.recipe_list_materials_indicators.resize(
|
||||
self.client.recipe_book().iter().len(),
|
||||
&mut ui.widget_id_generator(),
|
||||
)
|
||||
state
|
||||
.ids
|
||||
.recipe_list_materials_indicators
|
||||
.resize(recipe_list_length, &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_fields.crafting_tab.satisfies(recipe))
|
||||
.enumerate()
|
||||
{
|
||||
let button = Button::image(if state.selected_recipe.as_ref() == Some(name) {
|
||||
@ -561,7 +624,10 @@ impl<'a> Widget for Crafting<'a> {
|
||||
.press_image(self.imgs.selection_press)
|
||||
.image_color(color::rgba(1.0, 0.82, 0.27, 1.0));
|
||||
|
||||
let recipe_name = recipe.output.0.name();
|
||||
let recipe_name = match name.as_str() {
|
||||
"veloren.core.pseudo_recipe.modular_weapon" => Cow::Borrowed("Modular Weapon"),
|
||||
_ => recipe.output.0.name(),
|
||||
};
|
||||
|
||||
let text = Text::new(&recipe_name)
|
||||
.color(if is_craftable {
|
||||
@ -590,7 +656,10 @@ impl<'a> Widget for Crafting<'a> {
|
||||
if state.selected_recipe.as_ref() == Some(name) {
|
||||
state.update(|s| s.selected_recipe = None);
|
||||
} else {
|
||||
if matches!(self.show.crafting_tab, CraftingTab::Dismantle) {
|
||||
if matches!(
|
||||
self.show.crafting_fields.crafting_tab,
|
||||
CraftingTab::Dismantle
|
||||
) {
|
||||
// If current tab is dismantle, and recipe is selected, change to general
|
||||
// tab, as in dismantle tab recipe gets deselected
|
||||
events.push(Event::ChangeCraftingTab(CraftingTab::All));
|
||||
@ -655,31 +724,153 @@ impl<'a> Widget for Crafting<'a> {
|
||||
|
||||
// Deselect recipe if current tab is dismantle, elsewhere if recipe selected
|
||||
// while dismantling, tab is changed to general
|
||||
if matches!(self.show.crafting_tab, CraftingTab::Dismantle) {
|
||||
if matches!(
|
||||
self.show.crafting_fields.crafting_tab,
|
||||
CraftingTab::Dismantle
|
||||
) {
|
||||
state.update(|s| s.selected_recipe = None);
|
||||
}
|
||||
|
||||
// Selected Recipe
|
||||
if let Some((recipe_name, recipe)) = state
|
||||
.selected_recipe
|
||||
.as_ref()
|
||||
.and_then(|rn| self.client.recipe_book().get(rn.as_str()).map(|r| (rn, r)))
|
||||
{
|
||||
if let Some((recipe_name, recipe)) = match state.selected_recipe.as_deref() {
|
||||
Some("veloren.core.pseudo_recipe.modular_weapon") => {
|
||||
Some((modular_entry_name.as_str(), &modular_entry_recipe))
|
||||
},
|
||||
sel_recipe => {
|
||||
sel_recipe.and_then(|rn| self.client.recipe_book().get(rn).map(|r| (rn, r)))
|
||||
},
|
||||
} {
|
||||
let title = match recipe_name {
|
||||
"veloren.core.pseudo_recipe.modular_weapon" => Cow::Borrowed("Modular Weapon"),
|
||||
_ => recipe.output.0.name(),
|
||||
};
|
||||
// Title
|
||||
Text::new(&recipe.output.0.name())
|
||||
Text::new(&title)
|
||||
.mid_top_with_margin_on(state.ids.align_ing, -22.0)
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.font_size(self.fonts.cyri.scale(14))
|
||||
.color(TEXT_COLOR)
|
||||
.parent(state.ids.window)
|
||||
.set(state.ids.title_ing, ui);
|
||||
let can_perform = self
|
||||
.client
|
||||
|
||||
match recipe_name {
|
||||
"veloren.core.pseudo_recipe.modular_weapon" => {
|
||||
let mut slot_maker = SlotMaker {
|
||||
empty_slot: self.imgs.inv_slot,
|
||||
filled_slot: self.imgs.inv_slot,
|
||||
selected_slot: self.imgs.inv_slot_sel,
|
||||
background_color: Some(UI_MAIN),
|
||||
content_size: ContentSize {
|
||||
width_height_ratio: 1.0,
|
||||
max_fraction: 0.75,
|
||||
},
|
||||
selected_content_scale: 1.067,
|
||||
amount_font: self.fonts.cyri.conrod_id,
|
||||
amount_margins: Vec2::new(-4.0, 0.0),
|
||||
amount_font_size: self.fonts.cyri.scale(12),
|
||||
amount_text_color: TEXT_COLOR,
|
||||
content_source: self.inventory,
|
||||
image_source: self.item_imgs,
|
||||
slot_manager: Some(self.slot_manager),
|
||||
pulse: self.pulse,
|
||||
};
|
||||
|
||||
if state.ids.modular_inputs.len() < 2 {
|
||||
state.update(|s| {
|
||||
s.ids
|
||||
.modular_inputs
|
||||
.resize(2, &mut ui.widget_id_generator());
|
||||
});
|
||||
}
|
||||
|
||||
let primary_slot = CraftSlot {
|
||||
index: 0,
|
||||
invslot: self.show.crafting_fields.recipe_inputs.get(&0).copied(),
|
||||
requirement: |inv, slot| {
|
||||
inv.and_then(|inv| inv.get(slot)).map_or(false, |item| {
|
||||
matches!(
|
||||
&*item.kind(),
|
||||
ItemKind::ModularComponent(
|
||||
ModularComponent::ToolPrimaryComponent { .. }
|
||||
)
|
||||
)
|
||||
})
|
||||
},
|
||||
required_amount: 1,
|
||||
};
|
||||
|
||||
slot_maker
|
||||
.fabricate(primary_slot, [40.0; 2])
|
||||
.top_left_with_margins_on(state.ids.align_ing, 20.0, 20.0)
|
||||
.set(state.ids.modular_inputs[0], ui);
|
||||
|
||||
let secondary_slot = CraftSlot {
|
||||
index: 1,
|
||||
invslot: self.show.crafting_fields.recipe_inputs.get(&1).copied(),
|
||||
requirement: |inv, slot| {
|
||||
inv.and_then(|inv| inv.get(slot)).map_or(false, |item| {
|
||||
matches!(
|
||||
&*item.kind(),
|
||||
ItemKind::ModularComponent(
|
||||
ModularComponent::ToolSecondaryComponent { .. }
|
||||
)
|
||||
)
|
||||
})
|
||||
},
|
||||
required_amount: 1,
|
||||
};
|
||||
|
||||
slot_maker
|
||||
.fabricate(secondary_slot, [40.0; 2])
|
||||
.top_left_with_margins_on(state.ids.align_ing, 80.0, 20.0)
|
||||
.set(state.ids.modular_inputs[1], ui);
|
||||
|
||||
let can_perform =
|
||||
primary_slot.invslot.is_some() && secondary_slot.invslot.is_some();
|
||||
|
||||
// Craft button
|
||||
if Button::image(self.imgs.button)
|
||||
.w_h(105.0, 25.0)
|
||||
.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(self.localized_strings.get("hud.crafting.craft"))
|
||||
.label_y(conrod_core::position::Relative::Scalar(1.0))
|
||||
.label_color(can_perform.then_some(TEXT_COLOR).unwrap_or(TEXT_GRAY_COLOR))
|
||||
.label_font_size(self.fonts.cyri.scale(12))
|
||||
.label_font_id(self.fonts.cyri.conrod_id)
|
||||
.image_color(can_perform.then_some(TEXT_COLOR).unwrap_or(TEXT_GRAY_COLOR))
|
||||
.mid_bottom_with_margin_on(state.ids.align_ing, -31.0)
|
||||
.parent(state.ids.window_frame)
|
||||
.set(state.ids.btn_craft, ui)
|
||||
.was_clicked()
|
||||
{
|
||||
if let (Some(primary_slot), Some(secondary_slot)) =
|
||||
(primary_slot.invslot, secondary_slot.invslot)
|
||||
{
|
||||
events.push(Event::CraftModularWeapon {
|
||||
primary_slot,
|
||||
secondary_slot,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
let can_perform =
|
||||
self.client
|
||||
.available_recipes()
|
||||
.get(recipe_name.as_str())
|
||||
.get(recipe_name)
|
||||
.map_or(false, |cs| {
|
||||
cs.map_or(true, |cs| {
|
||||
Some(cs) == self.show.craft_sprite.map(|(_, s)| s)
|
||||
Some(cs)
|
||||
== self.show.crafting_fields.craft_sprite.map(|(_, s)| s)
|
||||
})
|
||||
});
|
||||
|
||||
@ -707,13 +898,13 @@ impl<'a> Widget for Crafting<'a> {
|
||||
.set(state.ids.btn_craft, ui)
|
||||
.was_clicked()
|
||||
{
|
||||
events.push(Event::CraftRecipe(recipe_name.clone()));
|
||||
events.push(Event::CraftRecipe(String::from(recipe_name)));
|
||||
}
|
||||
|
||||
// Output Image Frame
|
||||
let quality_col_img = match recipe.output.0.quality() {
|
||||
Quality::Low => self.imgs.inv_slot_grey,
|
||||
Quality::Common => self.imgs.inv_slot_common,
|
||||
Quality::Common => self.imgs.inv_slot,
|
||||
Quality::Moderate => self.imgs.inv_slot_green,
|
||||
Quality::High => self.imgs.inv_slot_blue,
|
||||
Quality::Epic => self.imgs.inv_slot_purple,
|
||||
@ -766,7 +957,9 @@ impl<'a> Widget for Crafting<'a> {
|
||||
CraftingTab::All => false,
|
||||
_ => crafting_tab.satisfies(recipe),
|
||||
})
|
||||
.filter(|crafting_tab| crafting_tab != &self.show.crafting_tab)
|
||||
.filter(|crafting_tab| {
|
||||
crafting_tab != &self.show.crafting_fields.crafting_tab
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.chunks(3)
|
||||
.enumerate()
|
||||
@ -847,7 +1040,9 @@ impl<'a> Widget for Crafting<'a> {
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.font_size(self.fonts.cyri.scale(14))
|
||||
.color(
|
||||
if self.show.craft_sprite.map(|(_, s)| s) == recipe.craft_sprite {
|
||||
if self.show.crafting_fields.craft_sprite.map(|(_, s)| s)
|
||||
== recipe.craft_sprite
|
||||
{
|
||||
TEXT_COLOR
|
||||
} else {
|
||||
TEXT_DULL_RED_COLOR
|
||||
@ -856,7 +1051,8 @@ impl<'a> Widget for Crafting<'a> {
|
||||
.set(state.ids.req_station_txt, ui);
|
||||
}
|
||||
// Ingredients Text
|
||||
let mut ing_txt = Text::new(self.localized_strings.get("hud.crafting.ingredients"))
|
||||
let mut ing_txt =
|
||||
Text::new(self.localized_strings.get("hud.crafting.ingredients"))
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.font_size(self.fonts.cyri.scale(18))
|
||||
.color(TEXT_COLOR);
|
||||
@ -919,7 +1115,8 @@ impl<'a> Widget for Crafting<'a> {
|
||||
.slots()
|
||||
.find_map(|slot| {
|
||||
slot.as_ref().and_then(|item| {
|
||||
if item.matches_recipe_input(recipe_input, *amount) {
|
||||
if item.matches_recipe_input(recipe_input, *amount)
|
||||
{
|
||||
Some(item.item_definition_id())
|
||||
} else {
|
||||
None
|
||||
@ -929,12 +1126,14 @@ impl<'a> Widget for Crafting<'a> {
|
||||
.unwrap_or_else(|| tag.exemplar_identifier()),
|
||||
)
|
||||
},
|
||||
RecipeInput::ListSameItem(item_defs) => Arc::<ItemDef>::load_expect_cloned(
|
||||
RecipeInput::ListSameItem(item_defs) => {
|
||||
Arc::<ItemDef>::load_expect_cloned(
|
||||
self.inventory
|
||||
.slots()
|
||||
.find_map(|slot| {
|
||||
slot.as_ref().and_then(|item| {
|
||||
if item.matches_recipe_input(recipe_input, *amount) {
|
||||
if item.matches_recipe_input(recipe_input, *amount)
|
||||
{
|
||||
Some(item.item_definition_id())
|
||||
} else {
|
||||
None
|
||||
@ -947,10 +1146,12 @@ impl<'a> Widget for Crafting<'a> {
|
||||
.map(|i| i.item_definition_id())
|
||||
.unwrap_or("common.items.weapons.empty.empty")
|
||||
}),
|
||||
),
|
||||
)
|
||||
},
|
||||
};
|
||||
|
||||
// Grey color for images and text if their amount is too low to craft the item
|
||||
// Grey color for images and text if their amount is too low to craft the
|
||||
// item
|
||||
let item_count_in_inventory = self.inventory.item_count(&*item_def);
|
||||
let col = if item_count_in_inventory >= u64::from(*amount.max(&1)) {
|
||||
TEXT_COLOR
|
||||
@ -963,8 +1164,8 @@ impl<'a> Widget for Crafting<'a> {
|
||||
} else {
|
||||
state.ids.ingredient_frame[i - 1]
|
||||
};
|
||||
// add a larger offset for the the first ingredient and the "Required Text for
|
||||
// Catalysts/Tools"
|
||||
// add a larger offset for the the first ingredient and the "Required Text
|
||||
// for Catalysts/Tools"
|
||||
let frame_offset = if i == 0 {
|
||||
10.0
|
||||
} else if *amount == 0 {
|
||||
@ -974,7 +1175,7 @@ impl<'a> Widget for Crafting<'a> {
|
||||
};
|
||||
let quality_col_img = match &item_def.quality() {
|
||||
Quality::Low => self.imgs.inv_slot_grey,
|
||||
Quality::Common => self.imgs.inv_slot_common,
|
||||
Quality::Common => self.imgs.inv_slot,
|
||||
Quality::Moderate => self.imgs.inv_slot_green,
|
||||
Quality::High => self.imgs.inv_slot_blue,
|
||||
Quality::Epic => self.imgs.inv_slot_purple,
|
||||
@ -1077,6 +1278,8 @@ impl<'a> Widget for Crafting<'a> {
|
||||
.set(state.ids.ingredients[i], ui);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
} else if *sel_crafting_tab == CraftingTab::Dismantle {
|
||||
// Title
|
||||
Text::new(self.localized_strings.get("hud.crafting.dismantle_title"))
|
||||
@ -1115,7 +1318,7 @@ impl<'a> Widget for Crafting<'a> {
|
||||
}
|
||||
|
||||
// Search / Title Recipes
|
||||
if let Some(key) = &self.show.crafting_search_key {
|
||||
if let Some(key) = &self.show.crafting_fields.crafting_search_key {
|
||||
if Button::image(self.imgs.close_btn)
|
||||
.top_left_with_margins_on(state.ids.align_rec, -20.0, 5.0)
|
||||
.w_h(14.0, 14.0)
|
||||
|
@ -533,6 +533,11 @@ pub enum Event {
|
||||
slot: InvSlotId,
|
||||
salvage_pos: Vec3<i32>,
|
||||
},
|
||||
CraftModularWeapon {
|
||||
primary_slot: InvSlotId,
|
||||
secondary_slot: InvSlotId,
|
||||
craft_sprite: Option<(Vec3<i32>, SpriteKind)>,
|
||||
},
|
||||
InviteMember(Uid),
|
||||
AcceptInvite,
|
||||
DeclineInvite,
|
||||
@ -692,9 +697,7 @@ pub struct Show {
|
||||
chat_tab_settings_index: Option<usize>,
|
||||
settings_tab: SettingsTab,
|
||||
diary_fields: diary::DiaryShow,
|
||||
crafting_tab: CraftingTab,
|
||||
crafting_search_key: Option<String>,
|
||||
craft_sprite: Option<(Vec3<i32>, SpriteKind)>,
|
||||
crafting_fields: crafting::CraftingShow,
|
||||
social_search_key: Option<String>,
|
||||
want_grab: bool,
|
||||
stats: bool,
|
||||
@ -703,7 +706,6 @@ pub struct Show {
|
||||
camera_clamp: bool,
|
||||
prompt_dialog: Option<PromptDialogSettings>,
|
||||
location_markers: MapMarkers,
|
||||
salvage: bool,
|
||||
trade_amount_input_key: Option<TradeAmountInput>,
|
||||
}
|
||||
impl Show {
|
||||
@ -712,7 +714,7 @@ impl Show {
|
||||
self.bag = open;
|
||||
self.map = false;
|
||||
self.want_grab = !open;
|
||||
self.salvage = false;
|
||||
self.crafting_fields.salvage = false;
|
||||
|
||||
if !open {
|
||||
self.crafting = false;
|
||||
@ -734,7 +736,7 @@ impl Show {
|
||||
self.map = open;
|
||||
self.bag = false;
|
||||
self.crafting = false;
|
||||
self.salvage = false;
|
||||
self.crafting_fields.salvage = false;
|
||||
self.social = false;
|
||||
self.diary = false;
|
||||
self.want_grab = !open;
|
||||
@ -760,7 +762,7 @@ impl Show {
|
||||
self.search_crafting_recipe(None);
|
||||
}
|
||||
self.crafting = open;
|
||||
self.salvage = false;
|
||||
self.crafting_fields.salvage = false;
|
||||
self.bag = open;
|
||||
self.map = false;
|
||||
self.want_grab = !open;
|
||||
@ -774,16 +776,18 @@ impl Show {
|
||||
) {
|
||||
self.selected_crafting_tab(tab);
|
||||
self.crafting(true);
|
||||
self.craft_sprite = self.craft_sprite.or(craft_sprite);
|
||||
self.salvage = matches!(self.craft_sprite, Some((_, SpriteKind::DismantlingBench)))
|
||||
&& matches!(tab, CraftingTab::Dismantle);
|
||||
self.crafting_fields.craft_sprite = self.crafting_fields.craft_sprite.or(craft_sprite);
|
||||
self.crafting_fields.salvage = matches!(
|
||||
self.crafting_fields.craft_sprite,
|
||||
Some((_, SpriteKind::DismantlingBench))
|
||||
) && matches!(tab, CraftingTab::Dismantle);
|
||||
}
|
||||
|
||||
fn diary(&mut self, open: bool) {
|
||||
if !self.esc_menu {
|
||||
self.social = false;
|
||||
self.crafting = false;
|
||||
self.salvage = false;
|
||||
self.crafting_fields.salvage = false;
|
||||
self.bag = false;
|
||||
self.map = false;
|
||||
self.diary_fields = diary::DiaryShow::default();
|
||||
@ -802,7 +806,7 @@ impl Show {
|
||||
self.bag = false;
|
||||
self.social = false;
|
||||
self.crafting = false;
|
||||
self.salvage = false;
|
||||
self.crafting_fields.salvage = false;
|
||||
self.diary = false;
|
||||
self.want_grab = !open;
|
||||
}
|
||||
@ -894,10 +898,12 @@ impl Show {
|
||||
self.social = false;
|
||||
}
|
||||
|
||||
fn selected_crafting_tab(&mut self, sel_cat: CraftingTab) { self.crafting_tab = sel_cat; }
|
||||
fn selected_crafting_tab(&mut self, sel_cat: CraftingTab) {
|
||||
self.crafting_fields.crafting_tab = sel_cat;
|
||||
}
|
||||
|
||||
fn search_crafting_recipe(&mut self, search_key: Option<String>) {
|
||||
self.crafting_search_key = search_key;
|
||||
self.crafting_fields.crafting_search_key = search_key;
|
||||
}
|
||||
|
||||
fn search_social_players(&mut self, search_key: Option<String>) {
|
||||
@ -1106,9 +1112,7 @@ impl Hud {
|
||||
chat_tab_settings_index: None,
|
||||
settings_tab: SettingsTab::Interface,
|
||||
diary_fields: diary::DiaryShow::default(),
|
||||
crafting_tab: CraftingTab::All,
|
||||
crafting_search_key: None,
|
||||
craft_sprite: None,
|
||||
crafting_fields: crafting::CraftingShow::default(),
|
||||
social_search_key: None,
|
||||
want_grab: true,
|
||||
ingame: true,
|
||||
@ -1118,7 +1122,6 @@ impl Hud {
|
||||
camera_clamp: false,
|
||||
prompt_dialog: None,
|
||||
location_markers: MapMarkers::default(),
|
||||
salvage: false,
|
||||
trade_amount_input_key: None,
|
||||
},
|
||||
to_focus: None,
|
||||
@ -2930,6 +2933,7 @@ impl Hud {
|
||||
self.pulse,
|
||||
&self.rot_imgs,
|
||||
item_tooltip_manager,
|
||||
&mut self.slot_manager,
|
||||
&self.item_imgs,
|
||||
inventory,
|
||||
&msm,
|
||||
@ -2942,7 +2946,17 @@ impl Hud {
|
||||
crafting::Event::CraftRecipe(recipe) => {
|
||||
events.push(Event::CraftRecipe {
|
||||
recipe,
|
||||
craft_sprite: self.show.craft_sprite,
|
||||
craft_sprite: self.show.crafting_fields.craft_sprite,
|
||||
});
|
||||
},
|
||||
crafting::Event::CraftModularWeapon {
|
||||
primary_slot,
|
||||
secondary_slot,
|
||||
} => {
|
||||
events.push(Event::CraftModularWeapon {
|
||||
primary_slot,
|
||||
secondary_slot,
|
||||
craft_sprite: self.show.crafting_fields.craft_sprite,
|
||||
});
|
||||
},
|
||||
crafting::Event::Close => {
|
||||
@ -3352,6 +3366,7 @@ impl Hud {
|
||||
Hotbar(_) => None,
|
||||
Trade(_) => None,
|
||||
Ability(_) => None,
|
||||
Crafting(_) => None,
|
||||
};
|
||||
match event {
|
||||
slot::Event::Dragged(a, b) => {
|
||||
@ -3428,6 +3443,17 @@ impl Hud {
|
||||
},
|
||||
(AbilitySlot::Ability(_), AbilitySlot::Ability(_)) => {},
|
||||
}
|
||||
} else if let (Inventory(i), Crafting(c)) = (a, b) {
|
||||
// Add item to crafting input
|
||||
if (c.requirement)(inventories.get(client.entity()), i.slot) {
|
||||
self.show
|
||||
.crafting_fields
|
||||
.recipe_inputs
|
||||
.insert(c.index, i.slot);
|
||||
}
|
||||
} else if let (Crafting(c), Inventory(_)) = (a, b) {
|
||||
// Remove item from crafting input
|
||||
self.show.crafting_fields.recipe_inputs.remove(&c.index);
|
||||
}
|
||||
},
|
||||
slot::Event::Dropped(from) => {
|
||||
@ -3535,11 +3561,14 @@ impl Hud {
|
||||
slot::Event::Used(from) => {
|
||||
// Item used (selected and then clicked again)
|
||||
if let Some(from) = to_slot(from) {
|
||||
if self.show.salvage
|
||||
&& matches!(self.show.crafting_tab, CraftingTab::Dismantle)
|
||||
if self.show.crafting_fields.salvage
|
||||
&& matches!(
|
||||
self.show.crafting_fields.crafting_tab,
|
||||
CraftingTab::Dismantle
|
||||
)
|
||||
{
|
||||
if let (Slot::Inventory(slot), Some((salvage_pos, _sprite_kind))) =
|
||||
(from, self.show.craft_sprite)
|
||||
(from, self.show.crafting_fields.craft_sprite)
|
||||
{
|
||||
events.push(Event::SalvageItem { slot, salvage_pos })
|
||||
}
|
||||
@ -3575,6 +3604,9 @@ impl Hud {
|
||||
});
|
||||
} else if let Ability(AbilitySlot::Slot(index)) = from {
|
||||
events.push(Event::ChangeAbility(index, AuxiliaryAbility::Empty));
|
||||
} else if let Crafting(c) = from {
|
||||
// Remove item from crafting input
|
||||
self.show.crafting_fields.recipe_inputs.remove(&c.index);
|
||||
}
|
||||
},
|
||||
slot::Event::Request {
|
||||
@ -4232,7 +4264,8 @@ impl Hud {
|
||||
}
|
||||
|
||||
// Stop selecting a sprite to perform crafting with when out of range
|
||||
self.show.craft_sprite = self.show.craft_sprite.filter(|(pos, _)| {
|
||||
self.show.crafting_fields.craft_sprite =
|
||||
self.show.crafting_fields.craft_sprite.filter(|(pos, _)| {
|
||||
self.show.crafting
|
||||
&& if let Some(player_pos) = client.position() {
|
||||
pos.map(|e| e as f32 + 0.5).distance(player_pos) < MAX_PICKUP_RANGE
|
||||
|
@ -12,6 +12,7 @@ use common::comp::{
|
||||
};
|
||||
use conrod_core::{image, Color};
|
||||
use specs::Entity as EcsEntity;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
|
||||
pub use common::comp::slot::{ArmorSlot, EquipSlot};
|
||||
|
||||
@ -22,6 +23,7 @@ pub enum SlotKind {
|
||||
Hotbar(HotbarSlot),
|
||||
Trade(TradeSlot),
|
||||
Ability(AbilitySlot),
|
||||
Crafting(CraftSlot),
|
||||
/* Spellbook(SpellbookSlot), TODO */
|
||||
}
|
||||
|
||||
@ -233,6 +235,46 @@ impl<'a> SlotKey<AbilitiesSource<'a>, img_ids::Imgs> for AbilitySlot {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct CraftSlot {
|
||||
pub index: u32,
|
||||
pub invslot: Option<InvSlotId>,
|
||||
pub requirement: fn(Option<&Inventory>, InvSlotId) -> bool,
|
||||
pub required_amount: u32,
|
||||
}
|
||||
|
||||
impl PartialEq for CraftSlot {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
(self.index, self.invslot, self.required_amount)
|
||||
== (other.index, other.invslot, other.required_amount)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for CraftSlot {
|
||||
fn fmt(&self, _: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { todo!() }
|
||||
}
|
||||
|
||||
impl SlotKey<Inventory, ItemImgs> for CraftSlot {
|
||||
type ImageKey = ItemKey;
|
||||
|
||||
fn image_key(&self, source: &Inventory) -> Option<(Self::ImageKey, Option<Color>)> {
|
||||
self.invslot
|
||||
.and_then(|invslot| source.get(invslot))
|
||||
.map(|i| (i.into(), None))
|
||||
}
|
||||
|
||||
fn amount(&self, source: &Inventory) -> Option<u32> {
|
||||
self.invslot
|
||||
.and_then(|invslot| source.get(invslot))
|
||||
.map(|item| self.required_amount.min(item.amount()))
|
||||
.filter(|amount| *amount > 1)
|
||||
}
|
||||
|
||||
fn image_ids(key: &Self::ImageKey, source: &ItemImgs) -> Vec<image::Id> {
|
||||
source.img_ids_or_not_found_img(key.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<InventorySlot> for SlotKind {
|
||||
fn from(inventory: InventorySlot) -> Self { Self::Inventory(inventory) }
|
||||
}
|
||||
@ -244,6 +286,7 @@ impl From<EquipSlot> for SlotKind {
|
||||
impl From<HotbarSlot> for SlotKind {
|
||||
fn from(hotbar: HotbarSlot) -> Self { Self::Hotbar(hotbar) }
|
||||
}
|
||||
|
||||
impl From<TradeSlot> for SlotKind {
|
||||
fn from(trade: TradeSlot) -> Self { Self::Trade(trade) }
|
||||
}
|
||||
@ -252,6 +295,10 @@ impl From<AbilitySlot> for SlotKind {
|
||||
fn from(ability: AbilitySlot) -> Self { Self::Ability(ability) }
|
||||
}
|
||||
|
||||
impl From<CraftSlot> for SlotKind {
|
||||
fn from(craft: CraftSlot) -> Self { Self::Crafting(craft) }
|
||||
}
|
||||
|
||||
impl SumSlot for SlotKind {
|
||||
fn drag_size(&self) -> Option<[f64; 2]> {
|
||||
Some(match self {
|
||||
|
@ -1420,6 +1420,17 @@ impl PlayState for SessionState {
|
||||
.craft_recipe(&recipe, slots, craft_sprite);
|
||||
}
|
||||
},
|
||||
HudEvent::CraftModularWeapon {
|
||||
primary_slot,
|
||||
secondary_slot,
|
||||
craft_sprite,
|
||||
} => {
|
||||
self.client.borrow_mut().craft_modular_weapon(
|
||||
primary_slot,
|
||||
secondary_slot,
|
||||
craft_sprite.map(|(pos, _sprite)| pos),
|
||||
);
|
||||
},
|
||||
HudEvent::SalvageItem { slot, salvage_pos } => {
|
||||
self.client.borrow_mut().salvage_item(slot, salvage_pos);
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user