use std::collections::HashMap; use libpso::character::character::InventoryItem; use crate::entity::gateway::EntityGateway; use crate::entity::character::CharacterEntity; use crate::entity::item::{ItemEntity, ItemId, ItemDetail, ItemLocation}; use crate::entity::item::weapon::Weapon; use crate::entity::item::armor::Armor; use crate::entity::item::shield::Shield; use crate::entity::item::unit::Unit; use crate::entity::item::tool::Tool; use crate::entity::item::mag::Mag; use crate::entity::item::{Meseta, NewItemEntity}; use crate::ship::map::MapArea; use crate::ship::drops::{ItemDrop, ItemDropType}; use crate::ship::ship::ShipError; #[derive(Debug, PartialEq)] enum ItemInstance { Individual(ItemEntity), Stacked(Vec), Meseta(Meseta), } #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct ActiveItemId(pub u32); #[derive(Debug)] pub struct ActiveItem { pub id: ActiveItemId, item: ItemInstance, } impl ActiveItem { pub fn as_client_bytes(&self) -> [u8; 16] { match &self.item { ItemInstance::Individual(i) => { match &i.item { ItemDetail::Weapon(w) => w.as_bytes(), ItemDetail::Armor(a) => a.as_bytes(), ItemDetail::Shield(s) => s.as_bytes(), ItemDetail::Unit(u) => u.as_bytes(), ItemDetail::Tool(t) => t.as_individual_bytes(), ItemDetail::TechniqueDisk(d) => d.as_bytes(), ItemDetail::Mag(m) => m.as_bytes(), } }, ItemInstance::Stacked(i) => { let len = i.len(); match &i[0].item { ItemDetail::Tool(t) => t.as_stacked_bytes(len), _ => panic!(), } }, ItemInstance::Meseta(m) => { m.as_bytes() } } } } pub struct ActiveInventory(Vec); impl ActiveInventory { pub fn as_client_inventory_items(&self) -> [InventoryItem; 30] { self.0.iter() .enumerate() .fold([InventoryItem::default(); 30], |mut inventory, (index, item)| { let bytes = item.as_client_bytes(); inventory[index].data1.copy_from_slice(&bytes[0..12]); inventory[index].data2.copy_from_slice(&bytes[12..16]); inventory[index].item_id = item.id.0; // does this do anything? inventory[index].equipped = match item.item { ItemInstance::Individual(ItemEntity {location: ItemLocation::Inventory{ equipped: true, ..}, ..}) => 1, _ => 0, }; // because this actually equips the item inventory[index].flags |= match item.item { ItemInstance::Individual(ItemEntity {location: ItemLocation::Inventory{ equipped: true, ..}, ..}) => 8, _ => 0, }; inventory }) } pub fn count(&self) -> usize { self.0.len() } } fn inventory_item_index(item: &ItemInstance) -> usize { match item { ItemInstance::Individual(i) => { match i.location { ItemLocation::Inventory{index: index, ..} => index, _ => panic!() } }, ItemInstance::Stacked(i) => { match i[0].location { ItemLocation::Inventory{index: index, ..} => index, _ => panic!() } }, _ => panic!(), } } pub struct ActiveItemOnFloor { pub map_area: MapArea, pub x: f32, pub y: f32, pub z: f32, pub item: ActiveItem, } fn stack_items(items: Vec) -> Vec { let mut stacks = HashMap::new(); for item in items { stacks.entry(item.item.item_type()).or_insert(Vec::new()).push(item); } stacks.into_iter() .map(|(itype, items)| { match items[0].item.is_stackable() { true => { vec![ItemInstance::Stacked(items)] }, false => { items.into_iter().map(|i| { ItemInstance::Individual(i) }).collect() } } }) .flatten() .collect() } struct ActiveBank([Option; 200]); pub struct ActiveItemDatabase { id: u32, } impl ActiveItemDatabase { pub fn new() -> ActiveItemDatabase { ActiveItemDatabase { id: 0, } } fn activate_item(&mut self, item: ItemInstance) -> ActiveItem { self.id += 1; ActiveItem { id: ActiveItemId(self.id), item: item, } } // TODO: deactivate item pub fn get_character_inventory(&mut self, entity_gateway: &mut EG, character: &CharacterEntity) -> ActiveInventory { let items = entity_gateway.get_items_by_character(&character); let inventory_items = items.into_iter() .filter(|item| { match item.location { ItemLocation::Inventory{..} => true, _ => false, } }).collect(); let mut stacked = stack_items(inventory_items); stacked.sort_by(|a, b| { inventory_item_index(a).partial_cmp(&inventory_item_index(b)).unwrap() }); let activated = stacked.into_iter().map(|i| self.activate_item(i)); ActiveInventory(activated.take(30).collect()) } pub fn activate_item_drop(&mut self, entity_gateway: &mut EG, item_drop: ItemDrop) -> Result { let item_detail = match item_drop.item { ItemDropType::Weapon(w) => Some(ItemDetail::Weapon(w)), ItemDropType::Armor(w) => Some(ItemDetail::Armor(w)), ItemDropType::Shield(w) => Some(ItemDetail::Shield(w)), ItemDropType::Unit(w) => Some(ItemDetail::Unit(w)), ItemDropType::Tool(w) => Some(ItemDetail::Tool(w)), ItemDropType::TechniqueDisk(w) => Some(ItemDetail::TechniqueDisk(w)), ItemDropType::Mag(w) => Some(ItemDetail::Mag(w)), ItemDropType::Meseta(_) => None }; let item_instance = match item_detail { Some(item) => { let item_entity = entity_gateway.create_item(NewItemEntity { item: item, location: ItemLocation::Floor { map_area: item_drop.map_area, x: item_drop.x, y: item_drop.y, z: item_drop.z, } }).unwrap(); stack_items(vec![item_entity]).pop().ok_or(ShipError::ItemError)? }, None => { let meseta = match item_drop.item { ItemDropType::Meseta(m) => m, _ => panic!(), }; ItemInstance::Meseta(Meseta(meseta)) } }; let active_item = self.activate_item(item_instance); Ok(ActiveItemOnFloor { map_area: item_drop.map_area, x: item_drop.x, y: item_drop.y, z: item_drop.z, item: active_item, }) } } #[cfg(test)] mod test { use super::*; use crate::entity::character::CharacterEntityId; use crate::entity::item; use crate::entity::item::{ItemEntity, ItemDetail, ItemEntityId, ItemLocation}; #[test] fn test_stack_items() { let item1 = ItemEntity { id: ItemEntityId(1), location: ItemLocation::Inventory { character_id: CharacterEntityId(0), index: 0, equipped: false, }, item: ItemDetail::Weapon(item::weapon::Weapon { weapon: item::weapon::WeaponType::Saber, grind: 0, special: None, attrs: [None; 3], tekked: true, }) }; let item2 = ItemEntity { id: ItemEntityId(2), location: ItemLocation::Inventory { character_id: CharacterEntityId(0), index: 1, equipped: false, }, item: ItemDetail::Tool(Tool { tool: item::tool::ToolType::Monofluid, }) }; let item3 = ItemEntity { id: ItemEntityId(3), location: ItemLocation::Inventory { character_id: CharacterEntityId(0), index: 2, equipped: false, }, item: ItemDetail::Weapon(item::weapon::Weapon { weapon: item::weapon::WeaponType::Handgun, grind: 12, special: None, attrs: [None; 3], tekked: true, }) }; let item4 = ItemEntity { id: ItemEntityId(4), location: ItemLocation::Inventory { character_id: CharacterEntityId(0), index: 1, equipped: false, }, item: ItemDetail::Tool(Tool { tool: item::tool::ToolType::Monofluid, }) }; let item5 = ItemEntity { id: ItemEntityId(5), location: ItemLocation::Inventory { character_id: CharacterEntityId(0), index: 1, equipped: false, }, item: ItemDetail::Tool(Tool { tool: item::tool::ToolType::Monofluid, }) }; let item6 = ItemEntity { id: ItemEntityId(6), location: ItemLocation::Inventory { character_id: CharacterEntityId(0), index: 3, equipped: false, }, item: ItemDetail::Weapon(item::weapon::Weapon { weapon: item::weapon::WeaponType::Handgun, grind: 12, special: None, attrs: [None; 3], tekked: true, }) }; let item7 = ItemEntity { id: ItemEntityId(7), location: ItemLocation::Inventory { character_id: CharacterEntityId(0), index: 4, equipped: false, }, item: ItemDetail::Tool(Tool { tool: item::tool::ToolType::Monomate, }) }; let item8 = ItemEntity { id: ItemEntityId(8), location: ItemLocation::Inventory { character_id: CharacterEntityId(0), index: 4, equipped: false, }, item: ItemDetail::Tool(Tool { tool: item::tool::ToolType::Monomate, }) }; let item9 = ItemEntity { id: ItemEntityId(9), location: ItemLocation::Inventory { character_id: CharacterEntityId(0), index: 4, equipped: false, }, 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 == ItemInstance::Individual(item6.clone()) }).count() == 1); assert!(stacked.iter().filter(|k| { **k == ItemInstance::Individual(item3.clone()) }).count() == 1); assert!(stacked.iter().filter(|k| { **k == ItemInstance::Individual(item1.clone()) }).count() == 1); assert!(stacked.iter().filter(|k| { **k == ItemInstance::Stacked(vec![item2.clone(), item4.clone(), item5.clone()]) }).count() == 1); assert!(stacked.iter().filter(|k| { **k == ItemInstance::Stacked(vec![item7.clone(), item8.clone(), item9.clone()]) }).count() == 1); } }