mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Simple recipes now take a vec of slots to look in for the ingredients.
This commit is contained in:
parent
7a17863988
commit
794b072d3e
@ -988,7 +988,7 @@ impl Client {
|
||||
.zip(self.inventories().get(self.entity()))
|
||||
.map(|(recipe, inv)| {
|
||||
(
|
||||
inv.contains_ingredients(&*recipe).is_ok(),
|
||||
recipe.inventory_contains_ingredients(inv).is_ok(),
|
||||
recipe.craft_sprite,
|
||||
)
|
||||
})
|
||||
@ -998,6 +998,7 @@ impl Client {
|
||||
pub fn craft_recipe(
|
||||
&mut self,
|
||||
recipe: &str,
|
||||
slots: Vec<InvSlotId>,
|
||||
craft_sprite: Option<(Vec3<i32>, SpriteKind)>,
|
||||
) -> bool {
|
||||
let (can_craft, required_sprite) = self.can_craft_recipe(recipe);
|
||||
@ -1005,7 +1006,10 @@ impl Client {
|
||||
if can_craft && has_sprite {
|
||||
self.send_msg(ClientGeneral::ControlEvent(ControlEvent::InventoryEvent(
|
||||
InventoryEvent::CraftRecipe {
|
||||
craft_event: CraftEvent::Simple(recipe.to_string()),
|
||||
craft_event: CraftEvent::Simple {
|
||||
recipe: recipe.to_string(),
|
||||
slots,
|
||||
},
|
||||
craft_sprite: craft_sprite.map(|(pos, _)| pos),
|
||||
},
|
||||
)));
|
||||
|
@ -92,7 +92,10 @@ impl From<InventoryEvent> for InventoryManip {
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub enum CraftEvent {
|
||||
Simple(String),
|
||||
Simple {
|
||||
recipe: String,
|
||||
slots: Vec<InvSlotId>,
|
||||
},
|
||||
Salvage(InvSlotId),
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ use core::ops::Not;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specs::{Component, DerefFlaggedStorage};
|
||||
use specs_idvs::IdvStorage;
|
||||
use std::{collections::HashMap, convert::TryFrom, mem, ops::Range};
|
||||
use std::{convert::TryFrom, mem, ops::Range};
|
||||
use tracing::{debug, trace, warn};
|
||||
use vek::Vec3;
|
||||
|
||||
@ -16,7 +16,6 @@ use crate::{
|
||||
slot::{InvSlotId, SlotId},
|
||||
Item,
|
||||
},
|
||||
recipe::{Recipe, RecipeInput},
|
||||
uid::Uid,
|
||||
LoadoutBuilder,
|
||||
};
|
||||
@ -460,48 +459,6 @@ impl Inventory {
|
||||
.sum()
|
||||
}
|
||||
|
||||
/// Determine whether the inventory contains the ingredients for a recipe.
|
||||
/// If it does, return a vector of numbers, where is number corresponds
|
||||
/// to an inventory slot, along with the number of items that need
|
||||
/// removing from it. It items are missing, return the missing items, and
|
||||
/// how many are missing.
|
||||
pub fn contains_ingredients<'a>(
|
||||
&self,
|
||||
recipe: &'a Recipe,
|
||||
) -> Result<HashMap<InvSlotId, u32>, Vec<(&'a RecipeInput, u32)>> {
|
||||
let mut slot_claims = HashMap::<InvSlotId, u32>::new();
|
||||
let mut missing = Vec::<(&RecipeInput, u32)>::new();
|
||||
|
||||
for (input, mut needed) in recipe.inputs() {
|
||||
let mut contains_any = false;
|
||||
|
||||
for (inv_slot_id, slot) in self.slots_with_id() {
|
||||
if let Some(item) = slot
|
||||
.as_ref()
|
||||
.filter(|item| item.matches_recipe_input(&*input))
|
||||
{
|
||||
let claim = slot_claims.entry(inv_slot_id).or_insert(0);
|
||||
// FIXME: Fishy, looks like it can underflow before min which can trigger an
|
||||
// overflow check.
|
||||
let can_claim = (item.amount() - *claim).min(needed);
|
||||
*claim += can_claim;
|
||||
needed -= can_claim;
|
||||
contains_any = true;
|
||||
}
|
||||
}
|
||||
|
||||
if needed > 0 || !contains_any {
|
||||
missing.push((input, needed));
|
||||
}
|
||||
}
|
||||
|
||||
if missing.is_empty() {
|
||||
Ok(slot_claims)
|
||||
} else {
|
||||
Err(missing)
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a new item to the first empty slot of the inventory. Returns the
|
||||
/// item again in an Err if no free slot was found, otherwise returns a
|
||||
/// reference to the item.
|
||||
|
@ -27,38 +27,60 @@ pub struct Recipe {
|
||||
#[allow(clippy::type_complexity)]
|
||||
impl Recipe {
|
||||
/// Perform a recipe, returning a list of missing items on failure
|
||||
pub fn perform(
|
||||
pub fn craft_simple(
|
||||
&self,
|
||||
inv: &mut Inventory,
|
||||
slots: Vec<InvSlotId>,
|
||||
ability_map: &AbilityMap,
|
||||
msm: &MaterialStatManifest,
|
||||
) -> Result<(Item, u32), Vec<(&RecipeInput, u32)>> {
|
||||
// Get ingredient cells from inventory,
|
||||
let mut components = Vec::new();
|
||||
) -> Result<Vec<Item>, Vec<(&RecipeInput, u32)>> {
|
||||
let mut recipe_inputs = Vec::new();
|
||||
let mut unsatisfied_requirements = Vec::new();
|
||||
|
||||
inv.contains_ingredients(self)?
|
||||
.into_iter()
|
||||
.for_each(|(pos, n)| {
|
||||
(0..n).for_each(|_| {
|
||||
let component = inv
|
||||
.take(pos, ability_map, msm)
|
||||
.expect("Expected item to exist in inventory");
|
||||
components.push(component);
|
||||
})
|
||||
self.inputs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.for_each(|(i, (input, amount))| {
|
||||
let valid_input = if let Some(item) = slots.get(i).and_then(|slot| inv.get(*slot)) {
|
||||
item.matches_recipe_input(input)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if let Some(slot) = slots.get(i) {
|
||||
if !valid_input {
|
||||
unsatisfied_requirements.push((input, *amount));
|
||||
} else {
|
||||
for taken in 0..*amount {
|
||||
if let Some(item) = inv.take(*slot, ability_map, msm) {
|
||||
recipe_inputs.push(item);
|
||||
} else {
|
||||
unsatisfied_requirements.push((input, *amount - taken));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
unsatisfied_requirements.push((input, *amount));
|
||||
}
|
||||
});
|
||||
|
||||
let crafted_item =
|
||||
Item::new_from_item_def(Arc::clone(&self.output.0), &components, ability_map, msm);
|
||||
|
||||
Ok((crafted_item, self.output.1))
|
||||
|
||||
// for i in 0..self.output.1 {
|
||||
// if let Err(item) = inv.push(crafted_item) {
|
||||
// return Ok(Some((item, self.output.1 - i)));
|
||||
// }
|
||||
// }
|
||||
|
||||
// Ok(None)
|
||||
if unsatisfied_requirements.is_empty() {
|
||||
let (item_def, quantity) = &self.output;
|
||||
let crafted_item =
|
||||
Item::new_from_item_def(Arc::clone(item_def), &[], ability_map, msm);
|
||||
let mut crafted_items = Vec::with_capacity(*quantity as usize);
|
||||
for _ in 0..*quantity {
|
||||
crafted_items.push(crafted_item.duplicate(ability_map, msm));
|
||||
}
|
||||
Ok(crafted_items)
|
||||
} else {
|
||||
for item in recipe_inputs {
|
||||
inv.push(item)
|
||||
.expect("Item was in inventory before craft attempt");
|
||||
}
|
||||
Err(unsatisfied_requirements)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn inputs(&self) -> impl ExactSizeIterator<Item = (&RecipeInput, u32)> {
|
||||
@ -66,6 +88,52 @@ impl Recipe {
|
||||
.iter()
|
||||
.map(|(item_def, amount)| (item_def, *amount))
|
||||
}
|
||||
|
||||
/// Determine whether the inventory contains the ingredients for a recipe.
|
||||
/// If it does, return a vec of inventory slots that contain the
|
||||
/// ingredients needed, whose positions correspond to particular recipe
|
||||
/// inputs. If items are missing, return the missing items, and how many
|
||||
/// are missing.
|
||||
pub fn inventory_contains_ingredients<'a>(
|
||||
&self,
|
||||
inv: &'a Inventory,
|
||||
) -> Result<Vec<InvSlotId>, Vec<(&RecipeInput, u32)>> {
|
||||
let mut slot_claims = HashMap::<InvSlotId, u32>::new();
|
||||
// Important to be a vec and to remain separate from slot_claims as it must
|
||||
// remain ordered, unlike the hashmap
|
||||
let mut slots = Vec::<InvSlotId>::new();
|
||||
let mut missing = Vec::<(&RecipeInput, u32)>::new();
|
||||
|
||||
for (input, mut needed) in self.inputs() {
|
||||
let mut contains_any = false;
|
||||
|
||||
for (inv_slot_id, slot) in inv.slots_with_id() {
|
||||
if let Some(item) = slot
|
||||
.as_ref()
|
||||
.filter(|item| item.matches_recipe_input(&*input))
|
||||
{
|
||||
let claim = slot_claims.entry(inv_slot_id).or_insert(0);
|
||||
slots.push(inv_slot_id);
|
||||
// FIXME: Fishy, looks like it can underflow before min which can trigger an
|
||||
// overflow check.
|
||||
let can_claim = (item.amount() - *claim).min(needed);
|
||||
*claim += can_claim;
|
||||
needed -= can_claim;
|
||||
contains_any = true;
|
||||
}
|
||||
}
|
||||
|
||||
if needed > 0 || !contains_any {
|
||||
missing.push((input, needed));
|
||||
}
|
||||
}
|
||||
|
||||
if missing.is_empty() {
|
||||
Ok(slots)
|
||||
} else {
|
||||
Err(missing)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum SalvageError {
|
||||
@ -108,7 +176,7 @@ impl RecipeBook {
|
||||
pub fn get_available(&self, inv: &Inventory) -> Vec<(String, Recipe)> {
|
||||
self.recipes
|
||||
.iter()
|
||||
.filter(|(_, recipe)| inv.contains_ingredients(recipe).is_ok())
|
||||
.filter(|(_, recipe)| recipe.inventory_contains_ingredients(inv).is_ok())
|
||||
.map(|(name, recipe)| (name.clone(), recipe.clone()))
|
||||
.collect()
|
||||
}
|
||||
|
@ -572,7 +572,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
|
||||
let msm = state.ecs().read_resource::<MaterialStatManifest>();
|
||||
|
||||
let crafted_items = match craft_event {
|
||||
CraftEvent::Simple(recipe) => recipe_book
|
||||
CraftEvent::Simple { recipe, slots } => recipe_book
|
||||
.get(&recipe)
|
||||
.filter(|r| {
|
||||
if let Some(needed_sprite) = r.craft_sprite {
|
||||
@ -604,19 +604,13 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
|
||||
}
|
||||
})
|
||||
.and_then(|r| {
|
||||
r.perform(
|
||||
r.craft_simple(
|
||||
&mut inventory,
|
||||
slots,
|
||||
&state.ecs().read_resource::<AbilityMap>(),
|
||||
&state.ecs().read_resource::<item::MaterialStatManifest>(),
|
||||
)
|
||||
.ok()
|
||||
})
|
||||
.map(|(crafted_item, amount)| {
|
||||
let mut crafted_items = Vec::with_capacity(amount as usize);
|
||||
for _ in 0..amount {
|
||||
crafted_items.push(crafted_item.duplicate(ability_map, &msm));
|
||||
}
|
||||
crafted_items
|
||||
}),
|
||||
CraftEvent::Salvage(slot) => {
|
||||
recipe::try_salvage(&mut inventory, slot, ability_map, &msm).ok()
|
||||
|
@ -1395,7 +1395,22 @@ impl PlayState for SessionState {
|
||||
recipe,
|
||||
craft_sprite,
|
||||
} => {
|
||||
self.client.borrow_mut().craft_recipe(&recipe, craft_sprite);
|
||||
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())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
if let Some(slots) = slots {
|
||||
self.client
|
||||
.borrow_mut()
|
||||
.craft_recipe(&recipe, slots, craft_sprite);
|
||||
}
|
||||
},
|
||||
HudEvent::SalvageItem(slot) => {
|
||||
self.client.borrow_mut().salvage_item(slot);
|
||||
|
Loading…
Reference in New Issue
Block a user