buy items from shop
This commit is contained in:
		
							parent
							
								
									ce09c93940
								
							
						
					
					
						commit
						b5c821f1ae
					
				@ -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<dyn EntityGatewayTransaction + 'a>), ())
 | 
			
		||||
                                               -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), 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<InventoryItem, ItemStateError>
 | 
			
		||||
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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -600,7 +600,7 @@ impl InventoryState {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn add_item(&mut self, item: InventoryItem) -> Result<AddItemResult, InventoryError> {
 | 
			
		||||
    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()
 | 
			
		||||
                            ))
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
@ -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<CreateItem, ShipError> {
 | 
			
		||||
    let bytes = item.as_client_bytes();
 | 
			
		||||
pub fn create_withdrawn_inventory_item(area_client: AreaClient, item: &InventoryItem2) -> Result<CreateItem, ShipError> {
 | 
			
		||||
    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,
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
@ -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<EG>(id: ClientId,
 | 
			
		||||
                          entity_gateway: &mut EG,
 | 
			
		||||
                          client_location: &ClientLocation,
 | 
			
		||||
                          clients: &mut Clients,
 | 
			
		||||
                          item_manager: &mut ItemManager)
 | 
			
		||||
                          item_state: &mut ItemState)
 | 
			
		||||
                          -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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 {
 | 
			
		||||
 | 
			
		||||
@ -395,17 +395,16 @@ where
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn player_sells_item<EG> (id: ClientId,
 | 
			
		||||
                            sold_item: &PlayerSoldItem,
 | 
			
		||||
                            entity_gateway: &mut EG,
 | 
			
		||||
                            // client_location: &ClientLocation,
 | 
			
		||||
                            clients: &mut Clients,
 | 
			
		||||
                            item_manager: &mut ItemManager)
 | 
			
		||||
                            -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
 | 
			
		||||
                                    sold_item: &PlayerSoldItem,
 | 
			
		||||
                                    entity_gateway: &mut EG,
 | 
			
		||||
                                    clients: &mut Clients,
 | 
			
		||||
                                    item_state: &mut ItemState)
 | 
			
		||||
                                    -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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()))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -521,7 +521,7 @@ impl<EG: EntityGateway> ShipServerState<EG> {
 | 
			
		||||
                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<EG: EntityGateway> ShipServerState<EG> {
 | 
			
		||||
                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?
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user