#![allow(dead_code)] use std::collections::{HashMap, BTreeMap}; use libpso::character::character;//::InventoryItem; use crate::entity::gateway::EntityGateway; use crate::entity::character::CharacterEntity; use crate::entity::item::{ItemEntityId, ItemEntity, ItemDetail, ItemLocation}; use crate::entity::item::{Meseta, NewItemEntity}; use crate::entity::item::tool::Tool; use crate::ship::map::MapArea; use crate::ship::drops::{ItemDrop, ItemDropType}; use crate::ship::ship::ClientState; #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct ActiveItemId(pub u32); #[derive(Debug)] enum ActiveItemEntityId { Individual(ItemEntityId), Stacked(Vec), Meseta(Meseta), } #[derive(Debug)] enum HeldItemType { Individual(ItemDetail), Stacked(Tool, usize), } impl HeldItemType { pub fn as_client_bytes(&self) -> [u8; 16] { match self { HeldItemType::Individual(item) => { match &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(), } }, HeldItemType::Stacked(tool, count) => { tool.as_stacked_bytes(*count) }, } } } #[derive(Debug)] pub struct InventoryItem { id: ActiveItemId, item: HeldItemType, //slot: usize, equipped: bool, } #[derive(Debug)] pub struct BankItem { id: ActiveItemId, item: HeldItemType, } #[derive(Debug)] pub enum FloorItemType { Individual(ItemDetail), Stacked(Tool, usize), Meseta(Meseta), } impl FloorItemType { pub fn as_client_bytes(&self) -> [u8; 16] { match self { FloorItemType::Individual(item) => { match &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(), } }, FloorItemType::Stacked(tool, count) => { tool.as_stacked_bytes(*count) }, FloorItemType::Meseta(m) => { m.as_bytes() } } } } #[derive(Debug)] pub struct FloorItem { pub id: ActiveItemId, pub item: FloorItemType, pub map_area: MapArea, pub x: f32, pub y: f32, pub z: f32, } #[derive(Debug)] pub enum InventoryError { } pub struct CharacterInventory(Vec); impl CharacterInventory { pub fn as_client_inventory_items(&self) -> [character::InventoryItem; 30] { self.0.iter() .enumerate() .fold([character::InventoryItem::default(); 30], |mut inventory, (slot, item)| { let bytes = item.item.as_client_bytes(); inventory[slot].data1.copy_from_slice(&bytes[0..12]); inventory[slot].data2.copy_from_slice(&bytes[12..16]); inventory[slot].item_id = item.id.0; // does this do anything? inventory[slot].equipped = if item.equipped { 1 } else { 0 }; // because this actually equips the item inventory[slot].flags |= if item.equipped { 8 } else { 0 }; inventory }) } pub fn add_item(&mut self, item: InventoryItem) -> Result { self.0.push(item); Ok(self.count() - 1) } pub fn count(&self) -> usize { self.0.len() } } #[derive(Debug)] pub enum ItemManagerError { EntityGatewayError, CouldNotAddToInventory(FloorItem), } pub struct ItemManager { id: usize, active_to_entity: HashMap, } impl ItemManager { pub fn new() -> ItemManager { ItemManager { id: 0, active_to_entity: HashMap::new() } } fn next_id(&mut self) -> ActiveItemId { self.id += 1; ActiveItemId(self.id as u32) } pub fn get_character_inventory(&mut self, entity_gateway: &mut EG, character: &CharacterEntity) -> CharacterInventory { let items = entity_gateway.get_items_by_character(&character); let inventory_items = items.into_iter() .filter_map(|item| { match item.location { ItemLocation::Inventory{slot, equipped, ..} => Some((item.id, item.item, slot, equipped)), _ => None, } }) .fold(BTreeMap::new(), |mut acc, (id, item, slot, equipped)| { if item.is_stackable() { if let ItemDetail::Tool(tool) = item { let stacked = acc.entry(slot).or_insert((HeldItemType::Stacked(tool, 0), ActiveItemEntityId::Stacked(Vec::new()), false)); if let HeldItemType::Stacked(_, ref mut item_count) = stacked.0 { *item_count += 1; } if let ActiveItemEntityId::Stacked(ref mut id_list) = stacked.1 { id_list.push(id); } } } else { acc.insert(slot, (HeldItemType::Individual(item), ActiveItemEntityId::Individual(id), equipped)); } acc }) .into_iter() .map(|(_slot, (held_item, entity_id, equipped))| { let id = self.next_id(); self.active_to_entity.insert(id, entity_id); InventoryItem { id: id, item: held_item, equipped: equipped, } }); CharacterInventory(inventory_items.take(30).collect()) } pub fn drop_item_on_local_floor(&mut self, entity_gateway: &mut EG, character: &CharacterEntity, item_drop: ItemDrop) -> Result { let item = match item_drop.item { ItemDropType::Weapon(w) => FloorItemType::Individual(ItemDetail::Weapon(w)), ItemDropType::Armor(w) => FloorItemType::Individual(ItemDetail::Armor(w)), ItemDropType::Shield(w) => FloorItemType::Individual(ItemDetail::Shield(w)), ItemDropType::Unit(w) => FloorItemType::Individual(ItemDetail::Unit(w)), ItemDropType::TechniqueDisk(w) => FloorItemType::Individual(ItemDetail::TechniqueDisk(w)), ItemDropType::Mag(w) => FloorItemType::Individual(ItemDetail::Mag(w)), ItemDropType::Tool(w) => FloorItemType::Individual(ItemDetail::Tool(w)), //ItemDropType::Tool(t) if t.is_stackable() => FloorItemType::Stacked(t, ), //ItemDropType::Tool(t) if !t.is_stackable() => FloorItemType::Individual(ItemDetail::Tool(w)), ItemDropType::Meseta(m) => FloorItemType::Meseta(Meseta(m)) }; let active_entity_ids = match &item { FloorItemType::Individual(i) => { let entity = entity_gateway.create_item(NewItemEntity { item: i.clone(), location: ItemLocation::LocalFloor { character_id: character.id, map_area: item_drop.map_area, x: item_drop.x, y: item_drop.y, z: item_drop.z, } }).ok_or(ItemManagerError::EntityGatewayError)?; ActiveItemEntityId::Individual(entity.id) }, FloorItemType::Stacked(tool, count) => { let entities = (0..*count).map(|_| { entity_gateway.create_item(NewItemEntity { item: ItemDetail::Tool(*tool), location: ItemLocation::LocalFloor { character_id: character.id, map_area: item_drop.map_area, x: item_drop.x, y: item_drop.y, z: item_drop.z, } })}) .map(|entity| -> Result { let e = entity.ok_or(ItemManagerError::EntityGatewayError)?; Ok(e.id) }); ActiveItemEntityId::Stacked(entities.collect::, _>>()?) }, FloorItemType::Meseta(m) => ActiveItemEntityId::Meseta(m.clone()), }; let id = self.next_id(); self.active_to_entity.insert(id, active_entity_ids); Ok(FloorItem { id: id, item: item, map_area: item_drop.map_area, x: item_drop.x, y: item_drop.y, z: item_drop.z, }) } pub fn move_item_from_floor_to_inventory(&mut self, entity_gateway: &mut EG, client: &mut ClientState, floor_item: FloorItem) -> Result<(), ItemManagerError> { match floor_item.item { FloorItemType::Individual(item) => { let inventory_item = InventoryItem { id: floor_item.id, item: HeldItemType::Individual(item.clone()), equipped: false, }; let item_entity_id = self.active_to_entity.get(&floor_item.id).unwrap(); // TODO: unwrap if let ActiveItemEntityId::Individual(item_id) = item_entity_id { let slot = client.inventory.add_item(inventory_item).unwrap(); // TODO: unwrap entity_gateway.save_item(&ItemEntity { id: *item_id, item: item, location: ItemLocation::Inventory { character_id: client.character.id, slot: slot, equipped: false, }, }); // TODO: error check } // else something went very wrong TODO: log it }, FloorItemType::Stacked(tool, usize) => { let inventory_item = InventoryItem { id: floor_item.id, item: HeldItemType::Stacked(tool, usize), equipped: false, }; let item_entity_id = self.active_to_entity.get(&floor_item.id).unwrap(); // TODO: unwrap if let ActiveItemEntityId::Stacked(item_ids) = item_entity_id { let slot = client.inventory.add_item(inventory_item).unwrap(); // TODO: unwrap for item_id in item_ids { entity_gateway.save_item(&ItemEntity { id: *item_id, item: ItemDetail::Tool(tool), location: ItemLocation::Inventory { character_id: client.character.id, slot: slot, equipped: false, }, }); // TODO: error check } } // else something went very wrong TODO: log it }, FloorItemType::Meseta(meseta) => { client.character.meseta += meseta.0; entity_gateway.save_character(&client.character); } } Ok(()) } }