remove old item manager code
This commit is contained in:
		
							parent
							
								
									8f44ca9d18
								
							
						
					
					
						commit
						25e793cda9
					
				
							
								
								
									
										12
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										12
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @ -172,6 +172,17 @@ dependencies = [ | ||||
|  "winapi", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "async-recursion" | ||||
| version = "1.0.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "2cda8f4bcc10624c4e85bc66b3f452cca98cfa5ca002dc83a16aad2367641bea" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "async-std" | ||||
| version = "1.11.0" | ||||
| @ -587,6 +598,7 @@ version = "0.1.0" | ||||
| dependencies = [ | ||||
|  "ages-prs", | ||||
|  "anyhow", | ||||
|  "async-recursion", | ||||
|  "async-std", | ||||
|  "async-trait", | ||||
|  "barrel", | ||||
|  | ||||
| @ -11,7 +11,6 @@ use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateAction, Item | ||||
| use crate::ship::items::apply_item::apply_item; | ||||
| use crate::entity::item::{ItemDetail, NewItemEntity, TradeId}; | ||||
| use crate::entity::item::tool::Tool; | ||||
| use crate::entity::item::weapon::WeaponModifier; | ||||
| use crate::entity::item::ItemModifier; | ||||
| use crate::ship::shops::ShopItem; | ||||
| use crate::ship::trade::TradeItem; | ||||
|  | ||||
| @ -1,12 +1,11 @@ | ||||
| use thiserror::Error; | ||||
| use std::convert::TryFrom; | ||||
| use std::convert::TryInto; | ||||
| use crate::entity::gateway::{EntityGateway, GatewayError}; | ||||
| use crate::entity::character::CharacterEntity; | ||||
| use crate::entity::item::mag::{MagCell, MagCellError}; | ||||
| use crate::entity::item::tool::ToolType; | ||||
| use crate::entity::item::{ItemDetail, ItemEntityId}; | ||||
| use crate::ship::items::state::{ItemStateProxy, InventoryState, InventoryItem, InventoryItemDetail, ItemStateError}; | ||||
| use crate::ship::items::state::{ItemStateProxy, InventoryItem, InventoryItemDetail, ItemStateError}; | ||||
| 
 | ||||
| 
 | ||||
| #[derive(Error, Debug)] | ||||
|  | ||||
| @ -1,324 +0,0 @@ | ||||
| use crate::ship::items::ClientItemId; | ||||
| use libpso::character::character;//::InventoryItem;
 | ||||
| use crate::entity::item::{ItemEntityId, ItemEntity, ItemDetail, BankEntity, BankItemEntity, BankName}; | ||||
| use crate::entity::character::CharacterEntityId; | ||||
| use crate::entity::item::tool::Tool; | ||||
| use crate::ship::items::inventory::{InventoryItemHandle, InventoryItem}; | ||||
| 
 | ||||
| const BANK_CAPACITY: usize = 200; | ||||
| 
 | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct IndividualBankItem { | ||||
|     pub entity_id: ItemEntityId, | ||||
|     pub item_id: ClientItemId, | ||||
|     pub item: ItemDetail, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct StackedBankItem { | ||||
|     pub entity_ids: Vec<ItemEntityId>, | ||||
|     pub item_id: ClientItemId, | ||||
|     pub tool: Tool, | ||||
| } | ||||
| 
 | ||||
| impl StackedBankItem { | ||||
|     pub fn count(&self) -> usize { | ||||
|         self.entity_ids.len() | ||||
|     } | ||||
| 
 | ||||
|     pub fn take_entity_ids(&mut self, amount: usize) -> Option<Vec<ItemEntityId>> { | ||||
|         if amount <= self.count() { | ||||
|             Some(self.entity_ids.drain(..amount).collect()) | ||||
|         } | ||||
|         else { | ||||
|             None | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone)] | ||||
| pub enum BankItem { | ||||
|     Individual(IndividualBankItem), | ||||
|     Stacked(StackedBankItem), | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| impl std::cmp::PartialEq for BankItem { | ||||
|     fn eq(&self, other: &BankItem) -> bool { | ||||
|         let mut self_bytes = [0u8; 4]; | ||||
|         let mut other_bytes = [0u8; 4]; | ||||
|         self_bytes.copy_from_slice(&self.as_client_bytes()[0..4]); | ||||
|         other_bytes.copy_from_slice(&other.as_client_bytes()[0..4]); | ||||
| 
 | ||||
|         let self_value = u32::from_be_bytes(self_bytes); | ||||
|         let other_value = u32::from_be_bytes(other_bytes); | ||||
| 
 | ||||
|         self_value.eq(&other_value) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl std::cmp::Eq for BankItem {} | ||||
| 
 | ||||
| impl std::cmp::PartialOrd for BankItem { | ||||
|     fn partial_cmp(&self, other: &BankItem) -> Option<std::cmp::Ordering> { | ||||
|         //let self_bytes = self.as_client_bytes();
 | ||||
|         //let other_bytes = other.as_client_bytes();
 | ||||
|         let mut self_bytes = [0u8; 4]; | ||||
|         let mut other_bytes = [0u8; 4]; | ||||
|         self_bytes.copy_from_slice(&self.as_client_bytes()[0..4]); | ||||
|         other_bytes.copy_from_slice(&other.as_client_bytes()[0..4]); | ||||
| 
 | ||||
| 
 | ||||
|         let self_value = u32::from_be_bytes(self_bytes); | ||||
|         let other_value = u32::from_be_bytes(other_bytes); | ||||
| 
 | ||||
|         self_value.partial_cmp(&other_value) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl std::cmp::Ord for BankItem { | ||||
|     fn cmp(&self, other: &BankItem) -> std::cmp::Ordering { | ||||
|         //let self_bytes = self.as_client_bytes();
 | ||||
|         //let other_bytes = other.as_client_bytes();
 | ||||
|         let mut self_bytes = [0u8; 4]; | ||||
|         let mut other_bytes = [0u8; 4]; | ||||
|         self_bytes.copy_from_slice(&self.as_client_bytes()[0..4]); | ||||
|         other_bytes.copy_from_slice(&other.as_client_bytes()[0..4]); | ||||
| 
 | ||||
| 
 | ||||
|         let self_value = u32::from_le_bytes(self_bytes); | ||||
|         let other_value = u32::from_le_bytes(other_bytes); | ||||
| 
 | ||||
|         self_value.cmp(&other_value) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl BankItem { | ||||
|     pub fn set_item_id(&mut self, item_id: ClientItemId) { | ||||
|         match self { | ||||
|             BankItem::Individual(individual_bank_item) => { | ||||
|                 individual_bank_item.item_id = item_id | ||||
|             }, | ||||
|             BankItem::Stacked(stacked_bank_item) => { | ||||
|                 stacked_bank_item.item_id = item_id | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn item_id(&self) -> ClientItemId { | ||||
|         match self { | ||||
|             BankItem::Individual(individual_bank_item) => { | ||||
|                 individual_bank_item.item_id | ||||
|             }, | ||||
|             BankItem::Stacked(stacked_bank_item) => { | ||||
|                 stacked_bank_item.item_id | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn as_client_bytes(&self) -> [u8; 16] { | ||||
|         match self { | ||||
|             BankItem::Individual(item) => { | ||||
|                 match &item.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(), | ||||
|                     ItemDetail::ESWeapon(e) => e.as_bytes(), | ||||
|                 } | ||||
|             }, | ||||
|             BankItem::Stacked(item) => { | ||||
|                 item.tool.as_stacked_bytes(item.entity_ids.len()) | ||||
|             }, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| pub struct BankItemHandle<'a> { | ||||
|     bank: &'a mut CharacterBank, | ||||
|     index: usize | ||||
| } | ||||
| 
 | ||||
| impl<'a> BankItemHandle<'a> { | ||||
|     pub fn item(&'a self) -> Option<&'a BankItem> { | ||||
|         self.bank.items.get(self.index) | ||||
|     } | ||||
| 
 | ||||
|     pub fn item_mut(&mut self) -> Option<&mut BankItem> { | ||||
|         self.bank.items.get_mut(self.index) | ||||
|     } | ||||
| 
 | ||||
|     pub fn remove_from_bank(self) { | ||||
|         self.bank.items.remove(self.index); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct CharacterBank { | ||||
|     item_id_counter: u32, | ||||
|     items: Vec<BankItem> | ||||
| } | ||||
| 
 | ||||
| impl CharacterBank { | ||||
|     pub fn new(mut items: Vec<BankItem>) -> CharacterBank { | ||||
|         items.sort(); | ||||
|         CharacterBank { | ||||
|             item_id_counter: 0, | ||||
|             items, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn initialize_item_ids(&mut self, base_item_id: u32) { | ||||
|         for (i, item) in self.items.iter_mut().enumerate() { | ||||
|             item.set_item_id(ClientItemId(base_item_id + i as u32)); | ||||
|         } | ||||
|         self.item_id_counter = base_item_id + self.items.len() as u32 + 1; | ||||
|     } | ||||
| 
 | ||||
|     pub fn get_item_handle_by_id(&mut self, item_id: ClientItemId) -> Option<BankItemHandle> { | ||||
|         let (index, _) = self.items.iter() | ||||
|             .enumerate() | ||||
|             .find(|(_, item)| { | ||||
|                 item.item_id() == item_id | ||||
|             })?; | ||||
|         Some(BankItemHandle { | ||||
|             bank: self, | ||||
|             index, | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     pub fn as_client_bank_items(&self) -> character::Bank { | ||||
|         self.items.iter() | ||||
|             .enumerate() | ||||
|             .fold(character::Bank::default(), |mut bank, (slot, item)| { | ||||
|                 bank.item_count = (slot + 1) as u32; | ||||
|                 let bytes = item.as_client_bytes(); | ||||
|                 bank.items[slot].data1.copy_from_slice(&bytes[0..12]); | ||||
|                 bank.items[slot].data2.copy_from_slice(&bytes[12..16]); | ||||
|                 bank.items[slot].item_id = item.item_id().0; | ||||
| 
 | ||||
|                 bank | ||||
|             }) | ||||
|     } | ||||
| 
 | ||||
|     pub fn as_client_bank_request(&self) -> Vec<character::BankItem> { | ||||
|         self.items.iter() | ||||
|             .map(|item| { | ||||
|                 let bytes = item.as_client_bytes(); | ||||
|                 let mut data1 = [0; 12]; | ||||
|                 let mut data2 = [0; 4]; | ||||
|                 data1.copy_from_slice(&bytes[0..12]); | ||||
|                 data2.copy_from_slice(&bytes[12..16]); | ||||
|                 let amount = match item { | ||||
|                     BankItem::Individual(_individual_bank_item) => { | ||||
|                         1 | ||||
|                     }, | ||||
|                     BankItem::Stacked(stacked_bank_item) => { | ||||
|                         stacked_bank_item.count() | ||||
|                     }, | ||||
|                 }; | ||||
|                 character::BankItem { | ||||
|                     data1, | ||||
|                     data2, | ||||
|                     item_id: item.item_id().0, | ||||
|                     amount: amount as u16, | ||||
|                     flags: 1, | ||||
|                 } | ||||
|             }) | ||||
|             .collect() | ||||
|     } | ||||
| 
 | ||||
|     pub fn count(&self) -> usize { | ||||
|         self.items.len() | ||||
|     } | ||||
| 
 | ||||
|     pub fn deposit_item(&mut self, mut inventory_item: InventoryItemHandle, amount: usize) -> Option<&BankItem> { | ||||
|         let remove = match inventory_item.item_mut()? { | ||||
|             InventoryItem::Individual(individual_inventory_item) => { | ||||
|                 if self.items.len() >= BANK_CAPACITY { | ||||
|                     return None | ||||
|                 } | ||||
|                 self.items.push(BankItem::Individual(IndividualBankItem { | ||||
|                     entity_id: individual_inventory_item.entity_id, | ||||
|                     item_id: individual_inventory_item.item_id, | ||||
|                     item: individual_inventory_item.item.clone(), | ||||
|                 })); | ||||
|                 true | ||||
|             }, | ||||
|             InventoryItem::Stacked(stacked_inventory_item) => { | ||||
|                 let existing_bank_item = self.items.iter_mut() | ||||
|                     .find_map(|item| { | ||||
|                         if let BankItem::Stacked(stacked_bank_item) = item { | ||||
|                             if stacked_bank_item.tool == stacked_inventory_item.tool { | ||||
|                                 return Some(stacked_bank_item) | ||||
|                             } | ||||
|                         } | ||||
|                         None | ||||
|                     }); | ||||
| 
 | ||||
|                 match existing_bank_item { | ||||
|                     Some(stacked_bank_item) => { | ||||
|                         if stacked_bank_item.count() + stacked_inventory_item.count() > stacked_inventory_item.tool.max_stack() { | ||||
|                             return None | ||||
|                         } | ||||
| 
 | ||||
|                         let mut deposited_entity_ids = stacked_inventory_item.take_entity_ids(amount)?; | ||||
|                         stacked_bank_item.entity_ids.append(&mut deposited_entity_ids); | ||||
|                     } | ||||
|                     None => { | ||||
|                         if self.items.len() >= BANK_CAPACITY { | ||||
|                             return None | ||||
|                         } | ||||
|                         let deposited_entity_ids = stacked_inventory_item.take_entity_ids(amount)?; | ||||
| 
 | ||||
|                         self.item_id_counter += 1; | ||||
|                         self.items.push(BankItem::Stacked(StackedBankItem { | ||||
|                             entity_ids: deposited_entity_ids, | ||||
|                             item_id: ClientItemId(self.item_id_counter), | ||||
|                             tool: stacked_inventory_item.tool, | ||||
|                         })) | ||||
|                     } | ||||
|                 } | ||||
|                 stacked_inventory_item.count() == 0 | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         if remove { | ||||
|             inventory_item.remove_from_inventory(); | ||||
|         } | ||||
| 
 | ||||
|         self.items.last() | ||||
|     } | ||||
| 
 | ||||
|     pub fn as_bank_entity(&self, _character_id: &CharacterEntityId, _bank_name: &BankName) -> BankEntity { | ||||
|         BankEntity { | ||||
|             items: self.items.iter() | ||||
|                 .map(|item| { | ||||
|                     match item { | ||||
|                         BankItem::Individual(item) => { | ||||
|                             BankItemEntity::Individual(ItemEntity { | ||||
|                                 id: item.entity_id, | ||||
|                                 item: item.item.clone(), | ||||
|                             }) | ||||
|                         }, | ||||
|                         BankItem::Stacked(items) => { | ||||
|                             BankItemEntity::Stacked(items.entity_ids.iter() | ||||
|                                                     .map(|id| { | ||||
|                                                         ItemEntity { | ||||
|                                                             id: *id, | ||||
|                                                             item: ItemDetail::Tool(items.tool) | ||||
|                                                         } | ||||
|                                                     }) | ||||
|                                                     .collect()) | ||||
|                         }, | ||||
|                     } | ||||
|                 }) | ||||
|                 .collect() | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| @ -1,253 +0,0 @@ | ||||
| use crate::ship::items::ClientItemId; | ||||
| use crate::entity::item::{ItemEntityId, ItemDetail}; | ||||
| use crate::entity::item::Meseta; | ||||
| use crate::entity::item::tool::Tool; | ||||
| use crate::ship::map::MapArea; | ||||
| use crate::ship::items::inventory::{IndividualInventoryItem, StackedInventoryItem, InventoryItemHandle}; | ||||
| 
 | ||||
| 
 | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct IndividualFloorItem { | ||||
|     pub entity_id: ItemEntityId, | ||||
|     pub item_id: ClientItemId, | ||||
|     pub item: ItemDetail, | ||||
|     pub map_area: MapArea, | ||||
|     pub x: f32, | ||||
|     pub y: f32, | ||||
|     pub z: f32, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct StackedFloorItem { | ||||
|     pub entity_ids: Vec<ItemEntityId>, | ||||
|     pub item_id: ClientItemId, | ||||
|     pub tool: Tool, | ||||
|     pub map_area: MapArea, | ||||
|     pub x: f32, | ||||
|     pub y: f32, | ||||
|     pub z: f32, | ||||
| } | ||||
| 
 | ||||
| impl StackedFloorItem { | ||||
|     pub fn count(&self) -> usize { | ||||
|         self.entity_ids.len() | ||||
|     } | ||||
| 
 | ||||
|     pub fn as_client_bytes(&self) -> [u8; 16] { | ||||
|         self.tool.as_stacked_bytes(self.count()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct MesetaFloorItem { | ||||
|     pub item_id: ClientItemId, | ||||
|     pub meseta: Meseta, | ||||
|     pub map_area: MapArea, | ||||
|     pub x: f32, | ||||
|     pub y: f32, | ||||
|     pub z: f32, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone)] | ||||
| pub enum FloorItem { | ||||
|     Individual(IndividualFloorItem), | ||||
|     Stacked(StackedFloorItem), | ||||
|     Meseta(MesetaFloorItem), | ||||
| } | ||||
| 
 | ||||
| impl FloorItem { | ||||
|     pub fn item_id(&self) -> ClientItemId { | ||||
|         match self { | ||||
|             FloorItem::Individual(individual_floor_item) => { | ||||
|                 individual_floor_item.item_id | ||||
|             }, | ||||
|             FloorItem::Stacked(stacked_floor_item) => { | ||||
|                 stacked_floor_item.item_id | ||||
|             }, | ||||
|             FloorItem::Meseta(meseta_floor_item) => { | ||||
|                 meseta_floor_item.item_id | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn x(&self) -> f32 { | ||||
|         match self { | ||||
|             FloorItem::Individual(individual_floor_item) => { | ||||
|                 individual_floor_item.x | ||||
|             }, | ||||
|             FloorItem::Stacked(stacked_floor_item) => { | ||||
|                 stacked_floor_item.x | ||||
|             }, | ||||
|             FloorItem::Meseta(meseta_floor_item) => { | ||||
|                 meseta_floor_item.x | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn y(&self) -> f32 { | ||||
|         match self { | ||||
|             FloorItem::Individual(individual_floor_item) => { | ||||
|                 individual_floor_item.y | ||||
|             }, | ||||
|             FloorItem::Stacked(stacked_floor_item) => { | ||||
|                 stacked_floor_item.y | ||||
|             }, | ||||
|             FloorItem::Meseta(meseta_floor_item) => { | ||||
|                 meseta_floor_item.y | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn z(&self) -> f32 { | ||||
|         match self { | ||||
|             FloorItem::Individual(individual_floor_item) => { | ||||
|                 individual_floor_item.z | ||||
|             }, | ||||
|             FloorItem::Stacked(stacked_floor_item) => { | ||||
|                 stacked_floor_item.z | ||||
|             }, | ||||
|             FloorItem::Meseta(meseta_floor_item) => { | ||||
|                 meseta_floor_item.z | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn map_area(&self) -> MapArea { | ||||
|         match self { | ||||
|             FloorItem::Individual(individual_floor_item) => { | ||||
|                 individual_floor_item.map_area | ||||
|             }, | ||||
|             FloorItem::Stacked(stacked_floor_item) => { | ||||
|                 stacked_floor_item.map_area | ||||
|             }, | ||||
|             FloorItem::Meseta(meseta_floor_item) => { | ||||
|                 meseta_floor_item.map_area | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn as_client_bytes(&self) -> [u8; 16] { | ||||
|         match self { | ||||
|             FloorItem::Individual(individual_floor_item) => { | ||||
|                 individual_floor_item.item.as_client_bytes() | ||||
|             }, | ||||
|             FloorItem::Stacked(stacked_floor_item) => { | ||||
|                 stacked_floor_item.as_client_bytes() | ||||
|             }, | ||||
|             FloorItem::Meseta(meseta_floor_item) => { | ||||
|                 meseta_floor_item.meseta.as_bytes() | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| pub struct FloorItemHandle<'a> { | ||||
|     floor: &'a mut RoomFloorItems, | ||||
|     index: usize, | ||||
| } | ||||
| 
 | ||||
| impl<'a> FloorItemHandle<'a> { | ||||
|     pub fn item(&'a self) -> Option<&'a FloorItem> { | ||||
|         self.floor.0.get(self.index) | ||||
|     } | ||||
| 
 | ||||
|     pub fn remove_from_floor(self) { | ||||
|         self.floor.0.remove(self.index); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // TODO: floors should keep track of their own item_ids
 | ||||
| #[derive(Debug, Default)] | ||||
| pub struct RoomFloorItems(Vec<FloorItem>); | ||||
| 
 | ||||
| impl RoomFloorItems { | ||||
|     pub fn add_item(&mut self, item: FloorItem) { | ||||
|         self.0.push(item); | ||||
|     } | ||||
| 
 | ||||
|     pub fn remove_item(&mut self, item_id: &ClientItemId) { | ||||
|         self.0.retain(|item| item.item_id() != *item_id); | ||||
|     } | ||||
| 
 | ||||
|     // TODO: &ClientItemId
 | ||||
|     pub fn get_item_by_id(&self, item_id: ClientItemId) -> Option<&FloorItem> { | ||||
|         self.0.iter().find(|item| item.item_id() == item_id) | ||||
|     } | ||||
| 
 | ||||
|     // TODO: &ClientItemId
 | ||||
|     pub fn get_item_handle_by_id(&mut self, item_id: ClientItemId) -> Option<FloorItemHandle> { | ||||
|         let index = self.0.iter().position(|item| item.item_id() == item_id)?; | ||||
|         Some(FloorItemHandle { | ||||
|             floor: self, | ||||
|             index, | ||||
|         }) | ||||
|     } | ||||
|     
 | ||||
|     pub fn take_item_by_id(&mut self, item_id: ClientItemId) -> Option<FloorItem> { | ||||
|         self.0 | ||||
|             .drain_filter(|i| i.item_id() == item_id) | ||||
|             .next() | ||||
|     } | ||||
| 
 | ||||
|     pub fn drop_individual_inventory_item(&mut self, individual_inventory_item: IndividualInventoryItem, item_drop_location: (MapArea, f32, f32, f32)) -> &IndividualFloorItem { | ||||
|         self.0.push(FloorItem::Individual(IndividualFloorItem { | ||||
|             entity_id: individual_inventory_item.entity_id, | ||||
|             item_id: individual_inventory_item.item_id, | ||||
|             item: individual_inventory_item.item, | ||||
|             map_area: item_drop_location.0, | ||||
|             x: item_drop_location.1, | ||||
|             y: item_drop_location.2, | ||||
|             z: item_drop_location.3, | ||||
|         })); | ||||
| 
 | ||||
|         match self.0.last().unwrap() { | ||||
|             FloorItem::Individual(item) => item, | ||||
|             _ => unreachable!(), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn drop_stacked_inventory_item(&mut self, stacked_inventory_item: StackedInventoryItem, item_drop_location: (MapArea, f32, f32, f32)) -> &StackedFloorItem { | ||||
|         self.0.push(FloorItem::Stacked(StackedFloorItem { | ||||
|             entity_ids: stacked_inventory_item.entity_ids, | ||||
|             item_id: stacked_inventory_item.item_id, | ||||
|             tool: stacked_inventory_item.tool, | ||||
|             map_area: item_drop_location.0, | ||||
|             x: item_drop_location.1, | ||||
|             y: item_drop_location.2, | ||||
|             z: item_drop_location.3, | ||||
|         })); | ||||
|             
 | ||||
|         match self.0.last().unwrap() { | ||||
|             FloorItem::Stacked(item) => item, | ||||
|             _ => unreachable!(), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // TODO: Result
 | ||||
|     // TODO: if consumed_item is not a tool items do not get placed back into inventory (should I care?)
 | ||||
|     pub fn drop_partial_stacked_inventory_item(&mut self, inventory_item: InventoryItemHandle, amount: usize, new_item_id: ClientItemId, item_drop_location: (MapArea, f32, f32, f32)) -> Option<&StackedFloorItem> { | ||||
|         let consumed_item = inventory_item.consume(amount).ok()?; | ||||
| 
 | ||||
|         if let ItemDetail::Tool(tool) = consumed_item.item() { | ||||
|             self.0.push(FloorItem::Stacked(StackedFloorItem { | ||||
|                 entity_ids: consumed_item.entity_ids(), | ||||
|                 item_id: new_item_id, | ||||
|                 tool, | ||||
|                 map_area: item_drop_location.0, | ||||
|                 x: item_drop_location.1, | ||||
|                 y: item_drop_location.2, | ||||
|                 z: item_drop_location.3, | ||||
|             })) | ||||
|         } | ||||
|         else { | ||||
|             return None | ||||
|         } | ||||
| 
 | ||||
|         match self.0.last().unwrap() { | ||||
|             FloorItem::Stacked(item) => Some(item), | ||||
|             _ => unreachable!(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,988 +0,0 @@ | ||||
| use std::cmp::Ordering; | ||||
| use thiserror::Error; | ||||
| use libpso::character::character; | ||||
| use crate::entity::character::CharacterEntityId; | ||||
| use crate::entity::item::{ItemEntityId, ItemDetail, ItemEntity, ItemType, InventoryEntity, InventoryItemEntity, EquippedEntity}; | ||||
| use crate::entity::item::tool::{Tool, ToolType}; | ||||
| use crate::entity::item::mag::Mag; | ||||
| use crate::entity::item::weapon::Weapon; | ||||
| use crate::ship::items::{ClientItemId, BankItem, BankItemHandle, ItemManagerError}; | ||||
| use crate::ship::items::floor::{IndividualFloorItem, StackedFloorItem}; | ||||
| use crate::ship::shops::{ShopItem, ArmorShopItem, ToolShopItem, WeaponShopItem}; | ||||
| 
 | ||||
| const INVENTORY_CAPACITY: usize = 30; | ||||
| 
 | ||||
| 
 | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct InventorySlot(pub usize); | ||||
| 
 | ||||
| 
 | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct IndividualInventoryItem { | ||||
|     pub entity_id: ItemEntityId, | ||||
|     pub item_id: ClientItemId, | ||||
|     pub item: ItemDetail, | ||||
| } | ||||
| 
 | ||||
| impl IndividualInventoryItem { | ||||
|     pub fn mag(&self) -> Option<&Mag> { | ||||
|         match self.item { | ||||
|             ItemDetail::Mag(ref mag) => Some(mag), | ||||
|             _ => None | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn weapon(&self) -> Option<&Weapon> { | ||||
|         match self.item { | ||||
|             ItemDetail::Weapon(ref weapon) => Some(weapon), | ||||
|             _ => None | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn mag_mut(&mut self) -> Option<&mut Mag> { | ||||
|         match self.item { | ||||
|             ItemDetail::Mag(ref mut mag) => Some(mag), | ||||
|             _ => None | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct StackedInventoryItem { | ||||
|     pub entity_ids: Vec<ItemEntityId>, | ||||
|     pub item_id: ClientItemId, | ||||
|     pub tool: Tool, | ||||
| } | ||||
| 
 | ||||
| impl StackedInventoryItem { | ||||
|     pub fn count(&self) -> usize { | ||||
|         self.entity_ids.len() | ||||
|     } | ||||
| 
 | ||||
|     pub fn take_entity_ids(&mut self, amount: usize) -> Option<Vec<ItemEntityId>> { | ||||
|         if amount <= self.count() { | ||||
|             Some(self.entity_ids.drain(..amount).collect()) | ||||
|         } | ||||
|         else { | ||||
|             None | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone)] | ||||
| pub enum InventoryItem { | ||||
|     Individual(IndividualInventoryItem), | ||||
|     Stacked(StackedInventoryItem), | ||||
| } | ||||
| 
 | ||||
| #[derive(Error, Debug, Clone)] | ||||
| #[error("")] | ||||
| pub enum InventoryItemAddToError { | ||||
|     BothAreNotStacked, | ||||
|     DifferentTool, | ||||
|     ExceedsCapacity, | ||||
| } | ||||
| 
 | ||||
| #[derive(Error, Debug, Clone)] | ||||
| #[error("")] | ||||
| pub enum InventoryAddError { | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone)] | ||||
| pub enum YesThereIsSpace { | ||||
|     NewStack, | ||||
|     ExistingStack, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone)] | ||||
| pub enum NoThereIsNotSpace { | ||||
|     FullStack, | ||||
|     FullInventory, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone)] | ||||
| pub enum SpaceForStack { | ||||
|     Yes(YesThereIsSpace), | ||||
|     No(NoThereIsNotSpace), | ||||
| } | ||||
| 
 | ||||
| impl InventoryItem { | ||||
|     pub fn entity_ids(&self) -> Vec<ItemEntityId> { | ||||
|         match self { | ||||
|             InventoryItem::Individual(individual_inventory_item) => { | ||||
|                 vec![individual_inventory_item.entity_id] | ||||
|             }, | ||||
|             InventoryItem::Stacked(stacked_inventory_item) => { | ||||
|                 stacked_inventory_item.entity_ids.clone() | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn item_id(&self) -> ClientItemId { | ||||
|         match self { | ||||
|             InventoryItem::Individual(individual_inventory_item) => { | ||||
|                 individual_inventory_item.item_id | ||||
|             }, | ||||
|             InventoryItem::Stacked(stacked_inventory_item) => { | ||||
|                 stacked_inventory_item.item_id | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn set_item_id(&mut self, item_id: ClientItemId) { | ||||
|         match self { | ||||
|             InventoryItem::Individual(individual_inventory_item) => { | ||||
|                 individual_inventory_item.item_id = item_id | ||||
|             }, | ||||
|             InventoryItem::Stacked(stacked_inventory_item) => { | ||||
|                 stacked_inventory_item.item_id = item_id | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn item_type(&self) -> ItemType { | ||||
|         match self { | ||||
|             InventoryItem::Individual(individual_inventory_item) => { | ||||
|                 individual_inventory_item.item.item_type() | ||||
|             }, | ||||
|             InventoryItem::Stacked(stacked_inventory_item) => { | ||||
|                 ItemType::Tool(stacked_inventory_item.tool.tool) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // TOOD: delete?
 | ||||
|     pub fn are_same_stackable_tool(&self, other_stacked_item: &StackedFloorItem) -> bool { | ||||
|         match self { | ||||
|             InventoryItem::Stacked(self_stacked_item) => { | ||||
|                 self_stacked_item.tool == other_stacked_item.tool | ||||
|                     && self_stacked_item.tool.is_stackable() && other_stacked_item.tool.is_stackable() | ||||
|             }, | ||||
|             _ => false | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // TOOD: delete?
 | ||||
|     pub fn can_combine_stacks(&self, other_stacked_item: &StackedFloorItem) -> bool { | ||||
|         match self { | ||||
|             InventoryItem::Stacked(self_stacked_item) => { | ||||
|                 self_stacked_item.tool == other_stacked_item.tool | ||||
|                     && self_stacked_item.tool.is_stackable() && other_stacked_item.tool.is_stackable() | ||||
|                     && self_stacked_item.count() + other_stacked_item.count() <= self_stacked_item.tool.max_stack() | ||||
|             }, | ||||
|             _ => false | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // TODO: result
 | ||||
|     // TOOD: delete?
 | ||||
|     pub fn combine_stacks(&mut self, other_stacked_item: &mut StackedFloorItem) { | ||||
|         if let InventoryItem::Stacked(self_stacked_item) = self { | ||||
|             self_stacked_item.entity_ids.append(&mut other_stacked_item.entity_ids); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn as_client_bytes(&self) -> [u8; 16] { | ||||
|         match self { | ||||
|             InventoryItem::Individual(item) => { | ||||
|                 match &item.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(), | ||||
|                     ItemDetail::ESWeapon(e) => e.as_bytes(), | ||||
|                 } | ||||
|             }, | ||||
|             InventoryItem::Stacked(item) => { | ||||
|                 item.tool.as_stacked_bytes(item.entity_ids.len()) | ||||
|             }, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn can_add_to(&mut self, stacked_floor_item: &StackedFloorItem) -> Result<(), InventoryItemAddToError> { | ||||
|         if let InventoryItem::Stacked(stacked_inventory_item) = self { | ||||
|             if stacked_floor_item.tool != stacked_inventory_item.tool { | ||||
|                 return Err(InventoryItemAddToError::DifferentTool) | ||||
|             } | ||||
| 
 | ||||
|             if stacked_floor_item.tool.tool.max_stack() < (stacked_floor_item.count() + stacked_inventory_item.count()) { | ||||
|                 return Err(InventoryItemAddToError::ExceedsCapacity) | ||||
|             } | ||||
|             Ok(()) | ||||
|         } | ||||
|         else { | ||||
|             Err(InventoryItemAddToError::BothAreNotStacked) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn add_to(&mut self, mut stacked_floor_item: StackedFloorItem) -> Result<(), InventoryItemAddToError> { | ||||
|         self.can_add_to(&stacked_floor_item)?; | ||||
|         if let InventoryItem::Stacked(stacked_inventory_item) = self { | ||||
|             stacked_inventory_item.entity_ids.append(&mut stacked_floor_item.entity_ids); | ||||
|         } | ||||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
|     pub fn individual(&self) -> Option<&IndividualInventoryItem> { | ||||
|         match self { | ||||
|             InventoryItem::Individual(ref individual_inventory_item) => Some(individual_inventory_item), | ||||
|             _ => None | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn individual_mut(&mut self) -> Option<&mut IndividualInventoryItem> { | ||||
|         match self { | ||||
|             InventoryItem::Individual(ref mut individual_inventory_item) => Some(individual_inventory_item), | ||||
|             _ => None | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn get_sell_price(&self) -> Result<u32, ItemManagerError> { | ||||
|         match self { | ||||
|             InventoryItem::Individual(individual_item) => { | ||||
|                 match &individual_item.item { | ||||
|                     // TODO: can wrapped items be sold?
 | ||||
|                     ItemDetail::Weapon(w) => { | ||||
|                         if !w.tekked { | ||||
|                             return Ok(1u32) | ||||
|                         } | ||||
|                         if w.is_rare_item() { | ||||
|                             return Ok(10u32) | ||||
|                         } | ||||
|                         Ok((WeaponShopItem::from(w).price() / 8) as u32) | ||||
|                     }, | ||||
|                     ItemDetail::Armor(a) => { | ||||
|                         if a.is_rare_item() { | ||||
|                             return Ok(10u32) | ||||
|                         } | ||||
|                         Ok((ArmorShopItem::from(a).price() / 8) as u32) | ||||
|                     }, | ||||
|                     ItemDetail::Shield(s) => { | ||||
|                         if s.is_rare_item() { | ||||
|                             return Ok(10u32) | ||||
|                         } | ||||
|                         Ok((ArmorShopItem::from(s).price() / 8) as u32) | ||||
|                     }, | ||||
|                     ItemDetail::Unit(u) => { | ||||
|                         if u.is_rare_item() { | ||||
|                             return Ok(10u32) | ||||
|                         } | ||||
|                         Ok((ArmorShopItem::from(u).price() / 8) as u32) | ||||
|                     }, | ||||
|                     ItemDetail::Tool(t) => { | ||||
|                         if !matches!(t.tool, ToolType::PhotonDrop | ToolType::PhotonSphere | ToolType::PhotonCrystal) && t.is_rare_item() { | ||||
|                             return Ok(10u32) | ||||
|                         } | ||||
|                         Ok((ToolShopItem::from(t).price() / 8) as u32) | ||||
|                     }, | ||||
|                     ItemDetail::TechniqueDisk(d) => { | ||||
|                         Ok((ToolShopItem::from(d).price() / 8) as u32) | ||||
|                     }, | ||||
|                     ItemDetail::Mag(_m) => { | ||||
|                         Err(ItemManagerError::ItemNotSellable(self.clone())) | ||||
|                     }, | ||||
|                     ItemDetail::ESWeapon(_e) => { | ||||
|                         Ok(10u32) | ||||
|                     }, | ||||
|                 } | ||||
|             }, | ||||
|             // the number of stacked items sold is handled by the caller. this is just the price of 1
 | ||||
|             InventoryItem::Stacked(stacked_item) => { | ||||
|                 Ok((ToolShopItem::from(&stacked_item.tool).price() / 8) as u32) | ||||
|             }, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn stacked(&self) -> Option<&StackedInventoryItem> { | ||||
|         match self { | ||||
|             InventoryItem::Stacked(ref stacked_inventory_item) => Some(stacked_inventory_item), | ||||
|             _ => None | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn stacked_mut(&mut self) -> Option<&mut StackedInventoryItem> { | ||||
|         match self { | ||||
|             InventoryItem::Stacked(ref mut stacked_inventory_item) => Some(stacked_inventory_item), | ||||
|             _ => None | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn mag(&self) -> Option<&Mag> { | ||||
|         match self { | ||||
|             InventoryItem::Individual(individual_inventory_item) => individual_inventory_item.mag(), | ||||
|             _ => None | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| #[derive(Error, Debug, Clone)] | ||||
| #[error("")] | ||||
| pub enum InventoryItemConsumeError { | ||||
|     InconsistentState, | ||||
|     InvalidAmount, | ||||
| } | ||||
| 
 | ||||
| pub struct IndividualConsumedItem { | ||||
|     pub entity_id: ItemEntityId, | ||||
|     pub item: ItemDetail, | ||||
| } | ||||
| 
 | ||||
| pub struct StackedConsumedItem { | ||||
|     pub entity_ids: Vec<ItemEntityId>, | ||||
|     pub tool: Tool | ||||
| } | ||||
| 
 | ||||
| pub enum ConsumedItem { | ||||
|     Individual(IndividualConsumedItem), | ||||
|     Stacked(StackedConsumedItem), | ||||
| } | ||||
| 
 | ||||
| impl ConsumedItem { | ||||
|     pub fn entity_ids(&self) -> Vec<ItemEntityId> { | ||||
|         match self { | ||||
|             ConsumedItem::Individual(individual_consumed_item) => { | ||||
|                 vec![individual_consumed_item.entity_id] | ||||
|             }, | ||||
|             ConsumedItem::Stacked(stacked_consumed_item) => { | ||||
|                 stacked_consumed_item.entity_ids.clone() | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn item(&self) -> ItemDetail { | ||||
|         match self { | ||||
|             ConsumedItem::Individual(individual_consumed_item) => { | ||||
|                 individual_consumed_item.item.clone() | ||||
|             }, | ||||
|             ConsumedItem::Stacked(stacked_consumed_item) => { | ||||
|                 ItemDetail::Tool(stacked_consumed_item.tool) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| pub struct InventoryItemHandle<'a> { | ||||
|     inventory: &'a mut CharacterInventory, | ||||
|     slot: usize, | ||||
| } | ||||
| 
 | ||||
| impl<'a> InventoryItemHandle<'a> { | ||||
|     pub fn item(&'a self) -> Option<&'a InventoryItem> { | ||||
|         self.inventory.items.get(self.slot) | ||||
|     } | ||||
| 
 | ||||
|     pub fn item_mut(&mut self) -> Option<&mut InventoryItem> { | ||||
|         self.inventory.items.get_mut(self.slot) | ||||
|     } | ||||
| 
 | ||||
|     pub fn remove_from_inventory(self) { | ||||
|         self.inventory.items.remove(self.slot); | ||||
|     } | ||||
| 
 | ||||
|     pub fn consume(self, amount: usize) -> Result<ConsumedItem, InventoryItemConsumeError> { | ||||
|         enum RemoveMethod { | ||||
|             EntireThing(ConsumedItem), | ||||
|             Partial(Tool), | ||||
|         } | ||||
| 
 | ||||
|         let inventory_item = self.inventory.items.get(self.slot).ok_or(InventoryItemConsumeError::InconsistentState)?; | ||||
|         let remove_method = match inventory_item { | ||||
|             InventoryItem::Individual(individual_inventory_item) => { | ||||
|                 RemoveMethod::EntireThing(ConsumedItem::Individual(IndividualConsumedItem { | ||||
|                     entity_id: individual_inventory_item.entity_id, | ||||
|                     item: individual_inventory_item.item.clone() | ||||
|                 })) | ||||
|             }, | ||||
|             InventoryItem::Stacked(stacked_inventory_item) => { | ||||
|                 match stacked_inventory_item.count().cmp(&amount) { | ||||
|                     Ordering::Equal => { | ||||
|                         RemoveMethod::EntireThing(ConsumedItem::Stacked(StackedConsumedItem { | ||||
|                             entity_ids: stacked_inventory_item.entity_ids.clone(), | ||||
|                             tool: stacked_inventory_item.tool, | ||||
|                         })) | ||||
|                     }, | ||||
|                     Ordering::Greater => { | ||||
|                         RemoveMethod::Partial(stacked_inventory_item.tool) | ||||
|                     }, | ||||
|                     Ordering::Less => { | ||||
|                         return Err(InventoryItemConsumeError::InvalidAmount) | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|         }; | ||||
| 
 | ||||
|         match remove_method { | ||||
|             RemoveMethod::EntireThing(consumed_item) => { | ||||
|                 self.inventory.items.remove(self.slot); | ||||
|                 Ok(consumed_item) | ||||
|             }, | ||||
|             RemoveMethod::Partial(tool) => { | ||||
|                 let entity_ids = self.inventory.items.get_mut(self.slot) | ||||
|                     .and_then(|item| { | ||||
|                         if let InventoryItem::Stacked(stacked_inventory_item) = item { | ||||
|                             Some(stacked_inventory_item.entity_ids.drain(..amount).collect::<Vec<_>>()) | ||||
|                         } | ||||
|                         else { | ||||
|                             None | ||||
|                         } | ||||
|                     }) | ||||
|                     .ok_or(InventoryItemConsumeError::InvalidAmount)?; | ||||
|                 Ok(ConsumedItem::Stacked(StackedConsumedItem { | ||||
|                     entity_ids, | ||||
|                     tool, | ||||
|                 })) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn get_slot(&self) -> usize { | ||||
|         self.slot | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub struct CharacterInventory { | ||||
|     item_id_counter: u32, | ||||
|     items: Vec<InventoryItem>, | ||||
|     equipped: EquippedEntity, | ||||
| } | ||||
| 
 | ||||
| impl CharacterInventory { | ||||
|     pub fn new(items: Vec<InventoryItem>, equipped: &EquippedEntity) -> CharacterInventory { | ||||
|         CharacterInventory{ | ||||
|             item_id_counter: 0, | ||||
|             items, | ||||
|             equipped: equipped.clone(), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn initialize_item_ids(&mut self, base_item_id: u32) { | ||||
|         for (i, item) in self.items.iter_mut().enumerate() { | ||||
|             item.set_item_id(ClientItemId(base_item_id + i as u32)); | ||||
|         } | ||||
|         self.item_id_counter = base_item_id + self.items.len() as u32 + 1; | ||||
|     } | ||||
| 
 | ||||
|     pub fn as_client_inventory_items(&self) -> [character::InventoryItem; 30] { | ||||
|         self.items.iter() | ||||
|             .enumerate() | ||||
|             .fold([character::InventoryItem::default(); 30], |mut inventory, (slot, item)| { | ||||
|                 let bytes = 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.item_id().0; | ||||
|                 inventory[slot].equipped = 0; | ||||
|                 inventory[slot].flags = 0; | ||||
| 
 | ||||
|                 if let InventoryItem::Individual(individual_item) = item { | ||||
|                     if self.equipped.is_equipped(&individual_item.entity_id) { | ||||
|                         if let ItemDetail::Unit(_) = individual_item.item { | ||||
|                             inventory[slot].data1[4] = self.equipped.unit.iter() | ||||
|                                 .enumerate() | ||||
|                                 .find(|(_, u_id)| **u_id == Some(individual_item.entity_id)) | ||||
|                                 .map(|(a, _)| a) | ||||
|                                 .unwrap_or(0) as u8 | ||||
|                         } | ||||
|                         inventory[slot].equipped = 1; | ||||
|                         inventory[slot].flags |= 8; | ||||
|                     } | ||||
|                 } | ||||
|                 inventory | ||||
|             }) | ||||
|     } | ||||
| 
 | ||||
|     pub fn slot(&self, slot: usize) -> Option<&InventoryItem> { | ||||
|         self.items.get(slot) | ||||
|     } | ||||
| 
 | ||||
|     pub fn count(&self) -> usize { | ||||
|         self.items.len() | ||||
|     } | ||||
| 
 | ||||
|     pub fn space_for_individual_item(&self) -> bool { | ||||
|         self.count() < INVENTORY_CAPACITY | ||||
|     } | ||||
| 
 | ||||
|     pub fn space_for_stacked_item(&self, tool: &Tool, amount: usize) -> SpaceForStack { | ||||
|         let existing_item = self.items.iter() | ||||
|             .filter_map(|item| { | ||||
|                 match item { | ||||
|                     InventoryItem::Stacked(s_item) => { | ||||
|                         Some(s_item) | ||||
|                     }, | ||||
|                     _ => None | ||||
|                 } | ||||
|             }) | ||||
|             .find(|s_item| { | ||||
|                 s_item.tool == *tool | ||||
|             }); | ||||
| 
 | ||||
|         match existing_item { | ||||
|             Some(item) => { | ||||
|                 if item.count() + amount <= tool.tool.max_stack() { | ||||
|                     SpaceForStack::Yes(YesThereIsSpace::ExistingStack) | ||||
|                 } | ||||
|                 else { | ||||
|                     SpaceForStack::No(NoThereIsNotSpace::FullStack) | ||||
|                 } | ||||
|             } | ||||
|             None => { | ||||
|                 if self.count() < INVENTORY_CAPACITY  { | ||||
|                     SpaceForStack::Yes(YesThereIsSpace::NewStack) | ||||
|                 } | ||||
|                 else { | ||||
|                     SpaceForStack::No(NoThereIsNotSpace::FullInventory) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn stack_item_id(&self, tool: &Tool) -> Option<ClientItemId> { | ||||
|         self.items.iter() | ||||
|             .filter_map(|item| { | ||||
|                 match item { | ||||
|                     InventoryItem::Stacked(s_item) => { | ||||
|                         Some(s_item) | ||||
|                     }, | ||||
|                     _ => None | ||||
|                 } | ||||
|             }) | ||||
|             .find(|s_item| { | ||||
|                 s_item.tool == *tool | ||||
|             }) | ||||
|             .map(|item| { | ||||
|                 item.item_id | ||||
|             }) | ||||
|     } | ||||
| 
 | ||||
|     pub fn get_item_handle_by_id(&mut self, item_id: ClientItemId) -> Option<InventoryItemHandle> { | ||||
|         let (slot, _) = self.items.iter() | ||||
|             .enumerate() | ||||
|             .find(|(_, item)| { | ||||
|                 item.item_id() == item_id | ||||
|             })?; | ||||
|         Some(InventoryItemHandle { | ||||
|             inventory: self, | ||||
|             slot, | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     pub fn get_equipped_mag_handle(&mut self) -> Option<InventoryItemHandle> { | ||||
|         let (slot, _) = self.items.iter() | ||||
|             .enumerate() | ||||
|             .find(|(_, item)| { | ||||
|                 if let InventoryItem::Individual(individual_inventory_item) = item { | ||||
|                     if let ItemDetail::Mag(_) = &individual_inventory_item.item { | ||||
|                         return self.equipped.is_equipped(&individual_inventory_item.entity_id) | ||||
|                     } | ||||
|                 } | ||||
|                 false | ||||
|             })?; | ||||
|         Some(InventoryItemHandle { | ||||
|             inventory: self, | ||||
|             slot, | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     pub fn get_equipped_armor_handle(&mut self) -> Option<InventoryItemHandle> { | ||||
|         let (slot, _) = self.items.iter() | ||||
|             .enumerate() | ||||
|             .find(|(_, item)| { | ||||
|                 if let InventoryItem::Individual(individual_inventory_item) = item { | ||||
|                     if let ItemDetail::Armor(_) = &individual_inventory_item.item { | ||||
|                         return self.equipped.is_equipped(&individual_inventory_item.entity_id) | ||||
|                     } | ||||
|                 } | ||||
|                 false | ||||
|             })?; | ||||
|         Some(InventoryItemHandle { | ||||
|             inventory: self, | ||||
|             slot, | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     pub fn get_equipped_shield_handle(&mut self) -> Option<InventoryItemHandle> { | ||||
|         let (slot, _) = self.items.iter() | ||||
|             .enumerate() | ||||
|             .find(|(_, item)| { | ||||
|                 if let InventoryItem::Individual(individual_inventory_item) = item { | ||||
|                     if let ItemDetail::Shield(_) = &individual_inventory_item.item { | ||||
|                         return self.equipped.is_equipped(&individual_inventory_item.entity_id) | ||||
|                     } | ||||
|                 } | ||||
|                 false | ||||
|             })?; | ||||
|         Some(InventoryItemHandle { | ||||
|             inventory: self, | ||||
|             slot, | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     pub fn get_equipped_weapon_handle(&mut self) -> Option<InventoryItemHandle> { | ||||
|         let (slot, _) = self.items.iter() | ||||
|             .enumerate() | ||||
|             .find(|(_, item)| { | ||||
|                 if let InventoryItem::Individual(individual_inventory_item) = item { | ||||
|                     if let ItemDetail::Weapon(_) = &individual_inventory_item.item { | ||||
|                         return self.equipped.is_equipped(&individual_inventory_item.entity_id) | ||||
|                     } | ||||
|                 } | ||||
|                 false | ||||
|             })?; | ||||
|         Some(InventoryItemHandle { | ||||
|             inventory: self, | ||||
|             slot, | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     pub fn get_item_by_id(&self, item_id: ClientItemId) -> Option<&InventoryItem> { | ||||
|         self.items.iter() | ||||
|             .find(|item| { | ||||
|                 item.item_id() == item_id | ||||
|             }) | ||||
|     } | ||||
| 
 | ||||
|     pub fn take_item_by_id(&mut self, item_id: ClientItemId) -> Option<InventoryItem> { | ||||
|         self.items | ||||
|             .drain_filter(|i| i.item_id() == item_id) | ||||
|             .next() | ||||
|     } | ||||
| 
 | ||||
|     pub fn take_stacked_item_by_id(&mut self, item_id: ClientItemId, amount: usize) -> Option<StackedInventoryItem> { | ||||
|         let idx = self.items | ||||
|             .iter_mut() | ||||
|             .position(|i| i.item_id() == item_id)?; | ||||
|         let item: &mut StackedInventoryItem = self.items.get_mut(idx)?.stacked_mut()?; | ||||
|         match item.entity_ids.len().cmp(&amount) { | ||||
|             Ordering::Equal => { | ||||
|                 let item = self.items.remove(idx); | ||||
|                 item.stacked().cloned() | ||||
|             }, | ||||
|             Ordering::Greater => { | ||||
|                 let entity_ids = item.entity_ids.drain(..amount).collect(); | ||||
|                 Some(StackedInventoryItem { | ||||
|                     entity_ids, | ||||
|                     tool: item.tool, | ||||
|                     item_id: item.item_id, | ||||
|                 }) | ||||
|             }, | ||||
|             Ordering::Less => { | ||||
|                 None | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn add_item(&mut self, item: InventoryItem) { | ||||
|         self.items.push(item); | ||||
|     } | ||||
| 
 | ||||
|     pub fn add_stacked_item(&mut self, mut item: StackedInventoryItem) { | ||||
|         let existing_item = self.items | ||||
|             .iter_mut() | ||||
|             .filter_map(|i| { | ||||
|                 match i { | ||||
|                     InventoryItem::Stacked(stacked) => { | ||||
|                         Some(stacked) | ||||
|                     }, | ||||
|                     _ => None | ||||
|                 } | ||||
|             }) | ||||
|             .find(|i| { | ||||
|                 i.tool == item.tool | ||||
|             }); | ||||
| 
 | ||||
|         match existing_item { | ||||
|             Some(existing_item) => { | ||||
|                 existing_item.entity_ids.append(&mut item.entity_ids) | ||||
|             }, | ||||
|             None => { | ||||
|                 self.items.push(InventoryItem::Stacked(item)) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn add_item_with_new_item_id(&mut self, item: InventoryItem, item_id: ClientItemId) { | ||||
|         match item { | ||||
|             InventoryItem::Individual(mut individual_inventory_item) => { | ||||
|                 individual_inventory_item.item_id = item_id; | ||||
|                 self.add_item(InventoryItem::Individual(individual_inventory_item)); | ||||
|             }, | ||||
|             InventoryItem::Stacked(mut stacked_inventory_item) => { | ||||
|                 stacked_inventory_item.item_id = item_id; | ||||
|                 self.add_stacked_item(stacked_inventory_item) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn add_individual_floor_item(&mut self, floor_item: &IndividualFloorItem) -> &InventoryItem { | ||||
|         self.items.push(InventoryItem::Individual(IndividualInventoryItem { | ||||
|             entity_id: floor_item.entity_id, | ||||
|             item_id: floor_item.item_id, | ||||
|             item: floor_item.item.clone(), | ||||
|         })); | ||||
| 
 | ||||
|         self.items.last().unwrap() | ||||
|     } | ||||
| 
 | ||||
|     // TODO: should these pick up functions take floor_item as mut and remove the ids?
 | ||||
|     pub fn pick_up_individual_floor_item(&mut self, floor_item: &IndividualFloorItem) -> Option<(&IndividualInventoryItem, InventorySlot)> { | ||||
|         if self.count() >= 30 { | ||||
|             return None; | ||||
|         } | ||||
| 
 | ||||
|         self.items.push(InventoryItem::Individual(IndividualInventoryItem { | ||||
|             entity_id: floor_item.entity_id, | ||||
|             item_id: floor_item.item_id, | ||||
|             item: floor_item.item.clone(), | ||||
|         })); | ||||
| 
 | ||||
|         if let Some(InventoryItem::Individual(new_item)) = self.items.last() { | ||||
|             Some((new_item, InventorySlot(self.count()-1))) | ||||
|         } | ||||
|         else { | ||||
|             None | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn add_stacked_floor_item(&mut self, floor_item: &StackedFloorItem) { | ||||
|         let existing_item = self.items.iter_mut() | ||||
|             .filter_map(|item| { | ||||
|                 match item { | ||||
|                     InventoryItem::Stacked(s_item) => Some(s_item), | ||||
|                     _ => None, | ||||
|                 } | ||||
| 
 | ||||
|             }) | ||||
|             .find(|item| { | ||||
|                 item.tool == floor_item.tool | ||||
|             }); | ||||
| 
 | ||||
|         match existing_item { | ||||
|             Some(item) => { | ||||
|                 item.entity_ids.append(&mut floor_item.entity_ids.clone()) | ||||
|             }, | ||||
|             None => { | ||||
|                 self.items.push(InventoryItem::Stacked(StackedInventoryItem { | ||||
|                     entity_ids: floor_item.entity_ids.clone(), | ||||
|                     item_id: floor_item.item_id, | ||||
|                     tool: floor_item.tool, | ||||
|                 })); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // TODO: can be simplified using find instead of position
 | ||||
|     pub fn pick_up_stacked_floor_item(&mut self, floor_item: &StackedFloorItem) -> Option<(&StackedInventoryItem, InventorySlot)> { | ||||
|         let existing_stack_position = self.items.iter() | ||||
|             .position(|inventory_item| { | ||||
|                 if let InventoryItem::Stacked(stacked_inventory_item) = inventory_item { | ||||
|                     if stacked_inventory_item.tool == floor_item.tool { | ||||
|                         return true | ||||
|                     } | ||||
|                 } | ||||
|                 false | ||||
|             }); | ||||
| 
 | ||||
|         if let Some(existing_stack_position) = existing_stack_position { | ||||
|             if let Some(InventoryItem::Stacked(stacked_item)) = self.items.get_mut(existing_stack_position) { | ||||
|                 if stacked_item.count() + floor_item.count() <= stacked_item.tool.max_stack() { | ||||
|                     stacked_item.entity_ids.append(&mut floor_item.entity_ids.clone()); | ||||
|                     Some((stacked_item, InventorySlot(existing_stack_position))) | ||||
|                 } | ||||
|                 else { | ||||
|                     None | ||||
|                 } | ||||
|             } | ||||
|             else { | ||||
|                 None | ||||
|             } | ||||
|         } | ||||
|         else { | ||||
|             let new_stacked_item = InventoryItem::Stacked(StackedInventoryItem { | ||||
|                 entity_ids: floor_item.entity_ids.clone(), | ||||
|                 item_id: floor_item.item_id, | ||||
|                 tool: floor_item.tool, | ||||
|             }); | ||||
| 
 | ||||
|             self.items.push(new_stacked_item); | ||||
|             if let Some(InventoryItem::Stacked(new_item)) = self.items.last() { | ||||
|                 Some((new_item, InventorySlot(self.count()-1))) | ||||
|             } | ||||
|             else { | ||||
|                 None | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn withdraw_item(&mut self, mut bank_item: BankItemHandle, amount: usize) -> Option<(&InventoryItem, usize)> { | ||||
|         let (remove, slot) = match bank_item.item_mut()? { | ||||
|             BankItem::Individual(individual_bank_item) => { | ||||
|                 if self.items.len() >= INVENTORY_CAPACITY { | ||||
|                     return None | ||||
|                 } | ||||
|                 self.items.push(InventoryItem::Individual(IndividualInventoryItem { | ||||
|                     entity_id: individual_bank_item.entity_id, | ||||
|                     item_id: individual_bank_item.item_id, | ||||
|                     item: individual_bank_item.item.clone(), | ||||
|                 })); | ||||
|                 (true, self.count()-1) | ||||
|             }, | ||||
|             BankItem::Stacked(stacked_bank_item) => { | ||||
|                 let existing_inventory_item = self.items.iter_mut() | ||||
|                     .enumerate() | ||||
|                     .find_map(|(index, item)| { | ||||
|                         if let InventoryItem::Stacked(stacked_inventory_item) = item { | ||||
|                             if stacked_bank_item.tool == stacked_inventory_item.tool { | ||||
|                                 return Some((index, stacked_inventory_item)) | ||||
|                             } | ||||
|                         } | ||||
|                         None | ||||
|                     }); | ||||
| 
 | ||||
|                 let slot = match existing_inventory_item { | ||||
|                     Some((slot, stacked_inventory_item)) => { | ||||
|                         if stacked_inventory_item.count() + stacked_bank_item.count() > stacked_bank_item.tool.max_stack() { | ||||
|                             return None | ||||
|                         } | ||||
| 
 | ||||
|                         let mut withdrawn_entity_ids = stacked_bank_item.take_entity_ids(amount)?; | ||||
|                         stacked_inventory_item.entity_ids.append(&mut withdrawn_entity_ids); | ||||
|                         slot | ||||
|                     } | ||||
|                     None => { | ||||
|                         if self.items.len() >= INVENTORY_CAPACITY { | ||||
|                             return None | ||||
|                         } | ||||
|                         let withdrawn_entity_ids = stacked_bank_item.take_entity_ids(amount)?; | ||||
| 
 | ||||
|                         self.item_id_counter += 1; // oh no
 | ||||
|                         self.items.push(InventoryItem::Stacked(StackedInventoryItem { | ||||
|                             entity_ids: withdrawn_entity_ids, | ||||
|                             item_id: ClientItemId(self.item_id_counter), | ||||
|                             tool: stacked_bank_item.tool, | ||||
|                         })); | ||||
|                         self.count()-1 | ||||
|                     } | ||||
|                 }; | ||||
|                 (stacked_bank_item.count() == 0, slot) | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         if remove { | ||||
|             bank_item.remove_from_bank(); | ||||
|         } | ||||
| 
 | ||||
|         self.items.last().map(|item| { | ||||
|             (item, slot) | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     pub fn iter(&self) -> impl Iterator<Item = &InventoryItem> { | ||||
|         self.items.iter() | ||||
|     } | ||||
| 
 | ||||
|     pub fn items(&self) -> &Vec<InventoryItem> { | ||||
|         &self.items | ||||
|     } | ||||
| 
 | ||||
|     pub fn set_items(&mut self, sorted_items: Vec<InventoryItem>) { | ||||
|         self.items = sorted_items; | ||||
|     } | ||||
| 
 | ||||
|     pub fn remove_by_id(&mut self, id: ClientItemId) -> Option<InventoryItem> { | ||||
|         self.items.iter() | ||||
|             .position(|i| i.item_id() == id) | ||||
|             .map(|position| { | ||||
|                 self.items.remove(position) | ||||
|             }) | ||||
|     } | ||||
| 
 | ||||
|     pub fn equip(&mut self, id: &ClientItemId, equip_slot: u8) { | ||||
|         for item in &self.items { | ||||
|             if let InventoryItem::Individual(inventory_item) = item { | ||||
|                 if inventory_item.item_id == *id { | ||||
|                     match inventory_item.item { | ||||
|                         ItemDetail::Weapon(_) => self.equipped.weapon = Some(inventory_item.entity_id), | ||||
|                         ItemDetail::Armor(_) => self.equipped.armor = Some(inventory_item.entity_id), | ||||
|                         ItemDetail::Shield(_) => self.equipped.shield = Some(inventory_item.entity_id), | ||||
|                         ItemDetail::Unit(_) => { | ||||
|                             if let Some(unit) = self.equipped.unit.get_mut(equip_slot as usize) { | ||||
|                                 *unit = Some(inventory_item.entity_id) | ||||
|                             } | ||||
|                         } | ||||
|                         ItemDetail::Mag(_) => self.equipped.mag = Some(inventory_item.entity_id), | ||||
|                         _ => {} | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn unequip(&mut self, id: &ClientItemId) { | ||||
|         for item in &self.items { | ||||
|             if let InventoryItem::Individual(inventory_item) = item { | ||||
|                 if inventory_item.item_id == *id { | ||||
|                     match inventory_item.item { | ||||
|                         ItemDetail::Weapon(_) => self.equipped.weapon = None, | ||||
|                         ItemDetail::Armor(_) => { | ||||
|                             self.equipped.armor = None; | ||||
|                             self.equipped.unit = [None; 4]; | ||||
|                         } | ||||
|                         ItemDetail::Shield(_) => self.equipped.shield = None, | ||||
|                         ItemDetail::Unit(_) => { | ||||
|                             for unit in self.equipped.unit.iter_mut() { | ||||
|                                 if *unit == Some(inventory_item.entity_id) { | ||||
|                                     *unit = None | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                         ItemDetail::Mag(_) => self.equipped.mag = Some(inventory_item.entity_id), | ||||
|                         _ => {} | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn as_inventory_entity(&self, _character_id: &CharacterEntityId) -> InventoryEntity { | ||||
|         InventoryEntity { | ||||
|             items: self.items.iter() | ||||
|                 .map(|item| { | ||||
|                     match item { | ||||
|                         InventoryItem::Individual(item) => { | ||||
|                             InventoryItemEntity::Individual(ItemEntity { | ||||
|                                 id: item.entity_id, | ||||
|                                 item: item.item.clone(), | ||||
|                             }) | ||||
|                         }, | ||||
|                         InventoryItem::Stacked(items) => { | ||||
|                             InventoryItemEntity::Stacked(items.entity_ids.iter() | ||||
|                                                          .map(|id| { | ||||
|                                                              ItemEntity { | ||||
|                                                                  id: *id, | ||||
|                                                                  item: ItemDetail::Tool(items.tool) | ||||
|                                                              } | ||||
|                                                          }) | ||||
|                                                          .collect()) | ||||
|                         }, | ||||
|                     } | ||||
|                 }) | ||||
|                 .collect() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn as_equipped_entity(&self) -> EquippedEntity { | ||||
|         self.equipped.clone() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -20,7 +20,6 @@ use crate::ship::packet::handler::trade::{TradeError, OTHER_MESETA_ITEM_ID}; | ||||
| use crate::ship::items::bank::*; | ||||
| use crate::ship::items::floor::*; | ||||
| use crate::ship::items::inventory::*; | ||||
| use crate::ship::items::use_tool; | ||||
| use crate::ship::items::transaction::{ItemTransaction, ItemAction, TransactionError, TransactionCommitError}; | ||||
| 
 | ||||
| #[derive(PartialEq, Eq)] | ||||
|  | ||||
| @ -1,9 +1,3 @@ | ||||
| mod bank; | ||||
| mod floor; | ||||
| pub mod inventory; | ||||
| pub mod manager; | ||||
| pub mod transaction; | ||||
| pub mod use_tool; | ||||
| pub mod state; | ||||
| pub mod actions; | ||||
| pub mod apply_item; | ||||
| @ -12,10 +6,4 @@ use serde::{Serialize, Deserialize}; | ||||
| #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Serialize, Deserialize, derive_more::Display)] | ||||
| pub struct ClientItemId(pub u32); | ||||
| 
 | ||||
| // TODO: remove these and fix use statements in the rest of the codebase
 | ||||
| pub use inventory::*; | ||||
| pub use floor::*; | ||||
| pub use bank::*; | ||||
| pub use manager::*; | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -1,337 +0,0 @@ | ||||
| use crate::entity::gateway::EntityGateway; | ||||
| use thiserror::Error; | ||||
| use crate::ship::items::manager::{ItemManager, ItemManagerError}; | ||||
| use crate::entity::gateway::GatewayError; | ||||
| 
 | ||||
| #[derive(Error, Debug)] | ||||
| pub enum TransactionCommitError { | ||||
|     #[error("transaction commit gateway error {0}")] | ||||
|     Gateway(#[from] GatewayError), | ||||
|     #[error("transaction commit itemmanager error {0}")] | ||||
|     ItemManager(#[from] ItemManagerError), | ||||
| } | ||||
| 
 | ||||
| #[async_trait::async_trait] | ||||
| pub trait ItemAction<EG: EntityGateway>: std::marker::Send + std::marker::Sync + std::fmt::Debug { | ||||
|     async fn commit(&self, manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError>; | ||||
| } | ||||
| 
 | ||||
| pub struct ItemTransactionActions<'a, EG: EntityGateway> { | ||||
|     action_queue: Vec<Box<dyn ItemAction<EG>>>, | ||||
|     pub manager: &'a ItemManager, | ||||
| } | ||||
| 
 | ||||
|     
 | ||||
| impl<'a, EG: EntityGateway> ItemTransactionActions<'a, EG> { | ||||
|     fn new(manager: &'a ItemManager) -> ItemTransactionActions<'a, EG> { | ||||
|         ItemTransactionActions { | ||||
|             action_queue: Vec::new(), | ||||
|             manager | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn action(&mut self, action: Box<dyn ItemAction<EG>>) { | ||||
|         self.action_queue.push(action) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| pub struct ItemTransaction<'a, T, EG: EntityGateway> { | ||||
|     data: T, | ||||
|     actions: ItemTransactionActions<'a, EG>, | ||||
| } | ||||
| 
 | ||||
| impl<'a, T, EG: EntityGateway> ItemTransaction<'a, T, EG> { | ||||
|     pub fn new(manager: &'a ItemManager, arg: T) -> ItemTransaction<'a, T, EG> { | ||||
|         ItemTransaction { | ||||
|             data: arg, | ||||
|             actions: ItemTransactionActions::new(manager), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn act<E: std::fmt::Debug, U>(mut self, action: fn(&mut ItemTransactionActions<EG>, &T) -> Result<U, E>) -> FinalizedItemTransaction<U, E, EG> { | ||||
|         match action(&mut self.actions, &self.data) { | ||||
|             Ok(k) => { | ||||
|                 FinalizedItemTransaction { | ||||
|                     value: Ok(k), | ||||
|                     action_queue: self.actions.action_queue, | ||||
|                 } | ||||
|             }, | ||||
|             Err(err) => { | ||||
|                 FinalizedItemTransaction { | ||||
|                     value: Err(err), | ||||
|                     action_queue: Vec::new(), | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #[derive(Error, Debug)] | ||||
| pub enum TransactionError<E: std::fmt::Debug> { | ||||
|     #[error("transaction action error {0:?}")] | ||||
|     Action(E), | ||||
|     #[error("transaction commit error {0}")] | ||||
|     Commit(#[from] TransactionCommitError), | ||||
|     
 | ||||
| } | ||||
| 
 | ||||
| // this only exists to drop the ItemManager borrow of ItemTransaction so a mutable ItemTransaction can be passed in later
 | ||||
| pub struct FinalizedItemTransaction<T, E: std::fmt::Debug, EG: EntityGateway> { | ||||
|     value: Result<T, E>, | ||||
|     action_queue: Vec<Box<dyn ItemAction<EG>>>, | ||||
| } | ||||
| 
 | ||||
| impl<T, E: std::fmt::Debug, EG: EntityGateway> FinalizedItemTransaction<T, E, EG> { | ||||
|     pub async fn commit(self, item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<T, TransactionError<E>> { | ||||
|         match self.value { | ||||
|             Ok(value) => { | ||||
|                 for action in self.action_queue.into_iter() { | ||||
|                     // TODO: better handle rolling back if this ever errors out
 | ||||
|                     action.commit(item_manager, entity_gateway).await.map_err(|err| TransactionError::Commit(err))?; | ||||
|                 } | ||||
|                 Ok(value) | ||||
|             }, | ||||
|             Err(err) => Err(TransactionError::Action(err)), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod test { | ||||
|     use super::*; | ||||
|     use crate::entity::account::{UserAccountId, NewUserAccountEntity, UserAccountEntity}; | ||||
|     use crate::entity::character::{NewCharacterEntity, CharacterEntity}; | ||||
|     use crate::entity::gateway::GatewayError; | ||||
|     use thiserror::Error; | ||||
|     
 | ||||
|     #[async_std::test] | ||||
|     async fn test_item_transaction() { | ||||
|         #[derive(Debug)] | ||||
|         struct DummyAction1 { | ||||
|             name: String, | ||||
|         } | ||||
|         #[derive(Debug)] | ||||
|         struct DummyAction2 { | ||||
|             value: u32, | ||||
|         } | ||||
| 
 | ||||
|         #[derive(Error, Debug)] | ||||
|         #[error("")] | ||||
|         enum DummyError { | ||||
|             Error | ||||
|         } | ||||
| 
 | ||||
|         #[derive(Default, Clone)] | ||||
|         struct DummyGateway { | ||||
|             d1_set: String, | ||||
|             d2_inc: u32, | ||||
|         } | ||||
| 
 | ||||
|         #[async_trait::async_trait] | ||||
|         impl EntityGateway for DummyGateway { | ||||
|             async fn create_user(&mut self, user: NewUserAccountEntity) -> Result<UserAccountEntity, GatewayError> { | ||||
|                 self.d1_set = user.username; | ||||
|                 Ok(UserAccountEntity::default()) | ||||
|             } | ||||
| 
 | ||||
|             async fn create_character(&mut self, char: NewCharacterEntity) -> Result<CharacterEntity, GatewayError> { | ||||
|                 self.d2_inc += char.slot; | ||||
|                 Ok(CharacterEntity::default()) | ||||
|             } | ||||
|         } | ||||
|         
 | ||||
| 
 | ||||
|         #[async_trait::async_trait] | ||||
|         impl<EG: EntityGateway> ItemAction<EG> for DummyAction1 { | ||||
|             async fn commit(&self, item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> { | ||||
|                 item_manager.id_counter = 55555; | ||||
|                 entity_gateway.create_user(NewUserAccountEntity { | ||||
|                     username: self.name.clone(), | ||||
|                     ..NewUserAccountEntity::default() | ||||
|                 }) | ||||
|                     .await?; | ||||
|                 Ok(()) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         #[async_trait::async_trait] | ||||
|         impl<EG: EntityGateway> ItemAction<EG> for DummyAction2 { | ||||
|             async fn commit(&self, item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> { | ||||
|                 item_manager.id_counter += self.value; | ||||
|                 entity_gateway.create_character(NewCharacterEntity { | ||||
|                     slot: self.value, | ||||
|                     ..NewCharacterEntity::new(UserAccountId(0), 1) // TODO: handle different keyboard_config_presets
 | ||||
|                 }) | ||||
|                     .await?; | ||||
|                 Ok(()) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         let mut item_manager = ItemManager::default(); | ||||
|         let mut entity_gateway = DummyGateway::default(); | ||||
| 
 | ||||
|         let result = ItemTransaction::new(&item_manager, 12) | ||||
|             .act(|it, k| { | ||||
|                 it.action(Box::new(DummyAction1 {name: "asdf".into()})); | ||||
|                 it.action(Box::new(DummyAction2 {value: 11})); | ||||
|                 it.action(Box::new(DummyAction2 {value: *k})); | ||||
|                 if *k == 99 { | ||||
|                     return Err(DummyError::Error) | ||||
|                 } | ||||
|                 Ok(String::from("hello")) | ||||
|             }) | ||||
|             .commit(&mut item_manager, &mut entity_gateway) | ||||
|             .await; | ||||
| 
 | ||||
|         assert!(entity_gateway.d1_set == "asdf"); | ||||
|         assert!(entity_gateway.d2_inc == 23); | ||||
|         assert!(item_manager.id_counter == 55578); | ||||
|         assert!(result.unwrap() == "hello"); | ||||
|     } | ||||
| 
 | ||||
|     #[async_std::test] | ||||
|     async fn test_item_transaction_with_action_error() { | ||||
|         #[derive(Debug)] | ||||
|         struct DummyAction1 { | ||||
|         } | ||||
|         #[derive(Debug)] | ||||
|         struct DummyAction2 { | ||||
|         } | ||||
| 
 | ||||
|         #[derive(Error, Debug, PartialEq, Eq)] | ||||
|         #[error("")] | ||||
|         enum DummyError { | ||||
|             Error | ||||
|         } | ||||
| 
 | ||||
|         #[derive(Default, Clone)] | ||||
|         struct DummyGateway { | ||||
|             _d1_set: String, | ||||
|             d2_inc: u32, | ||||
|         } | ||||
| 
 | ||||
|         #[async_trait::async_trait] | ||||
|         impl EntityGateway for DummyGateway { | ||||
|             async fn create_character(&mut self, char: NewCharacterEntity) -> Result<CharacterEntity, GatewayError> { | ||||
|                 self.d2_inc += char.slot; | ||||
|                 Ok(CharacterEntity::default()) | ||||
|             } | ||||
|         } | ||||
|         
 | ||||
| 
 | ||||
|         #[async_trait::async_trait] | ||||
|         impl<EG: EntityGateway> ItemAction<EG> for DummyAction1 { | ||||
|             async fn commit(&self, _item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> { | ||||
|                 entity_gateway.create_character(NewCharacterEntity { | ||||
|                     slot: 1, | ||||
|                     ..NewCharacterEntity::new(UserAccountId(0), 1) // TODO: handle different keyboard_config_presets
 | ||||
|                 }) | ||||
|                     .await?; | ||||
|                 Ok(()) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         #[async_trait::async_trait] | ||||
|         impl<EG: EntityGateway> ItemAction<EG> for DummyAction2 { | ||||
|             async fn commit(&self, _item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> { | ||||
|                 entity_gateway.create_character(NewCharacterEntity { | ||||
|                     slot: 1, | ||||
|                     ..NewCharacterEntity::new(UserAccountId(0), 1) // TODO: handle different keyboard_config_presets
 | ||||
|                 }) | ||||
|                     .await?; | ||||
|                 Ok(()) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         let mut item_manager = ItemManager::default(); | ||||
|         let mut entity_gateway = DummyGateway::default(); | ||||
| 
 | ||||
|         let result = ItemTransaction::new(&item_manager, 12) | ||||
|             .act(|it, _| -> Result<(), _> { | ||||
|                 it.action(Box::new(DummyAction1 {})); | ||||
|                 it.action(Box::new(DummyAction2 {})); | ||||
|                 it.action(Box::new(DummyAction2 {})); | ||||
|                 Err(DummyError::Error) | ||||
|             }) | ||||
|             .commit(&mut item_manager, &mut entity_gateway) | ||||
|             .await; | ||||
| 
 | ||||
|         assert!(entity_gateway.d2_inc == 0); | ||||
|         assert!(matches!(result, Err(TransactionError::Action(DummyError::Error)))); | ||||
|     } | ||||
| 
 | ||||
|     #[async_std::test] | ||||
|     async fn test_item_transaction_with_commit_error() { | ||||
|         #[derive(Debug)] | ||||
|         struct DummyAction1 { | ||||
|         } | ||||
|         #[derive(Debug)] | ||||
|         struct DummyAction2 { | ||||
|         } | ||||
| 
 | ||||
|         #[derive(Error, Debug, PartialEq, Eq)] | ||||
|         #[error("")] | ||||
|         enum DummyError { | ||||
|         } | ||||
| 
 | ||||
|         #[derive(Default, Clone)] | ||||
|         struct DummyGateway { | ||||
|             _d1_set: String, | ||||
|             d2_inc: u32, | ||||
|         } | ||||
| 
 | ||||
|         #[async_trait::async_trait] | ||||
|         impl EntityGateway for DummyGateway { | ||||
|             async fn create_character(&mut self, char: NewCharacterEntity) -> Result<CharacterEntity, GatewayError> { | ||||
|                 self.d2_inc += char.slot; | ||||
|                 Ok(CharacterEntity::default()) | ||||
|             } | ||||
|         } | ||||
|         
 | ||||
| 
 | ||||
|         #[async_trait::async_trait] | ||||
|         impl<EG: EntityGateway> ItemAction<EG> for DummyAction1 { | ||||
|             async fn commit(&self, _item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> { | ||||
|                 entity_gateway.create_character(NewCharacterEntity { | ||||
|                     slot: 1, | ||||
|                     ..NewCharacterEntity::new(UserAccountId(0), 1) // TODO: handle different keyboard_config_presets
 | ||||
|                 }) | ||||
|                     .await?; | ||||
|                 Err(GatewayError::Error.into()) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         #[async_trait::async_trait] | ||||
|         impl<EG: EntityGateway> ItemAction<EG> for DummyAction2 { | ||||
|             async fn commit(&self, _item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> { | ||||
|                 entity_gateway.create_character(NewCharacterEntity { | ||||
|                     slot: 1, | ||||
|                     ..NewCharacterEntity::new(UserAccountId(0), 1) // TODO: handle different keyboard_config_presets
 | ||||
|                 }) | ||||
|                     .await?; | ||||
|                 Ok(()) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         let mut item_manager = ItemManager::default(); | ||||
|         let mut entity_gateway = DummyGateway::default(); | ||||
| 
 | ||||
|         let result = ItemTransaction::new(&item_manager, 12) | ||||
|             .act(|it, _| -> Result<_, DummyError> { | ||||
|                 it.action(Box::new(DummyAction1 {})); | ||||
|                 it.action(Box::new(DummyAction2 {})); | ||||
|                 it.action(Box::new(DummyAction2 {})); | ||||
|                 Ok(()) | ||||
|             }) | ||||
|             .commit(&mut item_manager, &mut entity_gateway) | ||||
|             .await; | ||||
| 
 | ||||
|         // in an ideal world this would be 0 as rollbacks would occur
 | ||||
|         assert!(entity_gateway.d2_inc == 1); | ||||
|         assert!(matches!(result, Err(TransactionError::Commit(TransactionCommitError::Gateway(GatewayError::Error))))); | ||||
|     } | ||||
| } | ||||
|         
 | ||||
| @ -1,163 +0,0 @@ | ||||
| use thiserror::Error; | ||||
| use crate::entity::gateway::EntityGateway; | ||||
| use crate::entity::character::CharacterEntity; | ||||
| use crate::entity::item::mag::MagCell; | ||||
| use crate::ship::items::{CharacterInventory, ConsumedItem}; | ||||
| 
 | ||||
| #[derive(Error, Debug)] | ||||
| #[error("")] | ||||
| pub enum UseItemError { | ||||
|     NoCharacter, | ||||
|     ItemNotEquipped, | ||||
|     InvalidItem, | ||||
| } | ||||
| 
 | ||||
| pub async fn power_material<EG: EntityGateway>(entity_gateway: &mut EG, character: &mut CharacterEntity) { | ||||
|     character.materials.power += 1; | ||||
|     entity_gateway.save_character(character).await.unwrap(); | ||||
| } | ||||
| 
 | ||||
| pub async fn mind_material<EG: EntityGateway>(entity_gateway: &mut EG, character: &mut CharacterEntity) { | ||||
|     character.materials.mind += 1; | ||||
|     entity_gateway.save_character(character).await.unwrap(); | ||||
| } | ||||
| 
 | ||||
| pub async fn evade_material<EG: EntityGateway>(entity_gateway: &mut EG, character: &mut CharacterEntity) { | ||||
|     character.materials.evade += 1; | ||||
|     entity_gateway.save_character(character).await.unwrap(); | ||||
| } | ||||
| 
 | ||||
| pub async fn def_material<EG: EntityGateway>(entity_gateway: &mut EG, character: &mut CharacterEntity) { | ||||
|     character.materials.def += 1; | ||||
|     entity_gateway.save_character(character).await.unwrap(); | ||||
| } | ||||
| 
 | ||||
| pub async fn luck_material<EG: EntityGateway>(entity_gateway: &mut EG, character: &mut CharacterEntity) { | ||||
|     character.materials.luck += 1; | ||||
|     entity_gateway.save_character(character).await.unwrap(); | ||||
| } | ||||
| 
 | ||||
| pub async fn hp_material<EG: EntityGateway>(entity_gateway: &mut EG, character: &mut CharacterEntity) { | ||||
|     character.materials.hp += 1; | ||||
|     entity_gateway.save_character(character).await.unwrap(); | ||||
| } | ||||
| 
 | ||||
| pub async fn tp_material<EG: EntityGateway>(entity_gateway: &mut EG, character: &mut CharacterEntity) { | ||||
|     character.materials.tp += 1; | ||||
|     entity_gateway.save_character(character).await.unwrap(); | ||||
| } | ||||
| 
 | ||||
| async fn mag_cell<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory, mag_cell_type: MagCell) -> Result<(), UseItemError> { | ||||
|     let mut mag_handle = inventory.get_equipped_mag_handle().ok_or(UseItemError::ItemNotEquipped)?; | ||||
|     let mag_item = mag_handle.item_mut() | ||||
|         .ok_or(UseItemError::InvalidItem)?; | ||||
|     let actual_mag = mag_item | ||||
|         .individual_mut() | ||||
|         .ok_or(UseItemError::InvalidItem)? | ||||
|         .mag_mut() | ||||
|         .ok_or(UseItemError::InvalidItem)?; | ||||
|     actual_mag.apply_mag_cell(mag_cell_type); | ||||
|     for mag_entity_id in mag_item.entity_ids() { | ||||
|         for cell_entity_id in used_cell.entity_ids() { | ||||
|             entity_gateway.use_mag_cell(&mag_entity_id, &cell_entity_id).await.unwrap(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| pub async fn cell_of_mag_502<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { | ||||
|     mag_cell(entity_gateway, used_cell, inventory, MagCell::CellOfMag502).await | ||||
| } | ||||
| 
 | ||||
| pub async fn cell_of_mag_213<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { | ||||
|     mag_cell(entity_gateway, used_cell, inventory, MagCell::CellOfMag213).await | ||||
| } | ||||
| 
 | ||||
| pub async fn parts_of_robochao<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { | ||||
|     mag_cell(entity_gateway, used_cell, inventory, MagCell::PartsOfRobochao).await | ||||
| } | ||||
| 
 | ||||
| pub async fn heart_of_opaopa<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { | ||||
|     mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfOpaOpa).await | ||||
| } | ||||
| 
 | ||||
| pub async fn heart_of_pian<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { | ||||
|     mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfPian).await | ||||
| } | ||||
| 
 | ||||
| pub async fn heart_of_chao<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { | ||||
|     mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfChao).await | ||||
| } | ||||
| 
 | ||||
| pub async fn heart_of_angel<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { | ||||
|     mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfAngel).await | ||||
| } | ||||
| 
 | ||||
| pub async fn kit_of_hamburger<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { | ||||
|     mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfHamburger).await | ||||
| } | ||||
| 
 | ||||
| pub async fn panthers_spirit<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { | ||||
|     mag_cell(entity_gateway, used_cell, inventory, MagCell::PanthersSpirit).await | ||||
| } | ||||
| 
 | ||||
| pub async fn kit_of_mark3<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { | ||||
|     mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfMark3).await | ||||
| } | ||||
| 
 | ||||
| pub async fn kit_of_master_system<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { | ||||
|     mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfMasterSystem).await | ||||
| } | ||||
| 
 | ||||
| pub async fn kit_of_genesis<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { | ||||
|     mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfGenesis).await | ||||
| } | ||||
| 
 | ||||
| pub async fn kit_of_sega_saturn<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { | ||||
|     mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfSegaSaturn).await | ||||
| } | ||||
| 
 | ||||
| pub async fn kit_of_dreamcast<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { | ||||
|     mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfDreamcast).await | ||||
| } | ||||
| 
 | ||||
| pub async fn tablet<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { | ||||
|     mag_cell(entity_gateway, used_cell, inventory, MagCell::Tablet).await | ||||
| } | ||||
| 
 | ||||
| pub async fn dragon_scale<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { | ||||
|     mag_cell(entity_gateway, used_cell, inventory, MagCell::DragonScale).await | ||||
| } | ||||
| 
 | ||||
| pub async fn heaven_striker_coat<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { | ||||
|     mag_cell(entity_gateway, used_cell, inventory, MagCell::HeavenStrikerCoat).await | ||||
| } | ||||
| 
 | ||||
| pub async fn pioneer_parts<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { | ||||
|     mag_cell(entity_gateway, used_cell, inventory, MagCell::PioneerParts).await | ||||
| } | ||||
| 
 | ||||
| pub async fn amities_memo<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { | ||||
|     mag_cell(entity_gateway, used_cell, inventory, MagCell::AmitiesMemo).await | ||||
| } | ||||
| 
 | ||||
| pub async fn heart_of_morolian<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { | ||||
|     mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfMorolian).await | ||||
| } | ||||
| 
 | ||||
| pub async fn rappys_beak<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { | ||||
|     mag_cell(entity_gateway, used_cell, inventory, MagCell::RappysBeak).await | ||||
| } | ||||
| 
 | ||||
| pub async fn yahoos_engine<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { | ||||
|     mag_cell(entity_gateway, used_cell, inventory, MagCell::YahoosEngine).await | ||||
| } | ||||
| 
 | ||||
| pub async fn d_photon_core<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { | ||||
|     mag_cell(entity_gateway, used_cell, inventory, MagCell::DPhotonCore).await | ||||
| } | ||||
| 
 | ||||
| pub async fn liberta_kit<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { | ||||
|     mag_cell(entity_gateway, used_cell, inventory, MagCell::LibertaKit).await | ||||
| } | ||||
| @ -5,7 +5,6 @@ use crate::ship::ship::{ShipError, Clients}; | ||||
| use crate::ship::location::{ClientLocation, LobbyId, ClientLocationError}; | ||||
| use crate::ship::packet::builder::{player_info}; | ||||
| use crate::ship::items::state::ItemState; | ||||
| use crate::ship::items::ItemManager; | ||||
| 
 | ||||
| 
 | ||||
| pub fn join_lobby(id: ClientId, | ||||
|  | ||||
| @ -3,16 +3,14 @@ use libpso::packet::ship::*; | ||||
| use crate::entity::item; | ||||
| use crate::common::leveltable::CharacterStats; | ||||
| use crate::ship::ship::{ShipError}; | ||||
| use crate::ship::items::{ClientItemId, InventoryItem, StackedFloorItem, FloorItem, CharacterBank}; | ||||
| use crate::ship::items::state::FloorItem as FloorItem2; | ||||
| use crate::ship::items::state::InventoryItem as InventoryItem2; | ||||
| use crate::ship::items::state::{BankState, IndividualItemDetail}; | ||||
| use crate::ship::items::ClientItemId; | ||||
| use crate::ship::items::state::{InventoryItem, FloorItem, BankState, IndividualItemDetail}; | ||||
| use crate::ship::location::AreaClient; | ||||
| use std::convert::TryInto; | ||||
| use crate::ship::shops::ShopItem; | ||||
| 
 | ||||
| 
 | ||||
| pub fn item_drop(client: u8, target: u8, item_drop: &FloorItem2) -> Result<ItemDrop, ShipError> { | ||||
| pub fn item_drop(client: u8, target: u8, item_drop: &FloorItem) -> Result<ItemDrop, ShipError> { | ||||
|     let item_bytes = item_drop.as_client_bytes(); | ||||
|     Ok(ItemDrop { | ||||
|         client, | ||||
| @ -68,7 +66,7 @@ pub fn create_meseta(area_client: AreaClient, amount: usize) -> CreateItem { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub fn create_withdrawn_inventory_item(area_client: AreaClient, item: &InventoryItem2) -> Result<CreateItem, ShipError> { | ||||
| pub fn create_withdrawn_inventory_item(area_client: AreaClient, item: &InventoryItem) -> Result<CreateItem, ShipError> { | ||||
|     let bytes = item.item.as_client_bytes(); | ||||
|     Ok(CreateItem { | ||||
|         client: area_client.local_client.id(), | ||||
| @ -80,7 +78,7 @@ pub fn create_withdrawn_inventory_item(area_client: AreaClient, item: &Inventory | ||||
|     }) | ||||
| } | ||||
| 
 | ||||
| pub fn create_withdrawn_inventory_item2(area_client: AreaClient, item: &InventoryItem2) -> Result<CreateItem, ShipError> { | ||||
| pub fn create_withdrawn_inventory_item2(area_client: AreaClient, item: &InventoryItem) -> Result<CreateItem, ShipError> { | ||||
|     let bytes = item.item.as_client_bytes(); | ||||
|     Ok(CreateItem { | ||||
|         client: area_client.local_client.id(), | ||||
| @ -92,7 +90,7 @@ pub fn create_withdrawn_inventory_item2(area_client: AreaClient, item: &Inventor | ||||
|     }) | ||||
| } | ||||
| 
 | ||||
| pub fn remove_item_from_floor(area_client: AreaClient, item: &FloorItem2) -> Result<RemoveItemFromFloor, ShipError> { | ||||
| pub fn remove_item_from_floor(area_client: AreaClient, item: &FloorItem) -> Result<RemoveItemFromFloor, ShipError> { | ||||
|     Ok(RemoveItemFromFloor { | ||||
|         client: area_client.local_client.id(), | ||||
|         target: 0, | ||||
| @ -104,7 +102,7 @@ pub fn remove_item_from_floor(area_client: AreaClient, item: &FloorItem2) -> Res | ||||
|     }) | ||||
| } | ||||
| 
 | ||||
| pub fn drop_split_stack(area_client: AreaClient, item: &FloorItem2) -> Result<DropSplitStack, ShipError> { | ||||
| pub fn drop_split_stack(area_client: AreaClient, item: &FloorItem) -> Result<DropSplitStack, ShipError> { | ||||
|     let item_bytes = item.as_client_bytes(); | ||||
|     Ok(DropSplitStack { | ||||
|         client: area_client.local_client.id(), | ||||
| @ -121,7 +119,7 @@ pub fn drop_split_stack(area_client: AreaClient, item: &FloorItem2) -> Result<Dr | ||||
|     }) | ||||
| } | ||||
| 
 | ||||
| pub fn drop_split_meseta_stack(area_client: AreaClient, item: &FloorItem2) -> Result<DropSplitStack, ShipError> { | ||||
| pub fn drop_split_meseta_stack(area_client: AreaClient, item: &FloorItem) -> Result<DropSplitStack, ShipError> { | ||||
|     let item_bytes = item.as_client_bytes(); | ||||
|     Ok(DropSplitStack { | ||||
|         client: area_client.local_client.id(), | ||||
|  | ||||
| @ -5,7 +5,6 @@ use crate::ship::ship::{ShipError, ClientState, Clients}; | ||||
| use crate::ship::location::{ClientLocation, RoomId, AreaClient, ClientLocationError}; | ||||
| use crate::ship::room::RoomState; | ||||
| use crate::ship::items::state::ItemState; | ||||
| use crate::ship::items::ItemManager; | ||||
| use crate::ship::packet::builder::{player_header, player_info}; | ||||
| use std::convert::TryInto; | ||||
| 
 | ||||
|  | ||||
| @ -8,13 +8,13 @@ use crate::common::serverstate::ClientId; | ||||
| use crate::ship::ship::{SendShipPacket, ShipError, Clients, Rooms, ItemShops}; | ||||
| use crate::ship::location::{ClientLocation, ClientLocationError}; | ||||
| use crate::ship::drops::ItemDrop; | ||||
| use crate::ship::items::{ItemManager, ItemManagerError, ClientItemId, FloorItem}; | ||||
| use crate::ship::items::ClientItemId; | ||||
| use crate::entity::gateway::EntityGateway; | ||||
| use crate::entity::item; | ||||
| use libpso::utf8_to_utf16_array; | ||||
| use crate::ship::packet::builder; | ||||
| use crate::ship::shops::{ShopItem, ToolShopItem, ArmorShopItem}; | ||||
| use crate::ship::items::state::{ItemState, FloorType, FloorItemDetail}; | ||||
| use crate::ship::items::state::{ItemState, ItemStateError, FloorType, FloorItemDetail}; | ||||
| use crate::ship::items::actions::{pick_up_item, withdraw_meseta, deposit_meseta, withdraw_item, deposit_item, buy_shop_item, enemy_drops_item, take_meseta, apply_modifier, TriggerCreateItem}; | ||||
| 
 | ||||
| const BANK_ACTION_DEPOSIT: u8 = 0; | ||||
| @ -431,11 +431,11 @@ where | ||||
| 
 | ||||
|     let inventory = item_state.get_character_inventory(&client.character)?; | ||||
|     let item = inventory.get_by_client_id(&ClientItemId(tek_request.item_id)) | ||||
|         .ok_or(ItemManagerError::WrongItemType(ClientItemId(tek_request.item_id)))?; | ||||
|         .ok_or(ItemStateError::WrongItemType(ClientItemId(tek_request.item_id)))?; | ||||
|     let mut weapon = item.item.as_individual() | ||||
|         .ok_or(ItemManagerError::WrongItemType(ClientItemId(tek_request.item_id)))? | ||||
|         .ok_or(ItemStateError::WrongItemType(ClientItemId(tek_request.item_id)))? | ||||
|         .as_weapon() | ||||
|         .ok_or(ItemManagerError::WrongItemType(ClientItemId(tek_request.item_id)))? | ||||
|         .ok_or(ItemStateError::WrongItemType(ClientItemId(tek_request.item_id)))? | ||||
|         .clone(); | ||||
| 
 | ||||
|     weapon.apply_modifier(&item::weapon::WeaponModifier::Tekked { | ||||
|  | ||||
| @ -61,7 +61,6 @@ pub enum ShipError { | ||||
|     ItemError, // TODO: refine this
 | ||||
|     PickUpInvalidItemId(u32), | ||||
|     DropInvalidItemId(u32), | ||||
|     ItemManagerError(#[from] items::ItemManagerError), | ||||
|     ItemStateError(#[from] items::state::ItemStateError), | ||||
|     #[error("")] | ||||
|     ItemDropLocationNotSet, | ||||
| @ -406,7 +405,6 @@ impl<EG: EntityGateway> ShipServerStateBuilder<EG> { | ||||
|             clients: HashMap::new(), | ||||
|             level_table: CharacterLevelTable::default(), | ||||
|             name: self.name.unwrap_or_else(|| "NAMENOTSET".into()), | ||||
|             item_manager: items::ItemManager::default(), | ||||
|             item_state: items::state::ItemState::default(), | ||||
|             ip: self.ip.unwrap_or_else(|| Ipv4Addr::new(127,0,0,1)), | ||||
|             port: self.port.unwrap_or(SHIP_PORT), | ||||
| @ -451,7 +449,6 @@ pub struct ShipServerState<EG: EntityGateway> { | ||||
|     pub clients: Clients, | ||||
|     level_table: CharacterLevelTable, | ||||
|     name: String, | ||||
|     item_manager: items::ItemManager, | ||||
|     item_state: items::state::ItemState, | ||||
|     shops: Box<ItemShops>, | ||||
|     pub blocks: Blocks, | ||||
|  | ||||
| @ -3,7 +3,6 @@ use elseware::entity::gateway::{EntityGateway, InMemoryGateway}; | ||||
| use elseware::entity::item; | ||||
| use elseware::ship::ship::{ShipServerState, RecvShipPacket, SendShipPacket}; | ||||
| use elseware::ship::room::Difficulty; | ||||
| use elseware::ship::items::manager::ItemManagerError; | ||||
| use elseware::ship::items::state::ItemStateError; | ||||
| 
 | ||||
| use libpso::packet::ship::*; | ||||
|  | ||||
| @ -4,7 +4,6 @@ use elseware::entity::gateway::{EntityGateway, InMemoryGateway}; | ||||
| use elseware::entity::item; | ||||
| use elseware::ship::ship::{ShipServerState, RecvShipPacket, SendShipPacket, ShipError}; | ||||
| use elseware::entity::item::{Meseta, ItemEntity}; | ||||
| use elseware::ship::items::transaction::TransactionError; | ||||
| use elseware::ship::packet::handler::trade::TradeError; | ||||
| use elseware::ship::items::state::{ItemStateError, InventoryError}; | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user