use std::collections::HashMap; use crate::ship::items::ClientItemId; use crate::entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, InventoryEntity, InventoryItemEntity}; use std::future::Future; use crate::ship::location::RoomId; use crate::entity::character::CharacterEntityId; use crate::entity::gateway::GatewayError; use crate::entity::gateway::entitygateway::EntityGatewayTransaction; 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), } #[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 { entity_id: ItemEntityId, item: ItemDetail, } #[derive(Clone, Debug)] pub struct StackedItemDetail { pub entity_ids: Vec, pub tool: Tool, } #[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, } } } #[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)] 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 { item_id: ClientItemId, item: FloorItemDetail, } 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 } } #[derive(Clone, Debug)] pub struct Inventory(Vec); impl Inventory { pub fn as_inventory_entity(&self, _character_id: &CharacterEntityId) -> InventoryEntity { InventoryEntity { items: self.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() } } } #[derive(thiserror::Error, Debug)] pub enum InventoryError { #[error("inventory full")] InventoryFull, #[error("stack full")] StackFull, #[error("meseta full")] MesetaFull, } pub enum AddItemResult { NewItem, AddToStack, Meseta, } #[derive(Clone)] pub struct LocalFloor(Vec); #[derive(Clone)] pub struct SharedFloor(Vec); #[derive(Clone)] pub struct RoomFloorItems(Vec); pub struct InventoryState { character_id: CharacterEntityId, pub inventory: Inventory, meseta: Meseta, } impl InventoryState { 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 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 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_meseta: HashMap::new(), character_room: HashMap::new(), character_floor: HashMap::new(), room_floor: HashMap::new(), room_item_id_counter: 0x00810000, } } } #[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_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 { 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.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.room_item_id_counter += 1; Ok(ClientItemId(self.item_state.room_item_id_counter)) } }