mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Craft all
This commit is contained in:
parent
2d2d6b5c64
commit
9315482fc4
@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
### Added
|
||||
- Currently playing music track and artist now shows in the debug menu.
|
||||
- Added a setting to influence the gap between music track plays.
|
||||
- Added a Craft All button.
|
||||
|
||||
### Changed
|
||||
- Use fluent for translations
|
||||
|
@ -2,6 +2,7 @@ hud-crafting = Elaborar
|
||||
hud-crafting-recipes = Receptes
|
||||
hud-crafting-ingredients = Ingredients:
|
||||
hud-crafting-craft = Elaborar
|
||||
hud-crafting-craft_all = Elaborar Tot
|
||||
hud-crafting-tool_cata = Requereix:
|
||||
hud-crafting-req_crafting_station = Requereix:
|
||||
hud-crafting-anvil = Enclusa
|
||||
|
@ -2,6 +2,7 @@ hud-crafting = Crafting
|
||||
hud-crafting-recipes = Recipes
|
||||
hud-crafting-ingredients = Ingredients:
|
||||
hud-crafting-craft = Craft
|
||||
hud-crafting-craft_all = Craft All
|
||||
hud-crafting-tool_cata = Requires:
|
||||
hud-crafting-req_crafting_station = Requires:
|
||||
hud-crafting-anvil = Anvil
|
||||
|
@ -2,6 +2,7 @@ hud-crafting = Fabricación
|
||||
hud-crafting-recipes = Recetas
|
||||
hud-crafting-ingredients = Ingredientes:
|
||||
hud-crafting-craft = Fabricar
|
||||
hud-crafting-craft_all = Fabricar Todo
|
||||
hud-crafting-tool_cata = Requisitos:
|
||||
hud-crafting-req_crafting_station = Requisitos:
|
||||
hud-crafting-anvil = Yunque
|
||||
|
@ -2,6 +2,7 @@ hud-crafting = Fabrication
|
||||
hud-crafting-recipes = Recettes
|
||||
hud-crafting-ingredients = Ingrédients :
|
||||
hud-crafting-craft = Fabriquer
|
||||
hud-crafting-craft_all = Tout Fabriquer
|
||||
hud-crafting-tool_cata = Nécessite :
|
||||
hud-crafting-req_crafting_station = Nécessite:
|
||||
hud-crafting-anvil = Enclume
|
||||
|
@ -1,7 +1,8 @@
|
||||
hud-crafting = Criação
|
||||
hud-crafting-recipes = Receitas
|
||||
hud-crafting-ingredients = Ingredientes:
|
||||
hud-crafting-craft = Criar
|
||||
hud-crafting-craft = Forjar
|
||||
hud-crafting-craft_all = Forjar Tudo
|
||||
hud-crafting-tool_cata = Requer:
|
||||
hud-crafting-req_crafting_station = Requer:
|
||||
hud-crafting-anvil = Bigorna
|
||||
|
@ -1074,13 +1074,13 @@ impl Client {
|
||||
|
||||
/// Returns whether the specified recipe can be crafted and the sprite, if
|
||||
/// any, that is required to do so.
|
||||
pub fn can_craft_recipe(&self, recipe: &str) -> (bool, Option<SpriteKind>) {
|
||||
pub fn can_craft_recipe(&self, recipe: &str, amount: u32) -> (bool, Option<SpriteKind>) {
|
||||
self.recipe_book
|
||||
.get(recipe)
|
||||
.zip(self.inventories().get(self.entity()))
|
||||
.map(|(recipe, inv)| {
|
||||
(
|
||||
recipe.inventory_contains_ingredients(inv).is_ok(),
|
||||
recipe.inventory_contains_ingredients(inv, amount).is_ok(),
|
||||
recipe.craft_sprite,
|
||||
)
|
||||
})
|
||||
@ -1092,8 +1092,9 @@ impl Client {
|
||||
recipe: &str,
|
||||
slots: Vec<(u32, InvSlotId)>,
|
||||
craft_sprite: Option<(Vec3<i32>, SpriteKind)>,
|
||||
amount: u32,
|
||||
) -> bool {
|
||||
let (can_craft, required_sprite) = self.can_craft_recipe(recipe);
|
||||
let (can_craft, required_sprite) = self.can_craft_recipe(recipe, amount);
|
||||
let has_sprite = required_sprite.map_or(true, |s| Some(s) == craft_sprite.map(|(_, s)| s));
|
||||
if can_craft && has_sprite {
|
||||
self.send_msg(ClientGeneral::ControlEvent(ControlEvent::InventoryEvent(
|
||||
@ -1101,6 +1102,7 @@ impl Client {
|
||||
craft_event: CraftEvent::Simple {
|
||||
recipe: recipe.to_string(),
|
||||
slots,
|
||||
amount,
|
||||
},
|
||||
craft_sprite: craft_sprite.map(|(pos, _)| pos),
|
||||
},
|
||||
@ -1213,7 +1215,7 @@ impl Client {
|
||||
.iter()
|
||||
.map(|(name, _)| name.clone())
|
||||
.filter_map(|name| {
|
||||
let (can_craft, required_sprite) = self.can_craft_recipe(&name);
|
||||
let (can_craft, required_sprite) = self.can_craft_recipe(&name, 1);
|
||||
if can_craft {
|
||||
Some((name, required_sprite))
|
||||
} else {
|
||||
|
@ -98,6 +98,7 @@ pub enum CraftEvent {
|
||||
Simple {
|
||||
recipe: String,
|
||||
slots: Vec<(u32, InvSlotId)>,
|
||||
amount: u32,
|
||||
},
|
||||
Salvage(InvSlotId),
|
||||
// TODO: Maybe look at making this more general when there are more modular recipes?
|
||||
|
@ -164,13 +164,52 @@ impl Recipe {
|
||||
pub fn inventory_contains_ingredients(
|
||||
&self,
|
||||
inv: &Inventory,
|
||||
recipe_amount: u32,
|
||||
) -> Result<Vec<(u32, InvSlotId)>, Vec<(&RecipeInput, u32)>> {
|
||||
inventory_contains_ingredients(
|
||||
self.inputs()
|
||||
.map(|(input, amount, _is_modular)| (input, amount)),
|
||||
inv,
|
||||
recipe_amount,
|
||||
)
|
||||
}
|
||||
|
||||
/// Calculates the maximum number of items craftable given the current
|
||||
/// inventory state.
|
||||
pub fn max_from_ingredients(&self, inv: &Inventory) -> u32 {
|
||||
let mut max_recipes = None;
|
||||
|
||||
for (input, amount) in self
|
||||
.inputs()
|
||||
.map(|(input, amount, _is_modular)| (input, amount))
|
||||
{
|
||||
let needed = amount as f32;
|
||||
let mut input_max = HashMap::<InvSlotId, u32>::new();
|
||||
|
||||
// Checks through every slot, filtering to only those that contain items that
|
||||
// can satisfy the input.
|
||||
for (inv_slot_id, slot) in inv.slots_with_id() {
|
||||
if let Some(item) = slot
|
||||
.as_ref()
|
||||
.filter(|item| item.matches_recipe_input(&*input, amount))
|
||||
{
|
||||
*input_max.entry(inv_slot_id).or_insert(0) += item.amount();
|
||||
}
|
||||
}
|
||||
|
||||
// Updates maximum craftable amount based on least recipe-proportional
|
||||
// availability.
|
||||
let max_item_proportion =
|
||||
((input_max.values().sum::<u32>() as f32) / needed).floor() as u32;
|
||||
max_recipes = Some(match max_recipes {
|
||||
None => max_item_proportion,
|
||||
Some(max_recipes) if (max_item_proportion < max_recipes) => max_item_proportion,
|
||||
Some(n) => n,
|
||||
});
|
||||
}
|
||||
|
||||
max_recipes.unwrap_or(0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Determine whether the inventory contains the ingredients for a recipe.
|
||||
@ -183,6 +222,7 @@ impl Recipe {
|
||||
fn inventory_contains_ingredients<'a, I: Iterator<Item = (&'a RecipeInput, u32)>>(
|
||||
ingredients: I,
|
||||
inv: &Inventory,
|
||||
recipe_amount: u32,
|
||||
) -> Result<Vec<(u32, InvSlotId)>, Vec<(&'a RecipeInput, u32)>> {
|
||||
// Hashmap tracking the quantity that needs to be removed from each slot (so
|
||||
// that it doesn't think a slot can provide more items than it contains)
|
||||
@ -194,7 +234,7 @@ fn inventory_contains_ingredients<'a, I: Iterator<Item = (&'a RecipeInput, u32)>
|
||||
let mut missing = Vec::<(&RecipeInput, u32)>::new();
|
||||
|
||||
for (i, (input, amount)) in ingredients.enumerate() {
|
||||
let mut needed = amount;
|
||||
let mut needed = amount * recipe_amount;
|
||||
let mut contains_any = false;
|
||||
// Checks through every slot, filtering to only those that contain items that
|
||||
// can satisfy the input
|
||||
@ -358,7 +398,7 @@ impl RecipeBook {
|
||||
pub fn get_available(&self, inv: &Inventory) -> Vec<(String, Recipe)> {
|
||||
self.recipes
|
||||
.iter()
|
||||
.filter(|(_, recipe)| recipe.inventory_contains_ingredients(inv).is_ok())
|
||||
.filter(|(_, recipe)| recipe.inventory_contains_ingredients(inv, 1).is_ok())
|
||||
.map(|(name, recipe)| (name.clone(), recipe.clone()))
|
||||
.collect()
|
||||
}
|
||||
@ -654,6 +694,7 @@ impl ComponentRecipe {
|
||||
.iter()
|
||||
.map(|(input, amount)| (input, *amount)),
|
||||
inv,
|
||||
1,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -672,7 +672,11 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
|
||||
};
|
||||
|
||||
let crafted_items = match craft_event {
|
||||
CraftEvent::Simple { recipe, slots } => recipe_book
|
||||
CraftEvent::Simple {
|
||||
recipe,
|
||||
slots,
|
||||
amount,
|
||||
} => recipe_book
|
||||
.get(&recipe)
|
||||
.filter(|r| {
|
||||
if let Some(needed_sprite) = r.craft_sprite {
|
||||
@ -683,13 +687,21 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
|
||||
}
|
||||
})
|
||||
.and_then(|r| {
|
||||
let items = (0..amount)
|
||||
.into_iter()
|
||||
.filter_map(|_| {
|
||||
r.craft_simple(
|
||||
&mut inventory,
|
||||
slots,
|
||||
slots.clone(),
|
||||
&state.ecs().read_resource::<AbilityMap>(),
|
||||
&state.ecs().read_resource::<MaterialStatManifest>(),
|
||||
)
|
||||
.ok()
|
||||
})
|
||||
.flatten()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if items.is_empty() { None } else { Some(items) }
|
||||
}),
|
||||
CraftEvent::Salvage(slot) => {
|
||||
let sprite = get_craft_sprite(state, craft_sprite);
|
||||
|
@ -38,7 +38,7 @@ use hashbrown::HashMap;
|
||||
use i18n::Localization;
|
||||
use std::{borrow::Cow, collections::BTreeMap, sync::Arc};
|
||||
use strum::{EnumIter, IntoEnumIterator};
|
||||
use tracing::warn;
|
||||
use tracing::{error, warn};
|
||||
use vek::*;
|
||||
|
||||
widget_ids! {
|
||||
@ -61,6 +61,7 @@ widget_ids! {
|
||||
align_ing,
|
||||
scrollbar_ing,
|
||||
btn_craft,
|
||||
btn_craft_all,
|
||||
recipe_list_btns[],
|
||||
recipe_list_labels[],
|
||||
recipe_list_quality_indicators[],
|
||||
@ -96,7 +97,10 @@ widget_ids! {
|
||||
}
|
||||
|
||||
pub enum Event {
|
||||
CraftRecipe(String),
|
||||
CraftRecipe {
|
||||
recipe_name: String,
|
||||
amount: u32,
|
||||
},
|
||||
CraftModularWeapon {
|
||||
primary_slot: InvSlotId,
|
||||
secondary_slot: InvSlotId,
|
||||
@ -1355,7 +1359,7 @@ impl<'a> Widget for Crafting<'a> {
|
||||
.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)
|
||||
.bottom_left_with_margins_on(state.ids.align_ing, -31.0, 15.0)
|
||||
.parent(state.ids.window_frame)
|
||||
.set(state.ids.btn_craft, ui)
|
||||
.was_clicked()
|
||||
@ -1381,10 +1385,62 @@ impl<'a> Widget for Crafting<'a> {
|
||||
});
|
||||
}
|
||||
},
|
||||
RecipeKind::Simple => events.push(Event::CraftRecipe(recipe_name)),
|
||||
RecipeKind::Simple => events.push(Event::CraftRecipe {
|
||||
recipe_name,
|
||||
amount: 1,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
// Craft All button
|
||||
let can_perform_all = can_perform && matches!(recipe_kind, RecipeKind::Simple);
|
||||
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_all"))
|
||||
.label_y(conrod_core::position::Relative::Scalar(1.0))
|
||||
.label_color(
|
||||
can_perform_all
|
||||
.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_all
|
||||
.then_some(TEXT_COLOR)
|
||||
.unwrap_or(TEXT_GRAY_COLOR),
|
||||
)
|
||||
.bottom_right_with_margins_on(state.ids.align_ing, -31.0, 15.0)
|
||||
.parent(state.ids.window_frame)
|
||||
.set(state.ids.btn_craft_all, ui)
|
||||
.was_clicked()
|
||||
&& can_perform_all
|
||||
{
|
||||
if let (RecipeKind::Simple, Some(selected_recipe)) =
|
||||
(recipe_kind, &state.selected_recipe)
|
||||
{
|
||||
let amount = recipe.max_from_ingredients(self.inventory);
|
||||
if amount > 0 {
|
||||
events.push(Event::CraftRecipe {
|
||||
recipe_name: selected_recipe.to_string(),
|
||||
amount,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
error!("State shows no selected recipe when trying to craft multiple.");
|
||||
}
|
||||
};
|
||||
|
||||
// Crafting Station Info
|
||||
if recipe.craft_sprite.is_some() {
|
||||
Text::new(
|
||||
|
@ -538,8 +538,9 @@ pub enum Event {
|
||||
Quit,
|
||||
|
||||
CraftRecipe {
|
||||
recipe: String,
|
||||
recipe_name: String,
|
||||
craft_sprite: Option<(Vec3<i32>, SpriteKind)>,
|
||||
amount: u32,
|
||||
},
|
||||
SalvageItem {
|
||||
slot: InvSlotId,
|
||||
@ -2912,10 +2913,14 @@ impl Hud {
|
||||
.set(self.ids.crafting_window, ui_widgets)
|
||||
{
|
||||
match event {
|
||||
crafting::Event::CraftRecipe(recipe) => {
|
||||
crafting::Event::CraftRecipe {
|
||||
recipe_name,
|
||||
amount,
|
||||
} => {
|
||||
events.push(Event::CraftRecipe {
|
||||
recipe,
|
||||
recipe_name,
|
||||
craft_sprite: self.show.crafting_fields.craft_sprite,
|
||||
amount,
|
||||
});
|
||||
},
|
||||
crafting::Event::CraftModularWeapon {
|
||||
|
@ -1541,26 +1541,30 @@ impl PlayState for SessionState {
|
||||
},
|
||||
|
||||
HudEvent::CraftRecipe {
|
||||
recipe,
|
||||
recipe_name: recipe,
|
||||
craft_sprite,
|
||||
amount,
|
||||
} => {
|
||||
let slots = {
|
||||
let client = self.client.borrow();
|
||||
if let Some(recipe) = client.recipe_book().get(&recipe) {
|
||||
client
|
||||
.inventories()
|
||||
.get(client.entity())
|
||||
.and_then(|inv| recipe.inventory_contains_ingredients(inv).ok())
|
||||
client.inventories().get(client.entity()).and_then(|inv| {
|
||||
recipe.inventory_contains_ingredients(inv, 1).ok()
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
if let Some(slots) = slots {
|
||||
self.client
|
||||
.borrow_mut()
|
||||
.craft_recipe(&recipe, slots, craft_sprite);
|
||||
self.client.borrow_mut().craft_recipe(
|
||||
&recipe,
|
||||
slots,
|
||||
craft_sprite,
|
||||
amount,
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
HudEvent::CraftModularWeapon {
|
||||
primary_slot,
|
||||
secondary_slot,
|
||||
@ -1572,6 +1576,7 @@ impl PlayState for SessionState {
|
||||
craft_sprite,
|
||||
);
|
||||
},
|
||||
|
||||
HudEvent::CraftModularWeaponComponent {
|
||||
toolkind,
|
||||
material,
|
||||
|
Loading…
Reference in New Issue
Block a user