diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index 216c523..1f06d0f 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -9,8 +9,9 @@ use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction}; use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateAction, ItemAction, ItemStateError, FloorItem, InventoryItem, AddItemResult, FloorItemDetail, StackedItemDetail, BankItem, BankItemDetail, InventoryItemDetail, IndividualItemDetail}; use crate::ship::items::apply_item::apply_item; -use crate::entity::item::ItemDetail; +use crate::entity::item::{ItemDetail, ItemEntity, NewItemEntity}; use crate::entity::item::tool::Tool; +use crate::ship::shops::ShopItem; @@ -701,3 +702,88 @@ where Ok((transaction, ())) }).await } + + +fn add_bought_item_to_inventory<'a>(character_id: CharacterEntityId, + shop_item: &'a (dyn ShopItem + Send + Sync), + item_id: ClientItemId, + amount: u32) + -> impl Fn((ItemStateProxy<'a>, Box), ()) + -> Pin, Box), InventoryItem), ItemStateError>> + Send + 'a>> +{ + move |(mut item_state, mut transaction), _| { + Box::pin(async move { + let mut inventory = item_state.inventory(&character_id)?; + let bought_item = shop_item.as_item(); + + let inventory_item = match bought_item { + ItemDetail::Tool(tool) if tool.is_stackable() => { + let mut item_entities = Vec::new(); + for _ in 0..amount { + let item_entity = transaction.gateway().create_item(NewItemEntity { + item: ItemDetail::Tool(tool), + }).await?; + transaction.gateway().add_item_note(&item_entity.id, ItemNote::BoughtAtShop { + character_id: character_id, + }).await?; + item_entities.push(item_entity); + } + + let inventory_item = InventoryItem { + item_id, + item: InventoryItemDetail::Stacked(StackedItemDetail { + entity_ids: item_entities.into_iter().map(|i| i.id).collect(), + tool: tool, + }) + }; + inventory.add_item(inventory_item)?.1 + }, + item_detail => { + let item_entity = transaction.gateway().create_item(NewItemEntity { + item: item_detail.clone(), + }).await?; + transaction.gateway().add_item_note(&item_entity.id, ItemNote::BoughtAtShop { + character_id: character_id, + }).await?; + + let inventory_item = InventoryItem { + item_id, + item: InventoryItemDetail::Individual(IndividualItemDetail { + entity_id: item_entity.id, + item: item_detail, + }) + }; + inventory.add_item(inventory_item)?.1 + }, + }; + + transaction.gateway().set_character_inventory(&character_id, &inventory.as_inventory_entity(&character_id)).await?; + item_state.set_inventory(inventory); + Ok(((item_state, transaction), inventory_item)) + }) + } +} + +pub async fn buy_shop_item<'a, EG> ( + item_state: &'a mut ItemState, + entity_gateway: &mut EG, + character: &CharacterEntity, + shop_item: &'a (dyn ShopItem + Send + Sync), + item_id: ClientItemId, + amount: u32, +) -> Result +where + EG: EntityGateway, +{ + let item_price = shop_item.price() as u32 * amount; + entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), result) = ItemStateAction::default() + .act(take_meseta_from_inventory(character.id, item_price)) + .act(add_bought_item_to_inventory(character.id, shop_item, item_id, amount)) + .commit((item_state_proxy, transaction)) + .await?; + item_state_proxy.commit(); + Ok((transaction, result)) + }).await +} diff --git a/src/ship/items/state.rs b/src/ship/items/state.rs index 7c33c3f..3967182 100644 --- a/src/ship/items/state.rs +++ b/src/ship/items/state.rs @@ -600,7 +600,7 @@ impl InventoryState { } } - pub fn add_item(&mut self, item: InventoryItem) -> Result { + pub fn add_item(&mut self, item: InventoryItem) -> Result<(AddItemResult, InventoryItem), InventoryError> { match &item.item { InventoryItemDetail::Individual(_) => { if self.inventory.0.len() >= 30 { @@ -608,7 +608,13 @@ impl InventoryState { } else { self.inventory.0.push(item); - Ok(AddItemResult::NewItem) + Ok(( + AddItemResult::NewItem, + self.inventory.0 + .last() + .unwrap() + .clone() + )) } }, InventoryItemDetail::Stacked(sitem) => { @@ -625,7 +631,15 @@ impl InventoryState { } else { existing_stack.entity_ids.append(&mut sitem.entity_ids.clone()); - Ok(AddItemResult::AddToStack) + Ok(( + AddItemResult::AddToStack, + self.inventory.0[self.inventory.0 + .iter() + .filter_map(|item| item.item.stacked()) + .position(|item| item.tool == sitem.tool) + .unwrap()] + .clone() + )) } }, None => { @@ -634,7 +648,13 @@ impl InventoryState { } else { self.inventory.0.push(item); - Ok(AddItemResult::NewItem) + Ok(( + AddItemResult::NewItem, + self.inventory.0 + .last() + .unwrap() + .clone() + )) } } } diff --git a/src/ship/packet/builder/message.rs b/src/ship/packet/builder/message.rs index cf91fe9..a349b8f 100644 --- a/src/ship/packet/builder/message.rs +++ b/src/ship/packet/builder/message.rs @@ -68,13 +68,13 @@ pub fn create_meseta(area_client: AreaClient, amount: usize) -> CreateItem { } } -pub fn create_withdrawn_inventory_item(area_client: AreaClient, item: &InventoryItem) -> Result { - let bytes = item.as_client_bytes(); +pub fn create_withdrawn_inventory_item(area_client: AreaClient, item: &InventoryItem2) -> Result { + let bytes = item.item.as_client_bytes(); Ok(CreateItem { client: area_client.local_client.id(), target: 0, item_data: bytes[0..12].try_into()?, - item_id: item.item_id().0, + item_id: item.item_id.0, item_data2: bytes[12..16].try_into()?, unknown: 0, }) diff --git a/src/ship/packet/handler/direct_message.rs b/src/ship/packet/handler/direct_message.rs index 38968a3..1cc319a 100644 --- a/src/ship/packet/handler/direct_message.rs +++ b/src/ship/packet/handler/direct_message.rs @@ -15,7 +15,7 @@ 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::actions::{pick_up_item, withdraw_meseta, deposit_meseta, withdraw_item, deposit_item, TriggerCreateItem}; +use crate::ship::items::actions::{pick_up_item, withdraw_meseta, deposit_meseta, withdraw_item, deposit_item, buy_shop_item, TriggerCreateItem}; const BANK_ACTION_DEPOSIT: u8 = 0; const BANK_ACTION_WITHDRAW: u8 = 1; @@ -347,7 +347,7 @@ pub async fn buy_item(id: ClientId, entity_gateway: &mut EG, client_location: &ClientLocation, clients: &mut Clients, - item_manager: &mut ItemManager) + item_state: &mut ItemState) -> Result + Send>, anyhow::Error> where EG: EntityGateway @@ -355,7 +355,6 @@ where let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?; - let (item, remove): (&(dyn ShopItem + Send + Sync), bool) = match buy_item.shop_type { SHOP_OPTION_WEAPON => { (client.weapon_shop.get(buy_item.shop_index as usize).ok_or(ShipError::ShopError)?, false) @@ -375,16 +374,8 @@ where } }; - let character_meseta = item_manager.get_character_meseta_mut(&client.character.id)?; - if character_meseta.0 < (item.price() * buy_item.amount as usize) as u32 { - return Err(ShipError::ShopError.into()) - } - - character_meseta.0 -= (item.price() * buy_item.amount as usize) as u32; - entity_gateway.set_character_meseta(&client.character.id, *character_meseta).await?; - - let inventory_item = item_manager.player_buys_item(entity_gateway, &client.character, item, ClientItemId(buy_item.item_id), buy_item.amount as usize).await?; - let create = builder::message::create_withdrawn_inventory_item(area_client, inventory_item)?; + let inventory_item = buy_shop_item(item_state, entity_gateway, &mut client.character, item, ClientItemId(buy_item.item_id), buy_item.amount as u32).await?; + let create = builder::message::create_withdrawn_inventory_item(area_client, &inventory_item)?; if remove { match buy_item.shop_type { diff --git a/src/ship/packet/handler/message.rs b/src/ship/packet/handler/message.rs index f25e4cc..0977f43 100644 --- a/src/ship/packet/handler/message.rs +++ b/src/ship/packet/handler/message.rs @@ -395,17 +395,16 @@ where } pub async fn player_sells_item (id: ClientId, - sold_item: &PlayerSoldItem, - entity_gateway: &mut EG, - // client_location: &ClientLocation, - clients: &mut Clients, - item_manager: &mut ItemManager) - -> Result + Send>, anyhow::Error> + sold_item: &PlayerSoldItem, + entity_gateway: &mut EG, + clients: &mut Clients, + item_state: &mut ItemState) + -> Result + Send>, anyhow::Error> where EG: EntityGateway { let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; - item_manager.player_sells_item(entity_gateway, &mut client.character, ClientItemId(sold_item.item_id), sold_item.amount as usize).await?; + //item_manager.player_sells_item(entity_gateway, &mut client.character, ClientItemId(sold_item.item_id), sold_item.amount as usize).await?; // TODO: send the packet to other clients Ok(Box::new(None.into_iter())) } diff --git a/src/ship/ship.rs b/src/ship/ship.rs index d75b1f8..b833024 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -521,7 +521,7 @@ impl ShipServerState { handler::message::player_sorts_items(id, sort_items, &mut self.entity_gateway, &self.clients, &mut self.item_state).await? }, GameMessage::PlayerSoldItem(player_sold_item) => { - handler::message::player_sells_item(id, player_sold_item, &mut self.entity_gateway, &mut self.clients, &mut self.item_manager).await? + handler::message::player_sells_item(id, player_sold_item, &mut self.entity_gateway, &mut self.clients, &mut self.item_state).await? }, _ => { let cmsg = msg.clone(); @@ -560,7 +560,7 @@ impl ShipServerState { handler::direct_message::shop_request(id, shop_request, &block.client_location, &mut self.clients, &block.rooms, &self.level_table, &mut self.shops).await? }, GameMessage::BuyItem(buy_item) => { - handler::direct_message::buy_item(id, buy_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await? + handler::direct_message::buy_item(id, buy_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_state).await? }, GameMessage::TekRequest(tek_request) => { handler::direct_message::request_tek_item(id, tek_request, &mut self.entity_gateway, &mut self.clients, &mut self.item_manager).await?