From 1ef0231d93ef0562171ab9fa24bd27b756ceba7a Mon Sep 17 00:00:00 2001 From: jake Date: Tue, 19 Apr 2022 23:20:50 -0600 Subject: [PATCH] initial ItemState --- Cargo.toml | 2 +- src/lib.rs | 2 + src/ship/items/mod.rs | 1 + src/ship/items/state.rs | 813 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 817 insertions(+), 1 deletion(-) create mode 100644 src/ship/items/state.rs diff --git a/Cargo.toml b/Cargo.toml index 3f17eea..789571d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,4 +32,4 @@ sqlx = { version = "0.5.10", features = ["runtime-async-std-native-tls", "postgr strum = "0.19.5" strum_macros = "0.19" anyhow = { version = "1.0.47", features = ["backtrace"] } - +fix-hidden-lifetime-bug = "0.2.4" diff --git a/src/lib.rs b/src/lib.rs index b8ceb02..9934022 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,8 @@ #![feature(drain_filter)] #![feature(try_blocks)] +#[macro_use] +extern crate fix_hidden_lifetime_bug; pub mod common; diff --git a/src/ship/items/mod.rs b/src/ship/items/mod.rs index 8a917a8..2b88239 100644 --- a/src/ship/items/mod.rs +++ b/src/ship/items/mod.rs @@ -4,6 +4,7 @@ pub mod inventory; pub mod manager; pub mod transaction; pub mod use_tool; +pub mod state; use serde::{Serialize, Deserialize}; #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Serialize, Deserialize, derive_more::Display)] diff --git a/src/ship/items/state.rs b/src/ship/items/state.rs new file mode 100644 index 0000000..b7ce7d4 --- /dev/null +++ b/src/ship/items/state.rs @@ -0,0 +1,813 @@ +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::ship::drops::ItemDrop; + +pub enum TriggerCreateItem { + Yes, + No +} + + + +#[derive(thiserror::Error, Debug)] +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(GatewayError), +} + +impl From for ItemStateError { + fn from(other: GatewayError) -> ItemStateError { + ItemStateError::GatewayError(other) + } +} + + + +enum GatewayActions { + ItemNote(ItemEntityId, ItemNote), +} + +/* +enum ItemStateChange { + Inventory(CharacterInventory), + Bank(CharacterBank), + CharacterMeseta(CharacterMeseta), + BankMeseta(BankMeseta), + SharedFloor(SharedFloor), + LocalFloor(LocalFloor), +} +*/ + +#[async_trait::async_trait] +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>; +} + + +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, +{ + fn act(self, f: F) -> ItemActionStage, F, S, E> + where + F: Fn(S, ()) -> Pin> + Send>> + Send + Sync + { + ItemActionStage { + _s: Default::default(), + _e: std::marker::PhantomData, + prev: self, + actionf: f, + //actionf: |s, t| f(s, ()), + } + } +} + +struct ItemActionStage +where + P: ItemAction, + //F: Fn(&mut S, P::Output) -> Result + for<'r> std::ops::Fn<(&'r mut S, T)>, + F: Fn(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: Fn(S, P::Output) -> Pin> + Send>> + Send + Sync, + 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) -> Pin> + Send>> + Send + Sync, + S: Send + Sync, + P::Output: Send + Sync, + E: Send + Sync, + O: Send + Sync, + P::Error: Send + Sync, +{ + fn act(self, g: G) -> ItemActionStage, G, S, E> + where + S: Send + Sync, + G: Fn(S, as ItemAction>::Output) -> Pin> + Send>> + Send + Sync, + 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)] +struct IndividualItemDetail { + entity_id: ItemEntityId, + item: ItemDetail, +} + +#[derive(Clone)] +pub struct StackedItemDetail { + pub entity_ids: Vec, + pub tool: Tool, +} + +#[derive(Clone)] +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)] +struct InventoryItem { + item_id: ClientItemId, + item: InventoryItemDetail, +} + +#[derive(Clone)] +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, + } + } +} + +#[derive(Clone)] +struct FloorItem { + item_id: ClientItemId, + item: FloorItemDetail, +} + +// meseta is a floor item +/* +impl Into for FloorItem { + fn into(&self) -> InventoryItem { + InventoryItem { + item_id: self.item_id, + item: + } + } +} +*/ + +#[derive(Clone)] +struct Inventory(Vec); + + +#[derive(thiserror::Error, Debug)] +enum InventoryError { + #[error("inventory full")] + InventoryFull, + #[error("stack full")] + StackFull, + #[error("meseta full")] + MesetaFull, +} + +enum AddItemResult { + NewItem, + AddToStack, + Meseta, +} + + + + + +#[derive(Clone)] +struct LocalFloor(Vec); +#[derive(Clone)] +struct SharedFloor(Vec); +#[derive(Clone)] +struct RoomFloorItems(Vec); + +struct InventoryState { + character_id: CharacterEntityId, + inventory: Inventory, + meseta: Meseta, +} + + +/* +impl Deref for InventoryState { + type Target = Inventory; + + fn deref(&self) -> &Self::Target { + &self.inventory + } +} + + +impl DerefMut for InventoryState { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inventory + } +} +*/ + +impl InventoryState { + 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) + } + }, + } + } +} + +struct FloorState { + character_id: CharacterEntityId, + local: LocalFloor, + shared: SharedFloor, +} + +impl FloorState { + 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() + }) + } +} + + +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(), + } + } +} + +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, +} + +/* +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 { + 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, + } + } + + 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))?, + }) + } + + 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); + } + + 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))?, + }) + } + + 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); + } + + 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 take_item_from_floor(character_id: CharacterEntityId, item_id: ClientItemId) + -> impl for<'a> Fn(ItemStateProxy<'a>, ()) + -> Pin> + Send + 'a>> { + move |mut item_state, _| { + Box::pin(async move { + let mut floor = item_state.floor(&character_id)?; + let item = floor.take_item(&item_id).ok_or(ItemStateError::NoFloorItem(item_id))?; + item_state.set_floor(floor); + Ok((item_state, item)) + }) + } +} + +fn add_floor_item_to_inventory(character_id: CharacterEntityId) + -> impl for<'a> Fn(ItemStateProxy<'a>, FloorItem) + -> Pin> + Send + 'a>> { + move |mut item_state, floor_item| Box::pin(async move { + let mut inventory = item_state.inventory(&character_id)?; + let add_result = inventory.add_floor_item(floor_item)?; + item_state.set_inventory(inventory); + Ok((item_state, + match add_result { + AddItemResult::NewItem => TriggerCreateItem::Yes, + AddItemResult::AddToStack => TriggerCreateItem::No, + AddItemResult::Meseta => TriggerCreateItem::No, + })) + }) +} + + +//hint R>(f: F) -> F {f} + + +#[fix_hidden_lifetime_bug] +async fn pick_up_item<'a, EG>( + item_state: &'a mut ItemState, + //item_state: Arc>, + entity_gateway: &'a mut EG, + character_id: &CharacterEntityId, + item_id: &ClientItemId) + -> Result +where + 'a: 'static, + EG: EntityGateway, +{ + /* + let transaction = entity_gateway.transaction().await.unwrap(); + + let item_state_proxy = ItemStateProxy::new(item_state, transaction); + let (mut item_state_proxy, result) = ItemStateAction::default() + .act(take_item_from_floor(*character_id, *item_id)) + .act(add_floor_item_to_inventory(*character_id)) + .commit(item_state_proxy) + .await?; + item_state_proxy.transaction.commit().await.unwrap(); + */ + + //drop(item_state_proxy); + //transaction.commit().await.unwrap(); + //item_state_proxy.entity_gateway.commit().await.unwrap(); + + let k: Result = entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state, transaction); + let (item_state_proxy, result) = ItemStateAction::default() + .act(take_item_from_floor(*character_id, *item_id)) + .act(add_floor_item_to_inventory(*character_id)) + .commit(item_state_proxy) + .await?; + + //let u = transaction.get_user_by_id(crate::entity::account::UserAccountId(0)).await?; + //Ok((transaction, ())) + //drop(item_state_proxy); + Ok((item_state_proxy.transaction, result)) + }).await; + + /* + //fn subaction(transaction: &mut dyn EntityGateway) -> Pin> + Send + 'static>> { + async fn subaction(transaction: &mut dyn EntityGateway) -> Result<(), GatewayError> { + let item_state_proxy = ItemStateProxy::new(item_state, transaction); + + Ok(()) + } + + //async fn action(item_state: &mut ItemState, entity_gateway: &mut EG) -> Result<(), GatewayError> { + fn action(item_state: &mut ItemState) + -> impl FnOnce(&mut dyn EntityGateway) -> Pin> + Send + 'static>> + { + /* + |transaction| Box::pin(async move { + Ok(()) + }) + */ + subaction + } + */ + + //let k: Result<(), GatewayError> = entity_gateway.with_transaction(action(item_state)).await; + + //let item_state = item_state.clone(); + /* + let k: Result<(), GatewayError> = entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state, transaction); + /* + let item_state = item_state.clone(); + let mut s = item_state.lock().await; + //let s = s.borrow_mut(); + let item_state_proxy = ItemStateProxy::new(&mut s, transaction); + ItemStateAction::default() + .act(take_item_from_floor(*character_id, *item_id)) + .act(add_floor_item_to_inventory(*character_id)) + .commit(item_state_proxy) + .await + */ + Ok(()) + }).await; + */ + + //let mut transaction = entity_gateway.transaction().await?; + + /*entity_gateway.with_transaction(|transaction| { + item_state_proxy.execute(&mut transaction)?.await + }).await?;*/ + + //Ok(result) + Ok(TriggerCreateItem::Yes) + //.exec_gateway(&mut entity_gateway) +} + + +/* +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) +} +*/ +