use std::collections::HashMap; use crate::ship::items::ClientItemId; use crate::entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, ItemType, InventoryEntity, InventoryItemEntity, EquippedEntity, ItemNote}; use std::cell::{RefMut, RefCell}; use std::ops::{Deref, DerefMut}; use std::convert::{From, Into}; use std::future::Future; use std::pin::Pin; use async_std::sync::{Arc, Mutex}; use std::borrow::BorrowMut; use crate::ship::location::{AreaClient, RoomId}; use crate::entity::character::{CharacterEntity, CharacterEntityId, TechLevel}; use crate::entity::gateway::{EntityGateway, GatewayError}; use crate::entity::gateway::entitygateway::EntityGatewayTransaction; use crate::entity::item::tool::{Tool, ToolType}; 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: FnOnce(S, ()) -> Pin> + Send>> + Send + Sync 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, //F: FnOnce(S, P::Output) -> Pin> + Send>> + Send + Sync, { _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: FnOnce(S, P::Output) -> Pin> + Send>> + 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, { pub fn act(self, g: G) -> ItemActionStage, G, GFut, S, E> where S: Send + Sync, //G: FnOnce(S, as ItemAction>::Output) -> Pin> + Send>> + 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<'a>(&'a self) -> Option<&'a StackedItemDetail> { match self { InventoryItemDetail::Stacked(sitem) => Some(sitem), _ => None, } } fn stacked_mut <'a>(&'a mut self) -> Option<&'a 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.clone()).await; }, InventoryItemDetail::Stacked(stacked_item) => { for entity_id in &stacked_item.entity_ids { param = func(param, entity_id.clone()).await; } } } param } } #[derive(Clone)] pub enum FloorItemDetail { Individual(IndividualItemDetail), Stacked(StackedItemDetail), Meseta(Meseta), } impl FloorItemDetail { fn stacked<'a>(&'a self) -> Option<&'a StackedItemDetail> { match self { FloorItemDetail::Stacked(sitem) => Some(sitem), _ => None, } } /* fn mag<'a>(&'a self) -> Option<&'a IndividualItemDetail> { match self { FloorItemDetail::Individual(individual_item) => { match individual_item.item { ItemDetail::Mag(mag) => Some(mag), _ => None, } }, _ => 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.clone()).await; }, FloorItemDetail::Stacked(stacked_item) => { for entity_id in &stacked_item.entity_ids { param = func(param, entity_id.clone()).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, { match &self.item { FloorItemDetail::Individual(individual_item) => { match &individual_item.item { ItemDetail::Mag(mag) => { param = func(param, individual_item.entity_id.clone(), mag.clone()).await; } _ => {} } }, _ => {} } param } } // meseta is a floor item /* impl Into for FloorItem { fn into(&self) -> InventoryItem { InventoryItem { item_id: self.item_id, item: } } } */ #[derive(Clone, Debug)] pub struct Inventory(Vec); /* #[derive(Clone, Debug)] pub struct Inventory { item_id_counter: u32, items: Vec, equipped: EquippedEntity, } */ 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 { return 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() { return Err(InventoryError::StackFull) } else { existing_stack.entity_ids.append(&mut sitem.entity_ids.clone()); Ok(AddItemResult::AddToStack) } }, None => { if self.inventory.0.len() >= 30 { return 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, } } } /* struct ProxiedItemState { character_inventory: RefCell>>, //character_bank: HashMap>, //character_meseta: HashMap>, //bank_meseta: HashMap>, character_room: RefCell>>, character_floor: RefCell>>, room_floor: RefCell>>, //room_item_id_counter: HashMap ClientItemId + Send>>>, } impl Default for ProxiedItemState { fn default() -> Self { ProxiedItemState { character_inventory: RefCell::new(HashMap::new()), //character_bank: HashMap::new(), //character_meseta: HashMap::new(), //bank_meseta: HashMap::new(), character_floor: RefCell::new(HashMap::new()), character_room: RefCell::new(HashMap::new()), room_floor: RefCell::new(HashMap::new()), //room_item_id_counter: HashMap::new(), } } } */ 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, //entity_gateway: &'a mut dyn EntityGateway, //transaction: Box, //entity_gateway: &'a mut Box, //entity_gateway: &'a mut Box, proxied_state: ProxiedItemState, //gateway_actions: Vec, //_eg: std::marker::PhantomData, } //impl<'a> Drop for ItemStateProxy<'a> { // fn drop(&mut self) { 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<'a, K, V>(master: &HashMap, proxy: &'a RefCell>>, key: K, err: fn(K) -> ItemStateError) -> Result + 'a, ItemStateError> where K: Eq + std::hash::Hash + Copy, V: Clone { let existing_element = master.get(&key).ok_or_else(|| err(key))?; Ok(proxy.borrow_mut().entry(key) .or_insert(RefCell::new(existing_element.clone())) .borrow_mut()) } */ /* fn get_or_clone<'a, K, V>(master: &HashMap, proxy: &'a mut HashMap>, key: K, err: fn(K) -> ItemStateError) -> Result + 'a, ItemStateError> 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(RefCell::new(existing_element.clone())) .borrow_mut()) } */ 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(existing_element.clone()).clone()) } impl<'a> ItemStateProxy<'a> { //fn new(item_state: &'a mut ItemState, entity_gateway: &'a mut EG) -> Self { //fn new(item_state: &'a mut ItemState, entity_gateway: &'a mut dyn EntityGateway) -> Self { pub fn new(item_state: &'a mut ItemState/*, transaction: Box*/) -> Self { ItemStateProxy { item_state, //entity_gateway, //transaction, proxied_state: Default::default(), //gateway_actions: Vec::new(), //_eg: std::marker::PhantomData, } } 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, |c| ItemStateError::NoCharacter(c))?, meseta: get_or_clone(&self.item_state.character_meseta, &mut self.proxied_state.character_meseta, *character_id, |c| ItemStateError::NoCharacter(c))?, }) } 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, |c| ItemStateError::NoCharacter(c))?; Ok(FloorState { character_id: *character_id, local: get_or_clone(&self.item_state.character_floor, &mut self.proxied_state.character_floor, *character_id, |c| ItemStateError::NoCharacter(c))?, shared: get_or_clone(&self.item_state.room_floor, &mut self.proxied_state.room_floor, room_id, |r| ItemStateError::NoRoom(r))?, }) } 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, |c| ItemStateError::NoCharacter(c)).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 { //let room_id = get_or_clone(&self.item_state.character_room, &mut self.proxied_state.character_room, *character_id, |c| ItemStateError::NoCharacter(c))?; self.item_state.room_item_id_counter += 1; Ok(ClientItemId(self.item_state.room_item_id_counter)) /* self.item_state.room_item_id_counter .borrow_mut() .get_mut(&room_id) .ok_or(ItemStateError::NoRoom(room_id)) .map(|f| f()) */ } } /* fn record_item_drop(character_id: CharacterEntityId, item_drop: ItemDrop) -> impl Fn(&mut ItemStateProxy, ()) -> Result<(), ItemStateError> { move |item_state, _| { // how do I do this? I need EG to add_item_note but how should I kick that till later? // general purpose vec in item_state `misc_gateway_actions`? // can't quite use actions here as it relies on an ItemEntityId which at this point does not yet exist for the dropped item //item_state.gateway_actions.push(GatewayAction::ItemNote(item_drop.)) Ok(()) } } */ /* fn map_drop_to_floor_item(character_id: CharacterEntityId, item_drop: ItemDrop) -> impl Fn(&mut ItemStateProxy, ()) -> Result { move |item_state, _| { match item_drop.item { ItemDropType::Weapon(w) => FloorItem { item_id: item_state.new_item_id(&character_id)?, item: FloorItemDetail::Individual(item_drop.item) }, ItemDropType::Armor(w) => ItemOrMeseta::Individual(ItemDetail::Armor(w)), ItemDropType::Shield(w) => ItemOrMeseta::Individual(ItemDetail::Shield(w)), ItemDropType::Unit(w) => ItemOrMeseta::Individual(ItemDetail::Unit(w)), ItemDropType::TechniqueDisk(w) => ItemOrMeseta::Individual(ItemDetail::TechniqueDisk(w)), ItemDropType::Mag(w) => ItemOrMeseta::Individual(ItemDetail::Mag(w)), //ItemDropType::IndividualTool(t) => ItemOrMeseta::Individual(ItemDetail::Tool(t)), //ItemDropType::StackedTool(t, _) => ItemOrMeseta::Stacked(t), ItemDropType::Tool(t) if t.tool.is_stackable() => ItemOrMeseta::Stacked(t), ItemDropType::Tool(t) if !t.tool.is_stackable() => ItemOrMeseta::Individual(ItemDetail::Tool(t)), ItemDropType::Meseta(m) => ItemOrMeseta::Meseta(Meseta(m)), _ => unreachable!() // rust isnt smart enough to see that the conditional on tool catches everything } } } fn enemy_drops_item(item_state: &mut ItemState, entity_gateway: &mut EG, character: &CharacterEntity, item_drop: ItemDrop) -> Result where EG: EntityGateway, { ItemStateAction::default() .act(record_item_drop(character.id, item_drop.clone())) .act(map_drop_to_floor_item(character.id, item_drop)) .act(add_item_drop_to_floor(character.id)) .commit(&mut ItemStateProxy::new(item_state)) } */ /* fn sell_item(item_state: &mut ItemState, entity_gateway: &mut EG, character_id: &CharacterEntityId, item_id: &ClientItemId, amount: usize) -> Result where EG: EntityGateway, { ItemStateAction::default() .act(take_item_from_inventory(*character_id, *item_id)) .act(sell_inventory_item(*character_id, *item_id)) .exec_state(&mut ItemStateProxy::new(item_state)) .exec_gateway(&mut entity_gateway) } */