2019-05-18 16:46:14 +00:00
|
|
|
pub mod item;
|
|
|
|
|
2019-07-25 12:46:54 +00:00
|
|
|
// Reexports
|
2019-10-22 18:18:40 +00:00
|
|
|
pub use item::{Debug, Item, ItemKind, Tool};
|
2019-07-25 12:46:54 +00:00
|
|
|
|
2019-10-22 18:18:40 +00:00
|
|
|
use crate::assets;
|
2019-08-01 15:07:42 +00:00
|
|
|
use specs::{Component, HashMapStorage, NullStorage};
|
2019-10-31 04:47:17 +00:00
|
|
|
use std::ops::Not;
|
2019-05-18 16:46:14 +00:00
|
|
|
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
|
|
pub struct Inventory {
|
|
|
|
pub slots: Vec<Option<Item>>,
|
|
|
|
}
|
|
|
|
|
2019-10-31 04:47:17 +00:00
|
|
|
/// Errors which the methods on `Inventory` produce
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum Error {
|
|
|
|
/// The inventory is full and items could not be added. The extra items have been returned.
|
|
|
|
Full(Vec<Item>),
|
|
|
|
}
|
|
|
|
|
2019-05-18 16:46:14 +00:00
|
|
|
impl Inventory {
|
2019-07-25 17:41:06 +00:00
|
|
|
pub fn slots(&self) -> &[Option<Item>] {
|
|
|
|
&self.slots
|
|
|
|
}
|
|
|
|
|
2019-07-25 22:52:28 +00:00
|
|
|
pub fn len(&self) -> usize {
|
|
|
|
self.slots.len()
|
|
|
|
}
|
|
|
|
|
2019-08-30 19:02:18 +00:00
|
|
|
/// Adds a new item to the first empty slot of the inventory. Returns the item again if no free
|
|
|
|
/// slot was found.
|
2019-08-28 20:47:52 +00:00
|
|
|
pub fn push(&mut self, item: Item) -> Option<Item> {
|
2019-07-25 22:52:28 +00:00
|
|
|
match self.slots.iter_mut().find(|slot| slot.is_none()) {
|
|
|
|
Some(slot) => {
|
|
|
|
*slot = Some(item);
|
2019-08-30 19:02:18 +00:00
|
|
|
None
|
2019-07-25 22:52:28 +00:00
|
|
|
}
|
2019-08-30 19:02:18 +00:00
|
|
|
None => Some(item),
|
2019-08-28 20:47:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-31 04:47:17 +00:00
|
|
|
/// Add a series of items to inventory, returning any which do not fit as an error.
|
2019-10-31 14:26:25 +00:00
|
|
|
pub fn push_all<I: Iterator<Item = Item>>(&mut self, mut items: I) -> Result<(), Error> {
|
2019-10-31 04:47:17 +00:00
|
|
|
// Vec doesn't allocate for zero elements so this should be cheap
|
|
|
|
let mut leftovers = Vec::new();
|
|
|
|
let mut slots = self.slots.iter_mut();
|
|
|
|
for item in &mut items {
|
|
|
|
if let Some(slot) = slots.find(|slot| slot.is_none()) {
|
|
|
|
slot.replace(item);
|
|
|
|
} else {
|
|
|
|
leftovers.push(item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if leftovers.len() > 0 {
|
|
|
|
Err(Error::Full(leftovers))
|
|
|
|
} else {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Add a series of items to an inventory without giving duplicates. (n * m complexity)
|
|
|
|
*
|
|
|
|
* Error if inventory cannot contain the items (is full), returning the un-added items.
|
|
|
|
* This is a lazy inefficient implementation, as it iterates over the inventory more times
|
|
|
|
* than necessary (n^2) and with the proper structure wouldn't need to iterate at all, but because
|
|
|
|
* this should be fairly cold code, clarity has been favored over efficiency.
|
|
|
|
*/
|
2019-10-31 14:26:25 +00:00
|
|
|
pub fn push_all_unique<I: Iterator<Item = Item>>(&mut self, mut items: I) -> Result<(), Error> {
|
2019-10-31 04:47:17 +00:00
|
|
|
let mut leftovers = Vec::new();
|
|
|
|
for item in &mut items {
|
|
|
|
if self.contains(&item).not() {
|
2019-10-31 14:26:25 +00:00
|
|
|
self.push(item).map(|overflow| leftovers.push(overflow));
|
2019-10-31 04:47:17 +00:00
|
|
|
} // else drop item if it was already in
|
|
|
|
}
|
|
|
|
if leftovers.len() > 0 {
|
|
|
|
Err(Error::Full(leftovers))
|
|
|
|
} else {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-30 22:09:25 +00:00
|
|
|
/// Replaces an item in a specific slot of the inventory. Returns the old item or the same item again if that slot
|
2019-08-30 19:02:18 +00:00
|
|
|
/// was not found.
|
2019-08-30 22:09:25 +00:00
|
|
|
pub fn insert(&mut self, cell: usize, item: Item) -> Result<Option<Item>, Item> {
|
2019-08-28 20:47:52 +00:00
|
|
|
match self.slots.get_mut(cell) {
|
|
|
|
Some(slot) => {
|
2019-08-30 22:09:25 +00:00
|
|
|
let old = slot.take();
|
|
|
|
*slot = Some(item);
|
|
|
|
Ok(old)
|
2019-08-28 20:47:52 +00:00
|
|
|
}
|
2019-08-30 22:09:25 +00:00
|
|
|
None => Err(item),
|
2019-07-25 22:52:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-25 22:53:43 +00:00
|
|
|
pub fn is_full(&self) -> bool {
|
|
|
|
self.slots.iter().all(|slot| slot.is_some())
|
|
|
|
}
|
|
|
|
|
2019-10-31 04:47:17 +00:00
|
|
|
/// O(n) count the number of items in this inventory.
|
|
|
|
pub fn count(&self) -> usize {
|
2019-10-31 14:26:25 +00:00
|
|
|
self.slots
|
|
|
|
.iter()
|
|
|
|
.fold(0, |count, slot| count + if slot.is_some() { 1 } else { 0 })
|
2019-10-31 04:47:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// O(n) check if an item is in this inventory.
|
|
|
|
pub fn contains(&self, item: &Item) -> bool {
|
2019-10-31 14:26:25 +00:00
|
|
|
self.slots.iter().any(|slot| {
|
|
|
|
slot.as_ref()
|
|
|
|
.map(|other_item| item == other_item)
|
|
|
|
.unwrap_or(false)
|
|
|
|
})
|
2019-10-31 04:47:17 +00:00
|
|
|
}
|
|
|
|
|
2019-08-30 20:42:43 +00:00
|
|
|
/// Get content of a slot
|
|
|
|
pub fn get(&self, cell: usize) -> Option<&Item> {
|
|
|
|
self.slots.get(cell).and_then(Option::as_ref)
|
2019-05-18 16:46:14 +00:00
|
|
|
}
|
|
|
|
|
2019-08-30 20:42:43 +00:00
|
|
|
/// Swap the items inside of two slots
|
2019-07-25 22:52:28 +00:00
|
|
|
pub fn swap_slots(&mut self, a: usize, b: usize) {
|
|
|
|
if a.max(b) < self.slots.len() {
|
|
|
|
self.slots.swap(a, b);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-30 20:42:43 +00:00
|
|
|
/// Remove an item from the slot
|
2019-07-25 12:46:54 +00:00
|
|
|
pub fn remove(&mut self, cell: usize) -> Option<Item> {
|
|
|
|
self.slots.get_mut(cell).and_then(|item| item.take())
|
2019-05-18 16:46:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-25 14:02:05 +00:00
|
|
|
impl Default for Inventory {
|
|
|
|
fn default() -> Inventory {
|
2019-08-26 18:05:13 +00:00
|
|
|
let mut inventory = Inventory {
|
2019-10-17 20:59:36 +00:00
|
|
|
slots: vec![None; 25],
|
2019-07-25 22:52:28 +00:00
|
|
|
};
|
|
|
|
|
2019-10-22 18:18:40 +00:00
|
|
|
inventory.push(assets::load_expect_cloned("common.items.debug.boost"));
|
|
|
|
inventory.push(assets::load_expect_cloned("common.items.debug.possess"));
|
2019-08-26 18:05:13 +00:00
|
|
|
inventory
|
2019-07-25 14:02:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-18 16:46:14 +00:00
|
|
|
impl Component for Inventory {
|
2019-07-31 09:30:46 +00:00
|
|
|
type Storage = HashMapStorage<Self>;
|
2019-05-18 16:46:14 +00:00
|
|
|
}
|
2019-07-25 14:48:27 +00:00
|
|
|
|
|
|
|
// ForceUpdate
|
|
|
|
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)]
|
|
|
|
pub struct InventoryUpdate;
|
|
|
|
|
|
|
|
impl Component for InventoryUpdate {
|
|
|
|
type Storage = NullStorage<Self>;
|
|
|
|
}
|
2019-10-31 04:47:17 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use super::*;
|
2019-10-31 14:26:25 +00:00
|
|
|
use lazy_static::lazy_static;
|
2019-10-31 04:47:17 +00:00
|
|
|
lazy_static! {
|
2019-10-31 14:26:25 +00:00
|
|
|
static ref TEST_ITEMS: Vec<Item> = vec![
|
|
|
|
assets::load_expect_cloned("common.items.debug.boost"),
|
|
|
|
assets::load_expect_cloned("common.items.debug.possess")
|
|
|
|
];
|
2019-10-31 04:47:17 +00:00
|
|
|
}
|
|
|
|
/// The `Default` inventory should contain two items
|
|
|
|
#[test]
|
|
|
|
fn create_default_count() {
|
|
|
|
assert_eq!(Inventory::default().count(), 2)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Attempting to push into a full inventory should return the same item.
|
|
|
|
#[test]
|
|
|
|
fn push_full() {
|
2019-10-31 14:26:25 +00:00
|
|
|
let mut inv = Inventory {
|
|
|
|
slots: TEST_ITEMS.iter().map(|a| Some(a.clone())).collect(),
|
|
|
|
};
|
2019-10-31 04:47:17 +00:00
|
|
|
assert_eq!(
|
|
|
|
inv.push(TEST_ITEMS[0].clone()).unwrap(),
|
|
|
|
TEST_ITEMS[0].clone()
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Attempting to push a series into a full inventory should return them all.
|
|
|
|
#[test]
|
|
|
|
fn push_all_full() {
|
2019-10-31 14:26:25 +00:00
|
|
|
let mut inv = Inventory {
|
|
|
|
slots: TEST_ITEMS.iter().map(|a| Some(a.clone())).collect(),
|
|
|
|
};
|
|
|
|
let Error::Full(leftovers) = inv
|
|
|
|
.push_all(TEST_ITEMS.iter().map(|a| a.clone()))
|
2019-10-31 04:47:17 +00:00
|
|
|
.expect_err("Pushing into a full inventory somehow worked!");
|
2019-10-31 14:26:25 +00:00
|
|
|
assert_eq!(leftovers, TEST_ITEMS.clone())
|
2019-10-31 04:47:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Attempting to push uniquely into an inventory containing all the items should work fine.
|
|
|
|
#[test]
|
|
|
|
fn push_unique_all_full() {
|
2019-10-31 14:26:25 +00:00
|
|
|
let mut inv = Inventory {
|
|
|
|
slots: TEST_ITEMS.iter().map(|a| Some(a.clone())).collect(),
|
|
|
|
};
|
2019-10-31 04:47:17 +00:00
|
|
|
inv.push_all_unique(TEST_ITEMS.iter().map(|a| a.clone()))
|
2019-10-31 14:26:25 +00:00
|
|
|
.expect(
|
|
|
|
"Pushing unique items into an inventory that already contains them didn't work!",
|
|
|
|
);
|
2019-10-31 04:47:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Attempting to push uniquely into an inventory containing all the items should work fine.
|
|
|
|
#[test]
|
|
|
|
fn push_all_empty() {
|
2019-10-31 14:26:25 +00:00
|
|
|
let mut inv = Inventory {
|
|
|
|
slots: vec![None, None],
|
|
|
|
};
|
2019-10-31 04:47:17 +00:00
|
|
|
inv.push_all(TEST_ITEMS.iter().map(|a| a.clone()))
|
|
|
|
.expect("Pushing items into an empty inventory didn't work!");
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Attempting to push uniquely into an inventory containing all the items should work fine.
|
|
|
|
#[test]
|
|
|
|
fn push_all_unique_empty() {
|
2019-10-31 14:26:25 +00:00
|
|
|
let mut inv = Inventory {
|
|
|
|
slots: vec![None, None],
|
|
|
|
};
|
2019-10-31 04:47:17 +00:00
|
|
|
inv.push_all_unique(TEST_ITEMS.iter().map(|a| a.clone()))
|
2019-10-31 14:26:25 +00:00
|
|
|
.expect(
|
|
|
|
"Pushing unique items into an empty inventory that didn't contain them didn't work!",
|
|
|
|
);
|
2019-10-31 04:47:17 +00:00
|
|
|
}
|
|
|
|
}
|