use std::cmp::Ordering; use std::collections::HashMap; use libpso::character::character; use crate::ship::items::ClientItemId; use crate::entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, InventoryEntity, InventoryItemEntity, BankItemEntity, BankName, EquippedEntity}; use std::future::Future; use crate::ship::map::MapArea; use crate::ship::location::{AreaClient, RoomId}; use crate::entity::character::{CharacterEntity, CharacterEntityId}; use crate::entity::gateway::{EntityGateway, GatewayError}; use crate::entity::item::tool::Tool; use crate::entity::item::mag::Mag; use crate::ship::drops::ItemDrop; #[derive(thiserror::Error, Debug)] pub enum ItemStateError { #[error("character {0} not found")] NoCharacter(CharacterEntityId), #[error("room {0} not found")] NoRoom(RoomId), #[error("floor item {0} not found")] NoFloorItem(ClientItemId), #[error("inventory error {0}")] InventoryError(#[from] InventoryError), #[error("invalid drop? {0:?} (this shouldn't occur)")] BadItemDrop(ItemDrop), #[error("idk")] Dummy, #[error("gateway")] GatewayError(#[from] GatewayError), #[error("tried to drop more meseta than in inventory: {0}")] InvalidMesetaDrop(u32), #[error("stacked item")] StackedItemError(Vec), } pub enum FloorType { Local, Shared, } #[async_trait::async_trait] pub trait ItemAction { type Input; type Output; type Start; type Error; async fn action(&self, s: Self::Start, i: Self::Input) -> Result<(Self::Start, Self::Output), Self::Error>; async fn commit(&self, v: Self::Start) -> Result<(Self::Start, Self::Output), Self::Error>; } pub struct ItemStateAction { _t: std::marker::PhantomData, _s: std::marker::PhantomData, _e: std::marker::PhantomData, } impl Default for ItemStateAction { fn default() -> ItemStateAction { ItemStateAction { _t: std::marker::PhantomData, _s: std::marker::PhantomData, _e: std::marker::PhantomData, } } } impl ItemStateAction where T: Send + Sync, S: Send + Sync, E: Send + Sync, { pub fn act(self, f: F) -> ItemActionStage, F, Fut, S, E> where F: Fn(S, ()) -> Fut + Send + Sync, Fut: Future> + Send { ItemActionStage { _s: Default::default(), _e: std::marker::PhantomData, prev: self, actionf: f, } } } pub struct ItemActionStage where P: ItemAction, F: Fn(S, P::Output) -> Fut + Send + Sync, Fut: Future> + Send, { _s: std::marker::PhantomData, _e: std::marker::PhantomData, prev: P, actionf: F, } #[async_trait::async_trait] impl ItemAction for ItemActionStage where P: ItemAction + ItemAction + Send + Sync, F: Fn(S, P::Output) -> Fut + Send + Sync, Fut: Future> + Send, S: Send + Sync, P::Output: Send + Sync, E: Send + Sync, O: Send + Sync, P::Error: Send + Sync, { type Input = P::Output; type Output = O; type Start = S; type Error = P::Error; async fn action(&self, s: Self::Start, i: Self::Input) -> Result<(Self::Start, Self::Output), Self::Error> { (self.actionf)(s, i).await } async fn commit(&self, i: Self::Start) -> Result<(Self::Start, Self::Output), Self::Error> { let (i, prev) = self.prev.commit(i).await?; self.action(i, prev).await } } impl ItemActionStage where P: ItemAction + Send + Sync, F: Fn(S, P::Output) -> Fut + Send + Sync, Fut: Future> + Send, S: Send + Sync, P::Output: Send + Sync, E: Send + Sync, O: Send + Sync, P::Error: Send + Sync, { #[allow(clippy::type_complexity)] pub fn act(self, g: G) -> ItemActionStage, G, GFut, S, E> where S: Send + Sync, G: Fn(S, as ItemAction>::Output) -> GFut + Send + Sync, GFut: Future> + Send, O2: Send + Sync, { ItemActionStage { _s: Default::default(), _e: Default::default(), prev: self, actionf: g, } } } #[async_trait::async_trait] impl ItemAction for ItemStateAction where T: Send + Sync, S: Send + Sync, E: Send + Sync, { type Input = T; type Output = (); type Start = T; type Error = E; async fn action(&self, s: Self::Start, _i: Self::Input) -> Result<(Self::Start, Self::Output), Self::Error> { Ok((s, ())) } async fn commit(&self, i: Self::Start) -> Result<(Self::Start, Self::Output), Self::Error> { Ok((i, ())) } } #[derive(Clone, Debug)] pub struct IndividualItemDetail { pub entity_id: ItemEntityId, pub item: ItemDetail, } #[derive(Clone, Debug)] pub struct StackedItemDetail { pub entity_ids: Vec, pub tool: Tool, } impl StackedItemDetail { pub fn count(&self) -> usize { self.entity_ids.len() } } #[derive(Clone, Debug)] pub enum InventoryItemDetail { Individual(IndividualItemDetail), Stacked(StackedItemDetail), } impl InventoryItemDetail { fn stacked(&self) -> Option<&StackedItemDetail> { match self { InventoryItemDetail::Stacked(sitem) => Some(sitem), _ => None, } } fn stacked_mut(&mut self) -> Option<&mut StackedItemDetail> { match self { InventoryItemDetail::Stacked(sitem) => Some(sitem), _ => None, } } pub fn as_client_bytes(&self) -> [u8; 16] { match self { InventoryItemDetail::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(), } }, InventoryItemDetail::Stacked(item) => { item.tool.as_stacked_bytes(item.entity_ids.len()) }, } } } #[derive(Clone, Debug)] pub struct InventoryItem { item_id: ClientItemId, item: InventoryItemDetail, } impl InventoryItem { pub async fn with_entity_id(&self, mut param: T, mut func: F) -> T where F: FnMut(T, ItemEntityId) -> Fut, Fut: Future, { match &self.item { InventoryItemDetail::Individual(individual_item) => { param = func(param, individual_item.entity_id).await; }, InventoryItemDetail::Stacked(stacked_item) => { for entity_id in &stacked_item.entity_ids { param = func(param, *entity_id).await; } } } param } } #[derive(Clone, Debug)] pub enum BankItemDetail { Individual(IndividualItemDetail), Stacked(StackedItemDetail), } impl BankItemDetail { pub fn as_client_bytes(&self) -> [u8; 16] { match self { BankItemDetail::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(), } }, BankItemDetail::Stacked(item) => { item.tool.as_stacked_bytes(item.entity_ids.len()) }, } } } #[derive(Clone, Debug)] pub struct BankItem { item_id: ClientItemId, item: BankItemDetail, } #[derive(Clone)] pub enum FloorItemDetail { Individual(IndividualItemDetail), Stacked(StackedItemDetail), Meseta(Meseta), } impl FloorItemDetail { fn stacked(&self) -> Option<&StackedItemDetail> { match self { FloorItemDetail::Stacked(sitem) => Some(sitem), _ => None, } } } #[derive(Clone)] pub struct FloorItem { pub item_id: ClientItemId, pub item: FloorItemDetail, pub map_area: MapArea, pub x: f32, pub y: f32, pub z: f32, } impl FloorItem { pub async fn with_entity_id(&self, mut param: T, mut func: F) -> T where F: FnMut(T, ItemEntityId) -> Fut, Fut: Future, { match &self.item { FloorItemDetail::Individual(individual_item) => { param = func(param, individual_item.entity_id).await; }, FloorItemDetail::Stacked(stacked_item) => { for entity_id in &stacked_item.entity_ids { param = func(param, *entity_id).await; } }, FloorItemDetail::Meseta(_meseta) => {}, } param } pub async fn with_mag(&self, mut param: T, mut func: F) -> T where F: FnMut(T, ItemEntityId, Mag) -> Fut, Fut: Future, { if let FloorItemDetail::Individual(individual_item) = &self.item { if let ItemDetail::Mag(mag) = &individual_item.item { param = func(param, individual_item.entity_id, mag.clone()).await; } } param } pub fn as_client_bytes(&self) -> [u8; 16] { match &self.item { FloorItemDetail::Individual(individual_floor_item) => { individual_floor_item.item.as_client_bytes() }, FloorItemDetail::Stacked(stacked_floor_item) => { stacked_floor_item.tool.as_stacked_bytes(stacked_floor_item.entity_ids.len()) }, FloorItemDetail::Meseta(meseta_floor_item) => { meseta_floor_item.as_bytes() } } } } #[derive(Clone, Debug)] pub struct Inventory(Vec); #[derive(thiserror::Error, Debug)] pub enum InventoryError { #[error("inventory full")] InventoryFull, #[error("stack full")] StackFull, #[error("meseta full")] MesetaFull, } #[derive(Clone)] pub enum AddItemResult { NewItem, AddToStack, Meseta, } #[derive(Clone, Default)] pub struct LocalFloor(Vec); #[derive(Clone, Default)] pub struct SharedFloor(Vec); #[derive(Clone)] pub struct RoomFloorItems(Vec); #[derive(Clone)] pub struct InventoryState { character_id: CharacterEntityId, item_id_counter: u32, pub inventory: Inventory, equipped: EquippedEntity, pub meseta: Meseta, } impl InventoryState { pub fn initialize_item_ids(&mut self, base_item_id: u32) { for (i, item) in self.inventory.0.iter_mut().enumerate() { item.item_id = ClientItemId(base_item_id + i as u32); } self.item_id_counter = base_item_id + self.inventory.0.len() as u32 + 1; } pub fn count(&self) -> usize { self.inventory.0.len() } pub fn add_floor_item(&mut self, item: FloorItem) -> Result { match item.item { FloorItemDetail::Individual(iitem) => { if self.inventory.0.len() >= 30 { Err(InventoryError::InventoryFull) } else { self.inventory.0.push(InventoryItem { item_id: item.item_id, item: InventoryItemDetail::Individual(iitem) }); Ok(AddItemResult::NewItem) } }, FloorItemDetail::Stacked(sitem) => { let existing_stack = self.inventory.0 .iter_mut() .filter_map(|item| item.item.stacked_mut()) .find(|item| { item.tool == sitem.tool }); match existing_stack { Some(existing_stack) => { if existing_stack.entity_ids.len() + sitem.entity_ids.len() > sitem.tool.max_stack() { Err(InventoryError::StackFull) } else { existing_stack.entity_ids.append(&mut sitem.entity_ids.clone()); Ok(AddItemResult::AddToStack) } }, None => { if self.inventory.0.len() >= 30 { Err(InventoryError::InventoryFull) } else { self.inventory.0.push(InventoryItem { item_id: item.item_id, item: InventoryItemDetail::Stacked(sitem) }); Ok(AddItemResult::NewItem) } } } }, FloorItemDetail::Meseta(meseta) => { if self.meseta == Meseta(999999) { Err(InventoryError::MesetaFull) } else { self.meseta.0 = std::cmp::min(self.meseta.0 + meseta.0 ,999999); Ok(AddItemResult::Meseta) } }, } } pub fn take_item(&mut self, item_id: &ClientItemId) -> Option { self.inventory.0 .drain_filter(|i| i.item_id == *item_id) .next() } pub fn take_partial_item(&mut self, item_id: &ClientItemId, amount: u32) -> Option { let amount = amount as usize; let (idx, _, stacked_item) = self.inventory.0 .iter_mut() .enumerate() .filter_map(|(k, item)| { match item.item { InventoryItemDetail::Stacked(ref mut stacked_item) => Some((k, item.item_id, stacked_item)), _ => None } }) .find(|(_, id, _)| *id == *item_id)?; let remove_all = match stacked_item.entity_ids.len().cmp(&amount) { Ordering::Equal => true, Ordering::Greater => false, Ordering::Less => return None, }; if remove_all { let stacked_item = stacked_item.clone(); self.inventory.0.remove(idx); Some(stacked_item) } else { let entity_ids = stacked_item.entity_ids.drain(..amount).collect(); Some(StackedItemDetail { entity_ids: entity_ids, tool: stacked_item.tool, }) } } pub fn as_inventory_entity(&self, _character_id: &CharacterEntityId) -> InventoryEntity { InventoryEntity { items: self.inventory.0.iter() .map(|item| { match &item.item { InventoryItemDetail::Individual(item) => { InventoryItemEntity::Individual(ItemEntity { id: item.entity_id, item: item.item.clone(), }) }, InventoryItemDetail::Stacked(items) => { InventoryItemEntity::Stacked(items.entity_ids.iter() .map(|id| { ItemEntity { id: *id, item: ItemDetail::Tool(items.tool) } }) .collect()) }, } }) .collect() } } pub fn remove_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> { if amount > self.meseta.0 { return Err(ItemStateError::InvalidMesetaDrop(amount)) } self.meseta.0 -= amount; Ok(()) } pub fn as_client_inventory_items(&self) -> [character::InventoryItem; 30] { self.inventory.0.iter() .enumerate() .fold([character::InventoryItem::default(); 30], |mut inventory, (slot, item)| { let bytes = item.item.as_client_bytes(); inventory[slot].data1.copy_from_slice(&bytes[0..12]); inventory[slot].data2.copy_from_slice(&bytes[12..16]); inventory[slot].item_id = item.item_id.0; inventory[slot].equipped = 0; inventory[slot].flags = 0; if let InventoryItemDetail::Individual(individual_item) = &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 }) } } #[derive(Clone, Debug)] pub struct Bank(Vec); #[derive(Clone, Debug)] pub struct BankState { character_id: CharacterEntityId, item_id_counter: u32, bank: Bank, pub meseta: Meseta, } impl BankState { pub fn initialize_item_ids(&mut self, base_item_id: u32) { for (i, item) in self.bank.0.iter_mut().enumerate() { item.item_id = ClientItemId(base_item_id + i as u32); } self.item_id_counter = base_item_id + self.bank.0.len() as u32 + 1; } pub fn as_client_bank_items(&self) -> character::Bank { self.bank.0.iter() .enumerate() .fold(character::Bank::default(), |mut bank, (slot, item)| { bank.item_count = (slot + 1) as u32; let bytes = item.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 struct FloorState { character_id: CharacterEntityId, local: LocalFloor, shared: SharedFloor, } impl FloorState { pub fn take_item(&mut self, item_id: &ClientItemId) -> Option { let item = self.local.0 .drain_filter(|item| { item.item_id == *item_id }) .next(); item.or_else(|| { self.shared.0 .drain_filter(|item| { item.item_id == *item_id }) .next() }) } pub fn add_inventory_item(&mut self, inventory_item: InventoryItem, map_area: MapArea, position: (f32, f32, f32)) -> &FloorItem { let floor_item = FloorItem { item_id: inventory_item.item_id, item: match inventory_item.item { InventoryItemDetail::Individual(individual_item) => FloorItemDetail::Individual(individual_item), InventoryItemDetail::Stacked(stacked_item) => FloorItemDetail::Stacked(stacked_item), }, map_area: map_area, x: position.0, y: position.1, z: position.2, }; self.shared.0.push(floor_item); &self.shared.0[self.shared.0.len()-1] } pub fn add_item(&mut self, floor_item: FloorItem) -> &FloorItem { self.shared.0.push(floor_item); &self.shared.0[self.shared.0.len()-1] } } pub struct ItemState { character_inventory: HashMap, character_bank: HashMap, //character_meseta: HashMap, //bank_meseta: HashMap, character_room: HashMap, character_floor: HashMap, room_floor: HashMap, //room_item_id_counter: Arc ClientItemId + Send + Sync>>>>, room_item_id_counter: u32, } impl Default for ItemState { fn default() -> ItemState { ItemState { character_inventory: HashMap::new(), character_bank: HashMap::new(), //character_meseta: HashMap::new(), //bank_meseta: HashMap::new(), character_room: HashMap::new(), character_floor: HashMap::new(), room_floor: HashMap::new(), room_item_id_counter: 0x00810000, } } } impl ItemState { pub fn get_character_inventory(&self, character: &CharacterEntity) -> Result<&InventoryState, ItemStateError> { Ok(self.character_inventory.get(&character.id) .ok_or(ItemStateError::NoCharacter(character.id))?) } pub fn get_character_bank(&self, character: &CharacterEntity) -> Result<&BankState, ItemStateError> { Ok(self.character_bank.get(&character.id) .ok_or(ItemStateError::NoCharacter(character.id))?) } } impl ItemState { fn new_item_id(&mut self) -> Result { self.room_item_id_counter += 1; Ok(ClientItemId(self.room_item_id_counter)) } pub async fn load_character(&mut self, entity_gateway: &mut EG, character: &CharacterEntity) -> Result<(), ItemStateError> { let inventory = entity_gateway.get_character_inventory(&character.id).await?; let bank = entity_gateway.get_character_bank(&character.id, BankName("".into())).await?; let equipped = entity_gateway.get_character_equips(&character.id).await?; let inventory_items = inventory.items.into_iter() .map(|item| -> Result { Ok(match item { InventoryItemEntity::Individual(item) => { InventoryItem { item_id: self.new_item_id()?, item: InventoryItemDetail::Individual(IndividualItemDetail { entity_id: item.id, item: item.item, }), } }, InventoryItemEntity::Stacked(items) => { InventoryItem { item_id: self.new_item_id()?, item: InventoryItemDetail::Stacked(StackedItemDetail { entity_ids: items.iter().map(|i| i.id).collect(), tool: items.get(0) .ok_or_else(|| ItemStateError::StackedItemError(items.clone()))? .item .clone() .as_tool() .ok_or_else(|| ItemStateError::StackedItemError(items.clone()))? }) } }, }) }) .collect::, _>>()?; let character_meseta = entity_gateway.get_character_meseta(&character.id).await?; let inventory_state = InventoryState { character_id: character.id, item_id_counter: 0, inventory: Inventory(inventory_items), equipped: equipped, meseta: character_meseta, }; let bank_items = bank.items.into_iter() .map(|item| -> Result { Ok(match item { BankItemEntity::Individual(item) => { BankItem { item_id: self.new_item_id()?, item: BankItemDetail::Individual(IndividualItemDetail { entity_id: item.id, item: item.item, }) } }, BankItemEntity::Stacked(items) => { BankItem { item_id: self.new_item_id()?, item: BankItemDetail::Stacked(StackedItemDetail { entity_ids: items.iter().map(|i| i.id).collect(), tool: items.get(0) .ok_or_else(|| ItemStateError::StackedItemError(items.clone()))? .item .clone() .as_tool() .ok_or_else(|| ItemStateError::StackedItemError(items.clone()))? }) } }, }) }) .collect::, _>>()?; let bank_meseta = entity_gateway.get_bank_meseta(&character.id, BankName("".into())).await?; let bank_state = BankState { character_id: character.id, item_id_counter: 0, bank: Bank(bank_items), meseta: bank_meseta, }; self.character_inventory.insert(character.id, inventory_state); self.character_bank.insert(character.id, bank_state); Ok(()) } pub fn add_character_to_room(&mut self, room_id: RoomId, character: &CharacterEntity, area_client: AreaClient) { let base_inventory_id = ((area_client.local_client.id() as u32) << 21) | 0x10000; let inventory = self.character_inventory.get_mut(&character.id).unwrap(); inventory.initialize_item_ids(base_inventory_id); let base_bank_id = ((area_client.local_client.id() as u32) << 21) | 0x20000; let default_bank = self.character_bank.get_mut(&character.id); if let Some(default_bank ) = default_bank { default_bank.initialize_item_ids(base_bank_id); } self.character_room.insert(character.id, room_id); self.character_floor.insert(character.id, LocalFloor::default()); self.room_floor.entry(room_id).or_insert_with(SharedFloor::default); /* let mut inc = 0x00810000; self.room_item_id_counter.borrow_mut().entry(room_id).or_insert_with(|| Box::new(move || { inc += 1; ClientItemId(inc) })); */ } pub fn remove_character_from_room(&mut self, character: &CharacterEntity) { self.character_inventory.remove(&character.id); self.character_floor.remove(&character.id); if let Some(room) = self.character_room.remove(&character.id).as_ref() { if self.character_room.iter().any(|(_, r)| r == room) { self.room_floor.remove(room); } } } pub fn get_floor_item(&self, character_id: &CharacterEntityId, item_id: &ClientItemId) -> Result<(&FloorItem, FloorType), ItemStateError> { let local_floor = self.character_floor.get(character_id).ok_or(ItemStateError::NoCharacter(*character_id))?; let room = self.character_room.get(character_id).ok_or(ItemStateError::NoCharacter(*character_id))?; let shared_floor = self.room_floor.get(room).ok_or(ItemStateError::NoCharacter(*character_id))?; local_floor.0 .iter() .find(|item| item.item_id == *item_id) .map(|item| (item, FloorType::Local)) .or_else(|| { shared_floor.0 .iter() .find(|item| item.item_id == *item_id) .map(|item| (item, FloorType::Shared)) }) .ok_or_else(|| ItemStateError::NoFloorItem(*item_id)) } } #[derive(Default)] struct ProxiedItemState { character_inventory: HashMap, character_bank: HashMap, //character_meseta: HashMap, //bank_meseta: HashMap, character_room: HashMap, character_floor: HashMap, room_floor: HashMap, //room_item_id_counter: HashMap ClientItemId + Send>>, } /* impl Default for ProxiedItemState { fn default() -> Self { ProxiedItemState { character_inventory: HashMap::new(), //character_bank: HashMap::new(), character_meseta: HashMap::new(), //bank_meseta: HashMap::new(), character_floor: HashMap::new(), character_room: HashMap::new(), room_floor: HashMap::new(), //room_item_id_counter: HashMap::new(), } } } */ pub struct ItemStateProxy<'a> { item_state: &'a mut ItemState, proxied_state: ProxiedItemState, } impl<'a> ItemStateProxy<'a> { pub fn commit(self) { self.item_state.character_inventory.extend(self.proxied_state.character_inventory.clone()); self.item_state.character_bank.extend(self.proxied_state.character_bank.clone()); //self.item_state.character_meseta.extend(self.proxied_state.character_meseta.clone()); self.item_state.character_room.extend(self.proxied_state.character_room.clone()); self.item_state.character_floor.extend(self.proxied_state.character_floor.clone()); self.item_state.room_floor.extend(self.proxied_state.room_floor.clone()); } } fn get_or_clone(master: &HashMap, proxy: &mut HashMap, key: K, err: fn(K) -> ItemStateError) -> Result where K: Eq + std::hash::Hash + Copy, V: Clone { let existing_element = master.get(&key).ok_or_else(|| err(key))?; Ok(proxy.entry(key) .or_insert_with(|| existing_element.clone()).clone()) } impl<'a> ItemStateProxy<'a> { pub fn new(item_state: &'a mut ItemState) -> Self { ItemStateProxy { item_state, proxied_state: Default::default(), } } pub fn inventory(&mut self, character_id: &CharacterEntityId) -> Result { get_or_clone(&self.item_state.character_inventory, &mut self.proxied_state.character_inventory, *character_id, ItemStateError::NoCharacter) /* Ok(InventoryState { character_id: *character_id, inventory: get_or_clone(&self.item_state.character_inventory, &mut self.proxied_state.character_inventory, *character_id, ItemStateError::NoCharacter)?, meseta: get_or_clone(&self.item_state.character_meseta, &mut self.proxied_state.character_meseta, *character_id, ItemStateError::NoCharacter)?, }) */ } pub fn set_inventory(&mut self, inventory: InventoryState) { self.proxied_state.character_inventory.insert(inventory.character_id, inventory); //self.proxied_state.character_inventory.insert(inventory.character_id, inventory.inventory); //self.proxied_state.character_meseta.insert(inventory.character_id, inventory.meseta); } pub fn floor(&mut self, character_id: &CharacterEntityId) -> Result { let room_id = get_or_clone(&self.item_state.character_room, &mut self.proxied_state.character_room, *character_id, ItemStateError::NoCharacter)?; Ok(FloorState { character_id: *character_id, local: get_or_clone(&self.item_state.character_floor, &mut self.proxied_state.character_floor, *character_id, ItemStateError::NoCharacter)?, shared: get_or_clone(&self.item_state.room_floor, &mut self.proxied_state.room_floor, room_id, ItemStateError::NoRoom)?, }) } pub fn set_floor(&mut self, floor: FloorState) { let room_id = get_or_clone(&self.item_state.character_room, &mut self.proxied_state.character_room, floor.character_id, ItemStateError::NoCharacter).unwrap(); self.proxied_state.character_floor.insert(floor.character_id, floor.local); self.proxied_state.room_floor.insert(room_id, floor.shared); } pub fn new_item_id(&mut self) -> Result { self.item_state.new_item_id() } }