diff --git a/src/ship/items.rs b/src/ship/items.rs new file mode 100644 index 0000000..b6f7c8c --- /dev/null +++ b/src/ship/items.rs @@ -0,0 +1,370 @@ +use std::collections::HashMap; +use std::hash::{Hash, Hasher}; + +use libpso::character::character::InventoryItem; +use libpso::item; + +use crate::entity::item::{Item, ItemDetail, ItemLocation, Weapon, Armor, Shield}; + + + +fn are_items_same_type(itema: &Item, itemb: &Item) -> bool { + match (&itema.item, &itemb.item) { + (ItemDetail::Weapon(a), ItemDetail::Weapon(b)) => a.weapon.weapon == b.weapon.weapon, + (ItemDetail::Armor(a), ItemDetail::Armor(b)) => a.armor.armor == b.armor.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) => { + item::tool::Tool { + 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.weapon.value().hash(hasher), + ItemDetail::Armor(a) => a.armor.armor.value().hash(hasher), + ItemDetail::Shield(s) => s.shield.shield.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)| { + log::warn!("inv item: {:?}", item); + if let Some(i) = item { + let bytes = i.item.as_bytes(); + log::warn!("inv item bytes:{:?} ", 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::{Item, ItemDetail, ItemEntityId, ItemLocation, Weapon, Tool}; + use libpso::item; + + #[test] + fn test_stacked_items() { + let item1 = Item { + id: ItemEntityId(1), + location: ItemLocation::Inventory { + character_id: 0, + index: 0, + }, + item: ItemDetail::Weapon(Weapon { + equipped: false, + weapon: item::weapon::Weapon { + weapon: item::weapon::WeaponType::Saber, + grind: 0, + special: None, + attrs: [None; 3] + } + }) + }; + 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(Weapon { + equipped: false, + weapon: item::weapon::Weapon { + weapon: item::weapon::WeaponType::Handgun, + grind: 12, + special: None, + attrs: [None; 3] + } + }) + }; + 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(Weapon { + equipped: false, + weapon: item::weapon::Weapon { + weapon: item::weapon::WeaponType::Handgun, + grind: 12, + special: None, + attrs: [None; 3] + } + }) + }; + 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); + } +}