use std::collections::HashMap; use std::hash::{Hash, Hasher}; use libpso::character::character::InventoryItem; use crate::entity::item::{Item, ItemDetail, ItemLocation, Shield}; use crate::entity::item::weapon::Weapon; use crate::entity::item::armor::Armor; use crate::entity::item::tool::StackedTool; fn are_items_same_type(itema: &Item, itemb: &Item) -> bool { match (&itema.item, &itemb.item) { (ItemDetail::Weapon(a), ItemDetail::Weapon(b)) => a.weapon == b.weapon, (ItemDetail::Armor(a), ItemDetail::Armor(b)) => a.armor == b.armor, (ItemDetail::Shield(a), ItemDetail::Shield(b)) => a.shield.shield == b.shield.shield, (ItemDetail::Tool(a), ItemDetail::Tool(b)) => a.tool == b.tool, _ => false } } #[derive(Debug)] pub struct ActiveItemId(u32); // TODO: Stacked(count, itemtype Vec)? #[derive(Debug, PartialEq)] pub enum StackedItem { Individual(Item), Stacked(Vec), } impl StackedItem { fn is_same_item_type(&self, item: &Item) -> bool { match self { StackedItem::Individual(i) => are_items_same_type(i, item), StackedItem::Stacked(i) => i.iter().all(|k| are_items_same_type(k, item)) } } fn index(&self) -> usize { match self { StackedItem::Individual(Item {location: ItemLocation::Inventory {index, ..}, ..}) => *index, StackedItem::Stacked(items) => { match items[0].location { ItemLocation::Inventory {index, ..} => index, _ => panic!() } } _ => panic!() } } fn as_bytes(&self) -> [u8; 16] { match self { StackedItem::Individual(item) => { item.item.as_bytes() } StackedItem::Stacked(items) => { let count = items.len(); match &items[0].item { ItemDetail::Tool(tool) => { StackedTool { tool: tool.tool, count: count }.as_bytes() }, _ => panic!() } } } } } pub struct ItemActivator { id: u32, } impl ItemActivator { pub fn new() -> ItemActivator { ItemActivator { id: 0 } } pub fn activate_item(&mut self, item: StackedItem) -> ActiveItem { self.id += 1; ActiveItem { id: ActiveItemId(self.id), item: item, } } } #[derive(Debug)] pub struct ActiveItem { pub id: ActiveItemId, pub item: StackedItem, } struct StackedItemKey(Item); impl Hash for StackedItemKey { fn hash(&self, hasher: &mut H) { match &self.0.item { ItemDetail::Weapon(w) => w.weapon.value().hash(hasher), ItemDetail::Armor(a) => a.armor.value().hash(hasher), ItemDetail::Shield(s) => s.shield.shield.value().hash(hasher), ItemDetail::Unit(u) => u.unit.unit.value().hash(hasher), ItemDetail::Tool(t) => t.tool.value().hash(hasher), } } } impl std::cmp::PartialEq for StackedItemKey { fn eq(&self, other: &StackedItemKey) -> bool { are_items_same_type(&self.0, &other.0) } } impl Eq for StackedItemKey {} pub fn stack_items(items: Vec) -> Vec { items.into_iter() .fold(HashMap::new(), |mut stacked: HashMap>, item| { stacked.entry(StackedItemKey(item.clone())) .and_modify(|stack| stack.push(item.clone())) .or_insert_with(|| { vec![item] }); stacked }) .into_iter() .map(|(_k, v)| { v }) .fold(Vec::new(), |mut stacked, item| { if item[0].item.is_stackable() { stacked.push(StackedItem::Stacked(item)) } else { stacked.append(&mut item.into_iter().map(|k| StackedItem::Individual(k)).collect()) } stacked }) } pub enum InventoryAddError { InventoryFull, FullToolStack, } pub enum InventoryRemoveError { NoItemInInventory, } pub struct Inventory([Option; 30]); impl Inventory { pub fn new(items: Vec) -> Inventory { items.into_iter() .fold(Inventory([None; 30]), |mut inventory, item| { let index = item.item.index(); inventory.0[index] = Some(item); inventory }) } pub fn count(&self) -> usize { self.0.iter() .filter(|k| k.is_some()) .count() } pub fn as_client_inventory_items(&self) -> [InventoryItem; 30] { self.0.iter() .enumerate() .fold([InventoryItem::default(); 30], |mut inventory, (index, item)| { if let Some(i) = item { let bytes = i.item.as_bytes(); inventory[index].data1.copy_from_slice(&bytes[0..12]); inventory[index].item_id = i.id.0; // does this do anything? inventory[index].equipped = match i.item { StackedItem::Individual(Item {item: ItemDetail::Weapon(Weapon {equipped: true, ..}), ..}) => 1, StackedItem::Individual(Item {item: ItemDetail::Armor(Armor {equipped: true, ..}), ..}) => 1, StackedItem::Individual(Item {item: ItemDetail::Shield(Shield {equipped: true, ..}), ..}) => 1, _ => 0, }; // because this actually equips the item inventory[index].flags |= match i.item { StackedItem::Individual(Item {item: ItemDetail::Weapon(Weapon {equipped: true, ..}), ..}) => 8, StackedItem::Individual(Item {item: ItemDetail::Armor(Armor {equipped: true, ..}), ..}) => 8, StackedItem::Individual(Item {item: ItemDetail::Shield(Shield {equipped: true, ..}), ..}) => 8, _ => 0, }; } inventory }) } } pub struct Bank {} pub fn split_items_into_inventory_and_bank(items: Vec) -> (Vec, Vec) { items.into_iter().partition(|item| { match item.location { ItemLocation::Inventory{..} => true, ItemLocation::Bank{..} => false, ItemLocation::Floor{..} => panic!("oh god what happened"), } }) } mod test { use super::*; use crate::entity::item; use crate::entity::item::{Item, ItemDetail, ItemEntityId, ItemLocation, Tool}; #[test] fn test_stacked_items() { let item1 = Item { id: ItemEntityId(1), location: ItemLocation::Inventory { character_id: 0, index: 0, }, item: ItemDetail::Weapon(item::weapon::Weapon { weapon: item::weapon::WeaponType::Saber, grind: 0, special: None, attrs: [None; 3], equipped: false, tekked: true, }) }; let item2 = Item { id: ItemEntityId(2), location: ItemLocation::Inventory { character_id: 0, index: 1 }, item: ItemDetail::Tool(Tool { tool: item::tool::ToolType::Monofluid, }) }; let item3 = Item { id: ItemEntityId(3), location: ItemLocation::Inventory { character_id: 0, index: 2, }, item: ItemDetail::Weapon(item::weapon::Weapon { weapon: item::weapon::WeaponType::Handgun, grind: 12, special: None, attrs: [None; 3], equipped: false, tekked: true, }) }; let item4 = Item { id: ItemEntityId(4), location: ItemLocation::Inventory { character_id: 0, index: 1 }, item: ItemDetail::Tool(Tool { tool: item::tool::ToolType::Monofluid, }) }; let item5 = Item { id: ItemEntityId(5), location: ItemLocation::Inventory { character_id: 0, index: 1 }, item: ItemDetail::Tool(Tool { tool: item::tool::ToolType::Monofluid, }) }; let item6 = Item { id: ItemEntityId(6), location: ItemLocation::Inventory { character_id: 0, index: 3, }, item: ItemDetail::Weapon(item::weapon::Weapon { weapon: item::weapon::WeaponType::Handgun, grind: 12, special: None, attrs: [None; 3], equipped: false, tekked: true, }) }; let item7 = Item { id: ItemEntityId(7), location: ItemLocation::Inventory { character_id: 0, index: 4 }, item: ItemDetail::Tool(Tool { tool: item::tool::ToolType::Monomate, }) }; let item8 = Item { id: ItemEntityId(8), location: ItemLocation::Inventory { character_id: 0, index: 4 }, item: ItemDetail::Tool(Tool { tool: item::tool::ToolType::Monomate, }) }; let item9 = Item { id: ItemEntityId(9), location: ItemLocation::Inventory { character_id: 0, index: 4 }, item: ItemDetail::Tool(Tool { tool: item::tool::ToolType::Monomate, }) }; let item_vec = vec![item1.clone(), item2.clone(), item3.clone(), item4.clone(), item5.clone(), item6.clone(), item7.clone(), item8.clone(), item9.clone()]; let stacked = stack_items(item_vec); assert!(stacked.len() == 5); assert!(stacked.iter().filter(|k| { **k == StackedItem::Individual(item6.clone()) }).count() == 1); assert!(stacked.iter().filter(|k| { **k == StackedItem::Individual(item3.clone()) }).count() == 1); assert!(stacked.iter().filter(|k| { **k == StackedItem::Individual(item1.clone()) }).count() == 1); assert!(stacked.iter().filter(|k| { **k == StackedItem::Stacked(vec![item2.clone(), item4.clone(), item5.clone()]) }).count() == 1); assert!(stacked.iter().filter(|k| { **k == StackedItem::Stacked(vec![item7.clone(), item8.clone(), item9.clone()]) }).count() == 1); } }