diff --git a/src/entity/item/mod.rs b/src/entity/item/mod.rs index 9dc4739..aab6d8c 100644 --- a/src/entity/item/mod.rs +++ b/src/entity/item/mod.rs @@ -36,26 +36,25 @@ pub enum ItemLocation { */ } -#[derive(Clone, Debug, PartialEq)] -pub struct Tool { - pub tool: tool::ToolType, -} -impl Tool { - pub fn as_bytes(&self) -> [u8; 16] { - let mut result = [0; 16]; - result[0..3].copy_from_slice(&self.tool.value()); - result - } +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum ItemType { + Weapon(weapon::WeaponType), + Armor(armor::ArmorType), + Shield(shield::ShieldType), + Unit(unit::UnitType), + Tool(tool::ToolType), + TechniqueDisk(tech::Technique) } + #[derive(Clone, Debug, PartialEq)] pub enum ItemDetail { Weapon(weapon::Weapon), Armor(armor::Armor), Shield(shield::Shield), Unit(unit::Unit), - Tool(Tool), + Tool(tool::Tool), TechniqueDisk(tech::TechniqueDisk) } @@ -67,14 +66,14 @@ impl ItemDetail { } } - pub fn as_bytes(&self) -> [u8; 16] { + pub fn item_type(&self) -> ItemType { match self { - ItemDetail::Weapon(weapon) => weapon.as_bytes(), - ItemDetail::Armor(armor) => armor.as_bytes(), - ItemDetail::Shield(shield) => shield.as_bytes(), - ItemDetail::Unit(unit) => unit.as_bytes(), - ItemDetail::Tool(tool) => tool.as_bytes(), - ItemDetail::TechniqueDisk(tech) => tech.as_bytes(), + ItemDetail::Weapon(w) => ItemType::Weapon(w.weapon), + ItemDetail::Armor(a) => ItemType::Armor(a.armor), + ItemDetail::Shield(s) => ItemType::Shield(s.shield), + ItemDetail::Unit(u) => ItemType::Unit(u.unit), + ItemDetail::Tool(t) => ItemType::Tool(t.tool), + ItemDetail::TechniqueDisk(d) => ItemType::TechniqueDisk(d.tech), } } } diff --git a/src/entity/item/tech.rs b/src/entity/item/tech.rs index 2e2551a..c2e3f85 100644 --- a/src/entity/item/tech.rs +++ b/src/entity/item/tech.rs @@ -1,7 +1,7 @@ use serde::{Serialize, Deserialize}; -#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum Technique { Foie, Gifoie, diff --git a/src/entity/item/tool.rs b/src/entity/item/tool.rs index a1e7d23..fe7caee 100644 --- a/src/entity/item/tool.rs +++ b/src/entity/item/tool.rs @@ -1,6 +1,6 @@ use serde::{Serialize, Deserialize}; -#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize, enum_utils::FromStr, derive_more::Display)] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Serialize, Deserialize, enum_utils::FromStr, derive_more::Display)] pub enum ToolType { Monomate, Dimate, @@ -394,25 +394,21 @@ impl ToolType { } -#[derive(Debug, Copy, Clone, PartialEq)] -pub struct StackedTool { +#[derive(Clone, Debug, PartialEq)] +pub struct Tool { pub tool: ToolType, - pub count: usize, } -impl StackedTool { - pub fn new(tool: ToolType, count: usize) -> StackedTool { - StackedTool { - tool: tool, - count: count, - } +impl Tool { + pub fn as_individual_bytes(&self) -> [u8; 16] { + let mut result = [0; 16]; + result[0..3].copy_from_slice(&self.tool.value()); + result } - - pub fn as_bytes(&self) -> [u8; 16] { + pub fn as_stacked_bytes(&self, len: usize) -> [u8; 16] { let mut result = [0; 16]; result[0..3].copy_from_slice(&self.tool.value()); - result[5] = self.count as u8; - + result[5] = len as u8; result } } diff --git a/src/entity/item/weapon.rs b/src/entity/item/weapon.rs index 089c1a1..8e43a12 100644 --- a/src/entity/item/weapon.rs +++ b/src/entity/item/weapon.rs @@ -61,7 +61,7 @@ pub enum WeaponSpecial { Demons, } -#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize, enum_utils::FromStr, derive_more::Display)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, enum_utils::FromStr, derive_more::Display)] pub enum WeaponType { Saber, Brand, diff --git a/src/login/character.rs b/src/login/character.rs index 129f306..faf3971 100644 --- a/src/login/character.rs +++ b/src/login/character.rs @@ -17,10 +17,11 @@ use libpso::{utf8_to_array, utf8_to_utf16_array}; use crate::entity::gateway::EntityGateway; use crate::entity::account::{UserAccount, USERFLAG_NEWCHAR, USERFLAG_DRESSINGROOM}; -use crate::entity::item::{ItemDetail, ItemLocation, Tool}; +use crate::entity::item::{ItemDetail, ItemLocation}; use crate::entity::item::weapon::Weapon; use crate::entity::item::armor::Armor; use crate::entity::item::tech::Technique; +use crate::entity::item::tool::Tool; use crate::entity::character::{Character, CharacterClass, TechLevel}; use crate::login::login::get_login_status; diff --git a/src/ship/character.rs b/src/ship/character.rs index f6334bc..df38c37 100644 --- a/src/ship/character.rs +++ b/src/ship/character.rs @@ -1,7 +1,7 @@ use libpso::character::character; use crate::common::leveltable::CharacterStats; use crate::entity::character::Character; -use crate::ship::items::Inventory; +use crate::ship::items::ActiveInventory; // TODO: exp pub struct CharacterBytesBuilder<'a> { @@ -79,7 +79,7 @@ pub struct FullCharacterBytesBuilder<'a> { character: Option<&'a Character>, stats: Option<&'a CharacterStats>, level: Option, - inventory: Option<&'a Inventory>, + inventory: Option<&'a ActiveInventory>, key_config: Option<&'a [u8; 0x16C]>, joystick_config: Option<&'a [u8; 0x38]>, } @@ -118,7 +118,7 @@ impl<'a> FullCharacterBytesBuilder<'a> { } } - pub fn inventory(self, inventory: &'a Inventory) -> FullCharacterBytesBuilder<'a> { + pub fn inventory(self, inventory: &'a ActiveInventory) -> FullCharacterBytesBuilder<'a> { FullCharacterBytesBuilder { inventory: Some(inventory), ..self diff --git a/src/ship/drops/tool_table.rs b/src/ship/drops/tool_table.rs index 161610c..073cb77 100644 --- a/src/ship/drops/tool_table.rs +++ b/src/ship/drops/tool_table.rs @@ -4,8 +4,8 @@ use serde::{Serialize, Deserialize}; use rand::{Rng, SeedableRng}; use rand::distributions::{WeightedIndex, Distribution}; -use crate::entity::item::{ItemDetail, Tool as ToolDetail}; -use crate::entity::item::tool::{StackedTool, ToolType}; +use crate::entity::item::ItemDetail; +use crate::entity::item::tool::{Tool, ToolType}; use crate::ship::room::{Difficulty, Episode}; use crate::ship::map::MapVariantType; use crate::entity::character::SectionID; @@ -132,7 +132,7 @@ impl ToolTable { pub fn get_drop(&self, map_area: &MapVariantType, rng: &mut R) -> Option { let tool_type = self.tool_type(map_area, rng); - Some(ItemDetail::Tool(ToolDetail { + Some(ItemDetail::Tool(Tool { tool: tool_type })) } diff --git a/src/ship/items.rs b/src/ship/items.rs index 7f206fc..4a5a66a 100644 --- a/src/ship/items.rs +++ b/src/ship/items.rs @@ -1,244 +1,186 @@ use std::collections::HashMap; -use std::hash::{Hash, Hasher}; use libpso::character::character::InventoryItem; -use crate::entity::item::{Item, ItemDetail, ItemLocation}; +use crate::entity::gateway::EntityGateway; +use crate::entity::character::Character; +use crate::entity::item::{Item, 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::StackedTool; +use crate::entity::item::tool::Tool; -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 == b.shield, - (ItemDetail::Unit(a), ItemDetail::Unit(b)) => a.unit == b.unit, - (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)) - } - } +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub struct ActiveItemId(u32); - 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!() +#[derive(Debug)] +pub struct ActiveItem { + id: ActiveItemId, + item: StackedItem, +} + +impl ActiveItem { + pub fn as_client_bytes(&self) -> [u8; 16] { + match &self.item { + StackedItem::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(), + } + }, + StackedItem::Stacked(i) => { + let len = i.len(); + match &i[0].item { + ItemDetail::Tool(t) => t.as_stacked_bytes(len), + _ => panic!(), } } } } - } +pub struct ActiveInventory(Vec); -pub struct ItemActivator { - id: u32, - -} - -impl ItemActivator { - pub fn new() -> ItemActivator { - ItemActivator { - id: 0 - } +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].item_id = item.id.0; + + // does this do anything? + inventory[index].equipped = match item.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, + StackedItem::Individual(Item {item: ItemDetail::Unit(Unit{equipped: true, ..}), ..}) => 1, + //StackedItem::Individual(Item {item: ItemDetail::Mag(Mag{equipped: true, ..}), ..}) => 1, + _ => 0, + }; + // because this actually equips the item + inventory[index].flags |= match item.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, + StackedItem::Individual(Item {item: ItemDetail::Unit(Unit {equipped: true, ..}), ..}) => 8, + //StackedItem::Individual(Item {item: ItemDetail::Mag(Mag{equipped: true, ..}), ..}) => 8, + _ => 0, + }; + inventory + }) } - pub fn activate_item(&mut self, item: StackedItem) -> ActiveItem { - self.id += 1; - ActiveItem { - id: ActiveItemId(self.id), - item: item, - } + pub fn count(&self) -> usize { + self.0.len() } } - -#[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.value().hash(hasher), - ItemDetail::Unit(u) => u.unit.value().hash(hasher), - ItemDetail::Tool(t) => t.tool.value().hash(hasher), - ItemDetail::TechniqueDisk(t) => t.tech.hash(hasher), +fn inventory_item_index(item: &StackedItem) -> usize { + match item { + StackedItem::Individual(i) => { + match i.location { + ItemLocation::Inventory{index: index, ..} => index, + _ => panic!() + } + }, + StackedItem::Stacked(i) => { + match i[0].location { + ItemLocation::Inventory{index: index, ..} => index, + _ => panic!() + } } } } -impl std::cmp::PartialEq for StackedItemKey { - fn eq(&self, other: &StackedItemKey) -> bool { - are_items_same_type(&self.0, &other.0) - } -} - -impl Eq for StackedItemKey {} +fn stack_items(items: Vec) -> Vec { + let mut stacks = HashMap::new(); -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] - }); + for item in items { + stacks.entry(item.item.item_type()).or_insert(Vec::new()).push(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()) + stacks.into_iter() + .map(|(itype, items)| { + match items[0].item.is_stackable() { + true => { + vec![StackedItem::Stacked(items)] + }, + false => { + items.into_iter().map(|i| { + StackedItem::Individual(i) + }).collect() + } } - - stacked }) + .flatten() + .collect() } +struct ActiveBank([Option; 200]); -pub enum InventoryAddError { - InventoryFull, - FullToolStack, -} - -pub enum InventoryRemoveError { - NoItemInInventory, +pub struct ActiveItemDatabase { + id: u32, } -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 - - }) +impl ActiveItemDatabase { + pub fn new() -> ActiveItemDatabase { + ActiveItemDatabase { + id: 0, + } } - pub fn count(&self) -> usize { - self.0.iter() - .filter(|k| k.is_some()) - .count() + fn activate_item(&mut self, item: StackedItem) -> ActiveItem { + self.id += 1; + ActiveItem { + id: ActiveItemId(self.id), + item: item, + } } - 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; + // deactivate item - // 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, - StackedItem::Individual(Item {item: ItemDetail::Unit(Unit{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, - StackedItem::Individual(Item {item: ItemDetail::Unit(Unit {equipped: true, ..}), ..}) => 8, - _ => 0, - }; + pub fn get_character_inventory(&mut self, entity_gateway: &mut EG, character: &Character) -> 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, } - inventory - }) + }).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 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"), - } - }) -} - - - +#[cfg(test)] mod test { use super::*; use crate::entity::item; - use crate::entity::item::{Item, ItemDetail, ItemEntityId, ItemLocation, Tool}; - + use crate::entity::item::{Item, ItemDetail, ItemEntityId, ItemLocation}; #[test] - fn test_stacked_items() { + fn test_stack_items() { let item1 = Item { id: ItemEntityId(1), location: ItemLocation::Inventory { @@ -344,7 +286,6 @@ mod test { 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); diff --git a/src/ship/ship.rs b/src/ship/ship.rs index cdc4ef7..6b44b19 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -122,14 +122,14 @@ struct ClientState { settings: UserSettings, character: Character, session: Session, - inventory: items::Inventory, guildcard: GuildCard, + inventory: items::ActiveInventory, //bank: Bank, block: u32, } impl ClientState { - fn new(user: UserAccount, settings: UserSettings, character: Character, inventory: items::Inventory, /*bank: Bank,*/ session: Session, guildcard: GuildCard) -> ClientState { + fn new(user: UserAccount, settings: UserSettings, character: Character, inventory: items::ActiveInventory, /*bank: Bank,*/ session: Session, guildcard: GuildCard) -> ClientState { ClientState { user: user, settings: settings, @@ -143,6 +143,7 @@ impl ClientState { } } + pub struct ShipServerState { entity_gateway: EG, clients: HashMap, @@ -150,7 +151,7 @@ pub struct ShipServerState { level_table: CharacterLevelTable, name: String, rooms: [Option; MAX_ROOMS], - item_activator: items::ItemActivator, + item_database: items::ActiveItemDatabase, } impl ShipServerState { @@ -162,7 +163,7 @@ impl ShipServerState { level_table: CharacterLevelTable::new(), name: "Sona-Nyl".into(), rooms: [None; MAX_ROOMS], - item_activator: items::ItemActivator::new(), + item_database: items::ActiveItemDatabase::new(), } } @@ -180,14 +181,9 @@ impl ShipServerState { .clone(); let settings = self.entity_gateway.get_user_settings_by_user(&user) .ok_or(ShipError::ClientNotFound(id))?; - - let items = self.entity_gateway.get_items_by_character(&character); - let (inventory, bank) = items::split_items_into_inventory_and_bank(items); - let stacked_items = items::stack_items(inventory); - let activated_items = stacked_items.into_iter().map(|item| self.item_activator.activate_item(item)).collect(); - let inventory = items::Inventory::new(activated_items); let guildcard = self.entity_gateway.get_guild_card_by_character(&character) .ok_or(ShipError::ClientNotFound(id))?; + let inventory = self.item_database.get_character_inventory(&mut self.entity_gateway, &character); self.clients.insert(id, ClientState::new(user, settings, character, inventory, pkt.session, guildcard)); vec![SendShipPacket::LoginResponse(response), SendShipPacket::ShipBlockList(ShipBlockList::new(&self.name, 3))]