Compare commits

...

5 Commits

Author SHA1 Message Date
df135e1c3c split actions into actions+tasks
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2022-07-19 21:26:00 -06:00
24639496da move bank code out of state 2022-07-19 20:48:55 -06:00
414b3d2ce5 move floor code out of state 2022-07-19 20:06:43 -06:00
0fa3f4ea19 move inventory code out of state 2022-07-19 19:39:58 -06:00
60802d3377 move ItemStateAction to its own file 2022-07-19 19:04:53 -06:00
15 changed files with 1711 additions and 1617 deletions

View File

@ -2,7 +2,8 @@ use libpso::character::character;
use crate::common::leveltable::CharacterStats;
use crate::entity::character::CharacterEntity;
//use crate::ship::items::{CharacterInventory, CharacterBank};
use crate::ship::items::state::{InventoryState, BankState};
use crate::ship::items::bank::BankState;
use crate::ship::items::inventory::InventoryState;
use crate::entity::item::Meseta;

View File

@ -1,5 +1,4 @@
// TODO: replace various u32s and usizes denoting item amounts for ItemAmount(u32) for consistency
use crate::ship::items::ClientItemId;
use crate::entity::item::{Meseta, ItemNote};
use std::future::Future;
@ -7,16 +6,16 @@ use std::pin::Pin;
use crate::ship::map::MapArea;
use crate::entity::character::{CharacterEntity, CharacterEntityId};
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::entity::gateway::EntityGatewayTransaction;
use crate::ship::items::state::{ItemStateProxy, ItemStateError, AddItemResult, StackedItemDetail, IndividualItemDetail};
use crate::ship::items::bank::{BankItem, BankItemDetail};
use crate::ship::items::inventory::{InventoryItem, InventoryItemDetail};
use crate::ship::items::floor::{FloorItem, FloorItemDetail};
use crate::ship::items::apply_item::apply_item;
use crate::entity::item::{ItemDetail, NewItemEntity, TradeId};
use crate::entity::item::tool::Tool;
use crate::entity::item::ItemModifier;
use crate::ship::shops::ShopItem;
use crate::ship::trade::TradeItem;
use crate::ship::location::AreaClient;
use crate::ship::drops::{ItemDrop, ItemDropType};
pub enum TriggerCreateItem {
@ -24,7 +23,7 @@ pub enum TriggerCreateItem {
No
}
fn take_item_from_floor(character_id: CharacterEntityId, item_id: ClientItemId)
pub(super) fn take_item_from_floor(character_id: CharacterEntityId, item_id: ClientItemId)
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ())
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), FloorItem), ItemStateError>> + Send + 'a>>
{
@ -39,7 +38,7 @@ fn take_item_from_floor(character_id: CharacterEntityId, item_id: ClientItemId)
}
}
fn add_floor_item_to_inventory(character: &CharacterEntity)
pub(super) fn add_floor_item_to_inventory(character: &CharacterEntity)
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), FloorItem)
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), TriggerCreateItem), ItemStateError>> + Send + 'a>>
{
@ -81,28 +80,8 @@ fn add_floor_item_to_inventory(character: &CharacterEntity)
}
pub async fn pick_up_item<EG>(
item_state: &mut ItemState,
entity_gateway: &mut EG,
character: &CharacterEntity,
item_id: &ClientItemId)
-> Result<TriggerCreateItem, ItemStateError>
where
EG: EntityGateway,
{
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_item_from_floor(character.id, *item_id))
.act(add_floor_item_to_inventory(character))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
Ok((transaction, result))
}).await
}
fn take_item_from_inventory(character_id: CharacterEntityId, item_id: ClientItemId, amount: u32)
pub(super) fn take_item_from_inventory(character_id: CharacterEntityId, item_id: ClientItemId, amount: u32)
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ())
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem), ItemStateError>> + Send + 'a>>
{
@ -120,7 +99,7 @@ fn take_item_from_inventory(character_id: CharacterEntityId, item_id: ClientItem
}
fn add_inventory_item_to_shared_floor(character_id: CharacterEntityId, map_area: MapArea, drop_position: (f32, f32, f32))
pub(super) fn add_inventory_item_to_shared_floor(character_id: CharacterEntityId, map_area: MapArea, drop_position: (f32, f32, f32))
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem)
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), FloorItem), ItemStateError>> + Send + 'a>>
{
@ -147,55 +126,8 @@ fn add_inventory_item_to_shared_floor(character_id: CharacterEntityId, map_area:
}
}
pub async fn drop_item<EG>(
item_state: &mut ItemState,
entity_gateway: &mut EG,
character: &CharacterEntity,
item_id: &ClientItemId,
map_area: MapArea,
drop_position: (f32, f32, f32))
-> Result<FloorItem, ItemStateError>
where
EG: EntityGateway,
{
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_item_from_inventory(character.id, *item_id, 0))
.act(add_inventory_item_to_shared_floor(character.id, map_area, drop_position))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
Ok((transaction, result))
}).await
}
pub async fn drop_partial_item<'a, EG>(
item_state: &'a mut ItemState,
entity_gateway: &mut EG,
character: &CharacterEntity,
item_id: &ClientItemId,
map_area: MapArea,
drop_position: (f32, f32),
amount: u32)
-> Result<FloorItem, ItemStateError>
where
EG: EntityGateway,
{
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_item_from_inventory(character.id, *item_id, amount))
.act(add_inventory_item_to_shared_floor(character.id, map_area, (drop_position.0, 0.0, drop_position.1)))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
Ok((transaction, result))
}).await
}
fn take_meseta_from_inventory(character_id: CharacterEntityId, amount: u32)
pub(super) fn take_meseta_from_inventory(character_id: CharacterEntityId, amount: u32)
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ())
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()), ItemStateError>> + Send + 'a>>
{
@ -211,7 +143,7 @@ fn take_meseta_from_inventory(character_id: CharacterEntityId, amount: u32)
}
}
fn add_meseta_to_inventory(character_id: CharacterEntityId, amount: u32)
pub(super) fn add_meseta_to_inventory(character_id: CharacterEntityId, amount: u32)
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ())
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()), ItemStateError>> + Send + 'a>>
{
@ -227,7 +159,7 @@ fn add_meseta_to_inventory(character_id: CharacterEntityId, amount: u32)
}
}
fn add_meseta_to_shared_floor(character_id: CharacterEntityId, amount: u32, map_area: MapArea, drop_position: (f32, f32))
pub(super) fn add_meseta_to_shared_floor(character_id: CharacterEntityId, amount: u32, map_area: MapArea, drop_position: (f32, f32))
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ())
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), FloorItem), ItemStateError>> + Send + 'a>>
{
@ -252,31 +184,7 @@ fn add_meseta_to_shared_floor(character_id: CharacterEntityId, amount: u32, map_
}
}
pub async fn drop_meseta<'a, EG>(
item_state: &'a mut ItemState,
entity_gateway: &mut EG,
character: &CharacterEntity,
map_area: MapArea,
drop_position: (f32, f32),
amount: u32)
-> Result<FloorItem, ItemStateError>
where
EG: EntityGateway,
{
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, amount))
.act(add_meseta_to_shared_floor(character.id, amount, map_area, drop_position))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
Ok((transaction, result))
}).await
}
fn take_meseta_from_bank(character_id: CharacterEntityId, amount: u32)
pub(super) fn take_meseta_from_bank(character_id: CharacterEntityId, amount: u32)
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ())
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()), ItemStateError>> + Send + 'a>>
{
@ -291,7 +199,7 @@ fn take_meseta_from_bank(character_id: CharacterEntityId, amount: u32)
}
}
fn add_meseta_from_bank_to_inventory(character_id: CharacterEntityId, amount: u32)
pub(super) fn add_meseta_from_bank_to_inventory(character_id: CharacterEntityId, amount: u32)
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ())
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()), ItemStateError>> + Send + 'a>>
{
@ -307,28 +215,7 @@ fn add_meseta_from_bank_to_inventory(character_id: CharacterEntityId, amount: u3
}
pub async fn withdraw_meseta<'a, EG>(
item_state: &'a mut ItemState,
entity_gateway: &mut EG,
character: &CharacterEntity,
amount: u32)
-> Result<(), ItemStateError>
where
EG: EntityGateway,
{
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_bank(character.id, amount))
.act(add_meseta_from_bank_to_inventory(character.id, amount))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
Ok((transaction, result))
}).await
}
fn add_meseta_to_bank(character_id: CharacterEntityId, amount: u32)
pub(super) fn add_meseta_to_bank(character_id: CharacterEntityId, amount: u32)
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ())
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()), ItemStateError>> + Send + 'a>>
{
@ -343,28 +230,8 @@ fn add_meseta_to_bank(character_id: CharacterEntityId, amount: u32)
}
}
pub async fn deposit_meseta<'a, EG>(
item_state: &'a mut ItemState,
entity_gateway: &mut EG,
character: &CharacterEntity,
amount: u32)
-> Result<(), ItemStateError>
where
EG: EntityGateway,
{
entity_gateway.with_transaction(|transaction| async move {
let item_state_proxy = ItemStateProxy::new(item_state);
let ((item_state_proxy, transaction), _) = ItemStateAction::default()
.act(take_meseta_from_inventory(character.id, amount))
.act(add_meseta_to_bank(character.id, amount))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
Ok((transaction, ()))
}).await
}
fn take_item_from_bank(character_id: CharacterEntityId, item_id: ClientItemId, amount: u32)
pub(super) fn take_item_from_bank(character_id: CharacterEntityId, item_id: ClientItemId, amount: u32)
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ())
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), BankItem), ItemStateError>> + Send + 'a>>
{
@ -380,7 +247,7 @@ fn take_item_from_bank(character_id: CharacterEntityId, item_id: ClientItemId, a
}
}
fn add_bank_item_to_inventory(character: &CharacterEntity)
pub(super) fn add_bank_item_to_inventory(character: &CharacterEntity)
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), BankItem)
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem), ItemStateError>> + Send + 'a>>
{
@ -426,31 +293,8 @@ fn add_bank_item_to_inventory(character: &CharacterEntity)
}
}
pub async fn withdraw_item<'a, EG>(
item_state: &'a mut ItemState,
entity_gateway: &mut EG,
character: &CharacterEntity,
item_id: &ClientItemId,
amount: u32)
-> Result<InventoryItem, ItemStateError>
where
EG: EntityGateway,
{
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_item_from_bank(character.id, *item_id, amount))
//.act(bank_item_to_inventory_item)
//.act(add_item_to_inventory)
.act(add_bank_item_to_inventory(character))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
Ok((transaction, result))
}).await
}
fn add_inventory_item_to_bank(character_id: CharacterEntityId)
pub(super) fn add_inventory_item_to_bank(character_id: CharacterEntityId)
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem)
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()), ItemStateError>> + Send + 'a>>
{
@ -479,29 +323,8 @@ fn add_inventory_item_to_bank(character_id: CharacterEntityId)
}
}
pub async fn deposit_item<'a, EG> (
item_state: &'a mut ItemState,
entity_gateway: &mut EG,
character: &CharacterEntity,
item_id: &ClientItemId,
amount: u32)
-> Result<(), ItemStateError>
where
EG: EntityGateway,
{
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_item_from_inventory(character.id, *item_id, amount))
.act(add_inventory_item_to_bank(character.id))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
Ok((transaction, result))
}).await
}
fn equip_inventory_item(character_id: CharacterEntityId, item_id: ClientItemId, equip_slot: u8)
pub(super) fn equip_inventory_item(character_id: CharacterEntityId, item_id: ClientItemId, equip_slot: u8)
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ())
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()), ItemStateError>> + Send + 'a>>
{
@ -517,29 +340,8 @@ fn equip_inventory_item(character_id: CharacterEntityId, item_id: ClientItemId,
}
}
pub async fn equip_item<'a, EG> (
item_state: &'a mut ItemState,
entity_gateway: &mut EG,
character: &CharacterEntity,
item_id: &ClientItemId,
equip_slot: u8,
) -> Result<(), ItemStateError>
where
EG: EntityGateway,
{
entity_gateway.with_transaction(|transaction| async move {
let item_state_proxy = ItemStateProxy::new(item_state);
let ((item_state_proxy, transaction), result) = ItemStateAction::default()
.act(equip_inventory_item(character.id, *item_id, equip_slot))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
Ok((transaction, result))
}).await
}
fn unequip_inventory_item(character_id: CharacterEntityId, item_id: ClientItemId)
pub(super) fn unequip_inventory_item(character_id: CharacterEntityId, item_id: ClientItemId)
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ())
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()), ItemStateError>> + Send + 'a>>
{
@ -555,28 +357,9 @@ fn unequip_inventory_item(character_id: CharacterEntityId, item_id: ClientItemId
}
}
pub async fn unequip_item<'a, EG> (
item_state: &'a mut ItemState,
entity_gateway: &mut EG,
character: &CharacterEntity,
item_id: &ClientItemId,
) -> Result<(), ItemStateError>
where
EG: EntityGateway,
{
entity_gateway.with_transaction(|transaction| async move {
let item_state_proxy = ItemStateProxy::new(item_state);
let ((item_state_proxy, transaction), result) = ItemStateAction::default()
.act(unequip_inventory_item(character.id, *item_id))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
Ok((transaction, result))
}).await
}
fn sort_inventory_items(character_id: CharacterEntityId, item_ids: Vec<ClientItemId>)
pub(super) fn sort_inventory_items(character_id: CharacterEntityId, item_ids: Vec<ClientItemId>)
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ())
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()), ItemStateError>> + Send + 'a>>
{
@ -593,28 +376,8 @@ fn sort_inventory_items(character_id: CharacterEntityId, item_ids: Vec<ClientIte
}
}
pub async fn sort_inventory<'a, EG> (
item_state: &'a mut ItemState,
entity_gateway: &mut EG,
character: &CharacterEntity,
item_ids: Vec<ClientItemId>,
) -> Result<(), ItemStateError>
where
EG: EntityGateway,
{
entity_gateway.with_transaction(|transaction| async move {
let item_state_proxy = ItemStateProxy::new(item_state);
let ((item_state_proxy, transaction), result) = ItemStateAction::default()
.act(sort_inventory_items(character.id, item_ids))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
Ok((transaction, result))
}).await
}
fn use_consumed_item(character: CharacterEntity)
pub(super) fn use_consumed_item(character: CharacterEntity)
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem)
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), CharacterEntity), ItemStateError>> + Send + 'a>>
{
@ -634,30 +397,8 @@ fn use_consumed_item(character: CharacterEntity)
}
}
pub async fn use_item<'a, EG> (
item_state: &'a mut ItemState,
entity_gateway: &mut EG,
character: &mut CharacterEntity,
item_id: &ClientItemId,
amount: u32,
) -> Result<(), ItemStateError>
where
EG: EntityGateway,
{
entity_gateway.with_transaction(|transaction| async move {
let item_state_proxy = ItemStateProxy::new(item_state);
let ((item_state_proxy, transaction), new_character) = ItemStateAction::default()
.act(take_item_from_inventory(character.id, *item_id, amount))
.act(use_consumed_item(character.clone()))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
*character = new_character;
Ok((transaction, ()))
}).await
}
fn feed_mag_item(character: CharacterEntity, mag_item_id: ClientItemId)
pub(super) fn feed_mag_item(character: CharacterEntity, mag_item_id: ClientItemId)
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem)
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), CharacterEntity), ItemStateError>> + Send + 'a>>
{
@ -704,30 +445,7 @@ fn feed_mag_item(character: CharacterEntity, mag_item_id: ClientItemId)
}
pub async fn feed_mag<'a, EG> (
item_state: &'a mut ItemState,
entity_gateway: &mut EG,
character: &CharacterEntity,
mag_item_id: &ClientItemId,
tool_item_id: &ClientItemId,
) -> Result<(), ItemStateError>
where
EG: EntityGateway,
{
entity_gateway.with_transaction(|transaction| async move {
let item_state_proxy = ItemStateProxy::new(item_state);
let ((item_state_proxy, transaction), _) = ItemStateAction::default()
.act(take_item_from_inventory(character.id, *tool_item_id, 1))
.act(feed_mag_item(character.clone(), *mag_item_id))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
Ok((transaction, ()))
}).await
}
fn add_bought_item_to_inventory<'a>(character_id: CharacterEntityId,
pub(super) fn add_bought_item_to_inventory<'a>(character_id: CharacterEntityId,
shop_item: &'a (dyn ShopItem + Send + Sync),
item_id: ClientItemId,
amount: u32)
@ -787,34 +505,8 @@ fn add_bought_item_to_inventory<'a>(character_id: CharacterEntityId,
}
}
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(bought_item_to_inventory_item)
//.act(add_item_to_inventory)
.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
}
fn sell_inventory_item<'a>(character_id: CharacterEntityId)
pub(super) fn sell_inventory_item<'a>(character_id: CharacterEntityId)
-> impl Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem)
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem), ItemStateError>> + Send + 'a>>
{
@ -836,27 +528,6 @@ fn sell_inventory_item<'a>(character_id: CharacterEntityId)
}
}
pub async fn sell_item<'a, EG> (
item_state: &'a mut ItemState,
entity_gateway: &mut EG,
character: &CharacterEntity,
item_id: ClientItemId,
amount: u32,
) -> Result<InventoryItem, ItemStateError>
where
EG: EntityGateway,
{
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_item_from_inventory(character.id, item_id, amount))
.act(sell_inventory_item(character.id))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
Ok((transaction, result))
}).await
}
#[async_recursion::async_recursion]
async fn iterate_inner<'a, I, O, T, F, FR>(state: (ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>),
@ -887,7 +558,7 @@ where
Ok((state, output))
}
pub fn iterate<'k, I, O, T, F, FR>(
pub(super) fn iterate<'k, I, O, T, F, FR>(
input: Vec<I>,
func: F)
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), T)
@ -939,7 +610,7 @@ where
Ok((state, output))
}
pub fn foreach<'k, O, T, F>(func: F)
pub(super) fn foreach<'k, O, T, F>(func: F)
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), Vec<T>)
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), Vec<O>), ItemStateError>> + Send + 'a>>
where
@ -960,7 +631,7 @@ where
}
}
fn insert<'a, T: Send + Clone + 'a>(element: T)
pub(super) fn insert<'a, T: Send + Clone + 'a>(element: T)
-> impl Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ())
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), T), ItemStateError>> + Send + 'a>>
{
@ -972,7 +643,7 @@ fn insert<'a, T: Send + Clone + 'a>(element: T)
}
}
fn add_item_to_inventory(character: CharacterEntity)
pub(super) fn add_item_to_inventory(character: CharacterEntity)
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem)
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem), ItemStateError>> + Send + 'a>> + Clone
{
@ -996,7 +667,7 @@ fn add_item_to_inventory(character: CharacterEntity)
}
}
fn record_trade(trade_id: TradeId, character_to: CharacterEntityId, character_from: CharacterEntityId)
pub(super) fn record_trade(trade_id: TradeId, character_to: CharacterEntityId, character_from: CharacterEntityId)
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), Vec<InventoryItem>)
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), Vec<InventoryItem>), ItemStateError>> + Send + 'a>> + Clone
{
@ -1019,7 +690,7 @@ fn record_trade(trade_id: TradeId, character_to: CharacterEntityId, character_fr
}
fn assign_new_item_id()
pub(super) fn assign_new_item_id()
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem)
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem), ItemStateError>> + Send + 'a>> + Clone
{
@ -1031,98 +702,8 @@ fn assign_new_item_id()
}
}
pub async fn trade_items<'a, EG> (
item_state: &'a mut ItemState,
entity_gateway: &mut EG,
p1: (&AreaClient, &CharacterEntity, &Vec<TradeItem>, Meseta),
p2: (&AreaClient, &CharacterEntity, &Vec<TradeItem>, Meseta))
-> Result<(Vec<InventoryItem>, Vec<InventoryItem>), ItemStateError>
where
EG: EntityGateway,
{
let p1_trade_items = p1.2
.iter()
.map(|item| {
match item {
TradeItem::Individual(item_id) => (*item_id, 1),
TradeItem::Stacked(item_id, amount) => (*item_id, *amount as u32),
}
})
.collect();
let p2_trade_items = p2.2
.iter()
.map(|item| {
match item {
TradeItem::Individual(item_id) => (*item_id, 1),
TradeItem::Stacked(item_id, amount) => (*item_id, *amount as u32),
}
})
.collect();
entity_gateway.with_transaction(|mut transaction| async move {
let p1_id = p1.1.id;
let p2_id = p2.1.id;
let trade = transaction.gateway().create_trade(&p1_id, &p2_id).await?;
let item_state_proxy = ItemStateProxy::new(item_state);
let ((item_state_proxy, transaction), p1_removed_items) = ItemStateAction::default()
.act(iterate(p1_trade_items, move |p1_trade_item| take_item_from_inventory(p1_id, p1_trade_item.0, p1_trade_item.1) ))
.act(foreach(assign_new_item_id()))
.commit((item_state_proxy, transaction))
.await?;
let ((item_state_proxy, transaction), p2_removed_items) = ItemStateAction::default()
.act(iterate(p2_trade_items, move |p2_trade_item| take_item_from_inventory(p2_id, p2_trade_item.0, p2_trade_item.1) ))
.act(foreach(assign_new_item_id()))
.commit((item_state_proxy, transaction))
.await?;
let ((item_state_proxy, transaction), p2_new_items) = ItemStateAction::default()
.act(insert(p1_removed_items))
.act(foreach(add_item_to_inventory(p2.1.clone())))
.act(record_trade(trade.id, p1_id, p2_id))
.commit((item_state_proxy, transaction))
.await?;
let ((item_state_proxy, transaction), p1_new_items) = ItemStateAction::default()
.act(insert(p2_removed_items))
.act(foreach(add_item_to_inventory(p1.1.clone())))
.act(record_trade(trade.id, p2_id, p1_id))
.commit((item_state_proxy, transaction))
.await?;
let ((item_state_proxy, transaction), _) = ItemStateAction::default()
.act(take_meseta_from_inventory(p1_id, p1.3.0))
.act(take_meseta_from_inventory(p2_id, p2.3.0))
.act(add_meseta_to_inventory(p1_id, p2.3.0))
.act(add_meseta_to_inventory(p2_id, p1.3.0))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
Ok((transaction, (p1_new_items, p2_new_items)))
}).await
}
pub async fn take_meseta<'a, EG> (
item_state: &'a mut ItemState,
entity_gateway: &mut EG,
character_id: &CharacterEntityId,
meseta: Meseta)
-> Result<(), ItemStateError>
where
EG: EntityGateway,
{
entity_gateway.with_transaction(|transaction| async move {
let item_state_proxy = ItemStateProxy::new(item_state);
let ((item_state_proxy, transaction), _) = ItemStateAction::default()
.act(take_meseta_from_inventory(*character_id, meseta.0))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
Ok((transaction, ()))
}).await
}
fn convert_item_drop_to_floor_item(character_id: CharacterEntityId, item_drop: ItemDrop)
pub(super) fn convert_item_drop_to_floor_item(character_id: CharacterEntityId, item_drop: ItemDrop)
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ())
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), FloorItem), ItemStateError>> + Send + 'a>> + Clone
{
@ -1219,7 +800,7 @@ fn convert_item_drop_to_floor_item(character_id: CharacterEntityId, item_drop: I
}
}
fn add_item_to_local_floor(character_id: CharacterEntityId)
pub(super) fn add_item_to_local_floor(character_id: CharacterEntityId)
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), FloorItem)
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), FloorItem), ItemStateError>> + Send + 'a>>
{
@ -1234,29 +815,7 @@ fn add_item_to_local_floor(character_id: CharacterEntityId)
}
}
pub async fn enemy_drops_item<'a, EG> (
item_state: &'a mut ItemState,
entity_gateway: &mut EG,
character_id: CharacterEntityId,
item_drop: ItemDrop)
-> Result<FloorItem, ItemStateError>
where
EG: EntityGateway,
{
entity_gateway.with_transaction(|transaction| async move {
let item_state_proxy = ItemStateProxy::new(item_state);
let ((item_state_proxy, transaction), floor_item) = ItemStateAction::default()
.act(convert_item_drop_to_floor_item(character_id, item_drop))
.act(add_item_to_local_floor(character_id))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
Ok((transaction, floor_item))
}).await
}
fn apply_modifier_to_inventory_item(modifier: ItemModifier)
pub(super) fn apply_modifier_to_inventory_item(modifier: ItemModifier)
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem)
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem), ItemStateError>> + Send + 'a>>
{
@ -1276,7 +835,7 @@ fn apply_modifier_to_inventory_item(modifier: ItemModifier)
}
}
fn as_individual_item()
pub(super) fn as_individual_item()
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem)
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), IndividualItemDetail), ItemStateError>> + Send + 'a>>
{
@ -1291,29 +850,3 @@ fn as_individual_item()
})
}
}
pub async fn apply_modifier<'a, EG> (
item_state: &'a mut ItemState,
entity_gateway: &mut EG,
character: &CharacterEntity,
item_id: ClientItemId,
modifier: ItemModifier)
-> Result<IndividualItemDetail, ItemStateError>
where
EG: EntityGateway,
{
entity_gateway.with_transaction(|transaction| async move {
let item_state_proxy = ItemStateProxy::new(item_state);
let ((item_state_proxy, transaction), item) = ItemStateAction::default()
.act(take_item_from_inventory(character.id, item_id, 1))
.act(apply_modifier_to_inventory_item(modifier))
.act(add_item_to_inventory(character.clone()))
.act(as_individual_item())
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
Ok((transaction, item))
}).await
}

View File

@ -5,7 +5,8 @@ use crate::entity::character::CharacterEntity;
use crate::entity::item::mag::{MagCell, MagCellError};
use crate::entity::item::tool::ToolType;
use crate::entity::item::{ItemDetail, ItemEntityId};
use crate::ship::items::state::{ItemStateProxy, InventoryItem, InventoryItemDetail, ItemStateError};
use crate::ship::items::state::{ItemStateProxy, ItemStateError};
use crate::ship::items::inventory::{InventoryItem, InventoryItemDetail};
#[derive(Error, Debug)]

340
src/ship/items/bank.rs Normal file
View File

@ -0,0 +1,340 @@
use std::cmp::Ordering;
use libpso::character::character;
use crate::ship::items::ClientItemId;
use crate::entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, BankEntity, BankItemEntity, BankName};
use std::future::Future;
use crate::entity::character::CharacterEntityId;
use crate::ship::items::state::ItemStateError;
use crate::ship::items::state::{IndividualItemDetail, StackedItemDetail, AddItemResult};
use crate::ship::items::inventory::{InventoryItem, InventoryItemDetail};
#[derive(thiserror::Error, Debug)]
pub enum BankError {
#[error("bank full")]
BankFull,
#[error("stack full")]
StackFull,
#[error("meseta full")]
MesetaFull,
}
#[derive(Clone, Debug)]
pub enum BankItemDetail {
Individual(IndividualItemDetail),
Stacked(StackedItemDetail),
}
impl BankItemDetail {
fn stacked_mut(&mut self) -> Option<&mut StackedItemDetail> {
match self {
BankItemDetail::Stacked(sitem) => Some(sitem),
_ => None,
}
}
pub fn as_client_bytes(&self) -> [u8; 16] {
match self {
BankItemDetail::Individual(item) => {
match &item.item {
ItemDetail::Weapon(w) => w.as_bytes(),
ItemDetail::Armor(a) => a.as_bytes(),
ItemDetail::Shield(s) => s.as_bytes(),
ItemDetail::Unit(u) => u.as_bytes(),
ItemDetail::Tool(t) => t.as_individual_bytes(),
ItemDetail::TechniqueDisk(d) => d.as_bytes(),
ItemDetail::Mag(m) => m.as_bytes(),
ItemDetail::ESWeapon(e) => e.as_bytes(),
}
},
BankItemDetail::Stacked(item) => {
item.tool.as_stacked_bytes(item.entity_ids.len())
},
}
}
}
#[derive(Clone, Debug)]
pub struct BankItem {
pub item_id: ClientItemId,
pub item: BankItemDetail,
}
impl BankItem {
pub async fn with_entity_id<F, Fut, T>(&self, mut param: T, mut func: F) -> Result<T, ItemStateError>
where
F: FnMut(T, ItemEntityId) -> Fut,
Fut: Future<Output=Result<T, ItemStateError>>,
{
match &self.item {
BankItemDetail::Individual(individual_item) => {
param = func(param, individual_item.entity_id).await?;
},
BankItemDetail::Stacked(stacked_item) => {
for entity_id in &stacked_item.entity_ids {
param = func(param, *entity_id).await?;
}
}
}
Ok(param)
}
}
#[derive(Clone, Debug)]
pub struct Bank(Vec<BankItem>);
impl Bank {
pub fn new(items: Vec<BankItem>) -> Bank {
Bank(items)
}
}
#[derive(Clone, Debug)]
pub struct BankState {
pub character_id: CharacterEntityId,
pub item_id_counter: u32,
pub name: BankName,
pub bank: Bank,
pub meseta: Meseta,
}
impl BankState {
pub fn new(character_id: CharacterEntityId, name: BankName, mut bank: Bank, meseta: Meseta) -> BankState {
bank.0.sort();
BankState {
character_id,
item_id_counter: 0,
name,
bank,
meseta,
}
}
pub fn count(&self) -> usize {
self.bank.0.len()
}
pub fn initialize_item_ids(&mut self, base_item_id: u32) {
for (i, item) in self.bank.0.iter_mut().enumerate() {
item.item_id = ClientItemId(base_item_id + i as u32);
}
self.item_id_counter = base_item_id + self.bank.0.len() as u32;
}
pub fn add_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> {
if self.meseta.0 + amount > 999999 {
return Err(ItemStateError::FullOfMeseta)
}
self.meseta.0 += amount;
Ok(())
}
pub fn remove_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> {
if amount > self.meseta.0 {
return Err(ItemStateError::InvalidMesetaRemoval(amount))
}
self.meseta.0 -= amount;
Ok(())
}
pub fn add_inventory_item(&mut self, item: InventoryItem) -> Result<AddItemResult, BankError> {
match item.item {
InventoryItemDetail::Individual(iitem) => {
if self.bank.0.len() >= 30 {
Err(BankError::BankFull)
}
else {
self.bank.0.push(BankItem {
item_id: item.item_id,
item: BankItemDetail::Individual(iitem)
});
self.bank.0.sort();
Ok(AddItemResult::NewItem)
}
},
InventoryItemDetail::Stacked(sitem) => {
let existing_stack = self.bank.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(BankError::StackFull)
}
else {
existing_stack.entity_ids.append(&mut sitem.entity_ids.clone());
Ok(AddItemResult::AddToStack)
}
},
None => {
if self.bank.0.len() >= 30 {
Err(BankError::BankFull)
}
else {
self.bank.0.push(BankItem {
item_id: item.item_id,
item: BankItemDetail::Stacked(sitem)
});
self.bank.0.sort();
Ok(AddItemResult::NewItem)
}
}
}
}
}
}
pub fn take_item(&mut self, item_id: &ClientItemId, amount: u32) -> Option<BankItem> {
let idx = self.bank.0
.iter()
.position(|i| i.item_id == *item_id)?;
match &mut self.bank.0[idx].item {
BankItemDetail::Individual(_individual_item) => {
Some(self.bank.0.remove(idx))
},
BankItemDetail::Stacked(stacked_item) => {
let remove_all = match stacked_item.entity_ids.len().cmp(&(amount as usize)) {
Ordering::Equal => true,
Ordering::Greater => false,
Ordering::Less => return None,
};
if remove_all {
Some(self.bank.0.remove(idx))
}
else {
let entity_ids = stacked_item.entity_ids.drain(..(amount as usize)).collect();
self.item_id_counter += 1;
Some(BankItem {
item_id: ClientItemId(self.item_id_counter),
item: BankItemDetail::Stacked(StackedItemDetail {
entity_ids,
tool: stacked_item.tool,
})})
}
}
}
}
pub fn as_client_bank_items(&self) -> character::Bank {
self.bank.0.iter()
.enumerate()
.fold(character::Bank::default(), |mut bank, (slot, item)| {
bank.item_count = (slot + 1) as u32;
let bytes = item.item.as_client_bytes();
bank.items[slot].data1.copy_from_slice(&bytes[0..12]);
bank.items[slot].data2.copy_from_slice(&bytes[12..16]);
bank.items[slot].item_id = item.item_id.0;
bank
})
}
pub fn as_client_bank_request(&self) -> Vec<character::BankItem> {
self.bank.0.iter()
.map(|item| {
let bytes = item.item.as_client_bytes();
let mut data1 = [0; 12];
let mut data2 = [0; 4];
data1.copy_from_slice(&bytes[0..12]);
data2.copy_from_slice(&bytes[12..16]);
let amount = match &item.item {
BankItemDetail::Individual(_individual_bank_item) => {
1
},
BankItemDetail::Stacked(stacked_bank_item) => {
stacked_bank_item.count()
},
};
character::BankItem {
data1,
data2,
item_id: item.item_id.0,
amount: amount as u16,
flags: 1,
}
})
.collect()
}
pub fn as_bank_entity(&self) -> BankEntity {
BankEntity {
items: self.bank.0.iter()
.map(|item| {
match &item.item {
BankItemDetail::Individual(item) => {
BankItemEntity::Individual(ItemEntity {
id: item.entity_id,
item: item.item.clone(),
})
},
BankItemDetail::Stacked(items) => {
BankItemEntity::Stacked(items.entity_ids.iter()
.map(|id| {
ItemEntity {
id: *id,
item: ItemDetail::Tool(items.tool)
}
})
.collect())
},
}
})
.collect()
}
}
}
impl std::cmp::PartialEq for BankItem {
fn eq(&self, other: &BankItem) -> bool {
let mut self_bytes = [0u8; 4];
let mut other_bytes = [0u8; 4];
self_bytes.copy_from_slice(&self.item.as_client_bytes()[0..4]);
other_bytes.copy_from_slice(&other.item.as_client_bytes()[0..4]);
let self_value = u32::from_be_bytes(self_bytes);
let other_value = u32::from_be_bytes(other_bytes);
self_value.eq(&other_value)
}
}
impl std::cmp::Eq for BankItem {}
impl std::cmp::PartialOrd for BankItem {
fn partial_cmp(&self, other: &BankItem) -> Option<std::cmp::Ordering> {
let mut self_bytes = [0u8; 4];
let mut other_bytes = [0u8; 4];
self_bytes.copy_from_slice(&self.item.as_client_bytes()[0..4]);
other_bytes.copy_from_slice(&other.item.as_client_bytes()[0..4]);
let self_value = u32::from_be_bytes(self_bytes);
let other_value = u32::from_be_bytes(other_bytes);
self_value.partial_cmp(&other_value)
}
}
impl std::cmp::Ord for BankItem {
fn cmp(&self, other: &BankItem) -> std::cmp::Ordering {
let mut self_bytes = [0u8; 4];
let mut other_bytes = [0u8; 4];
self_bytes.copy_from_slice(&self.item.as_client_bytes()[0..4]);
other_bytes.copy_from_slice(&other.item.as_client_bytes()[0..4]);
let self_value = u32::from_le_bytes(self_bytes);
let other_value = u32::from_le_bytes(other_bytes);
self_value.cmp(&other_value)
}
}

139
src/ship/items/floor.rs Normal file
View File

@ -0,0 +1,139 @@
use crate::ship::items::ClientItemId;
use crate::entity::item::{Meseta, ItemEntityId, ItemDetail};
use std::future::Future;
use crate::ship::map::MapArea;
use crate::entity::character::CharacterEntityId;
use crate::entity::item::mag::Mag;
use crate::ship::items::state::ItemStateError;
use crate::ship::items::state::{IndividualItemDetail, StackedItemDetail};
use crate::ship::items::inventory::{InventoryItem, InventoryItemDetail};
pub enum FloorType {
Local,
Shared,
}
#[derive(Debug, Clone)]
pub enum FloorItemDetail {
Individual(IndividualItemDetail),
Stacked(StackedItemDetail),
Meseta(Meseta),
}
#[derive(Debug, Clone)]
pub struct FloorItem {
pub item_id: ClientItemId,
pub item: FloorItemDetail,
pub map_area: MapArea,
pub x: f32,
pub y: f32,
pub z: f32,
}
impl FloorItem {
pub async fn with_entity_id<F, Fut, T>(&self, mut param: T, mut func: F) -> Result<T, ItemStateError>
where
F: FnMut(T, ItemEntityId) -> Fut,
Fut: Future<Output=Result<T, ItemStateError>>,
{
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) => {},
}
Ok(param)
}
pub async fn with_mag<F, Fut, T>(&self, mut param: T, mut func: F) -> Result<T, ItemStateError>
where
F: FnMut(T, ItemEntityId, Mag) -> Fut,
Fut: Future<Output=Result<T, ItemStateError>>,
{
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?;
}
}
Ok(param)
}
pub fn as_client_bytes(&self) -> [u8; 16] {
match &self.item {
FloorItemDetail::Individual(individual_floor_item) => {
individual_floor_item.item.as_client_bytes()
},
FloorItemDetail::Stacked(stacked_floor_item) => {
stacked_floor_item.tool.as_stacked_bytes(stacked_floor_item.entity_ids.len())
},
FloorItemDetail::Meseta(meseta_floor_item) => {
meseta_floor_item.as_bytes()
}
}
}
}
#[derive(Debug, Clone, Default)]
pub struct LocalFloor(pub Vec<FloorItem>);
#[derive(Debug, Clone, Default)]
pub struct SharedFloor(pub Vec<FloorItem>);
#[derive(Debug)]
pub struct FloorState {
pub character_id: CharacterEntityId,
pub local: LocalFloor,
pub shared: SharedFloor,
}
impl FloorState {
pub fn take_item(&mut self, item_id: &ClientItemId) -> Option<FloorItem> {
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 fn add_inventory_item(&mut self, inventory_item: InventoryItem, map_area: MapArea, position: (f32, f32, f32)) -> &FloorItem {
let floor_item = FloorItem {
item_id: inventory_item.item_id,
item: match inventory_item.item {
InventoryItemDetail::Individual(individual_item) => FloorItemDetail::Individual(individual_item),
InventoryItemDetail::Stacked(stacked_item) => FloorItemDetail::Stacked(stacked_item),
},
map_area,
x: position.0,
y: position.1,
z: position.2,
};
self.shared.0.push(floor_item);
&self.shared.0[self.shared.0.len()-1]
}
pub fn add_shared_item(&mut self, floor_item: FloorItem) -> &FloorItem {
self.shared.0.push(floor_item);
&self.shared.0[self.shared.0.len()-1]
}
pub fn add_local_item(&mut self, floor_item: FloorItem) -> &FloorItem {
self.local.0.push(floor_item);
&self.local.0[self.local.0.len()-1]
}
}

529
src/ship/items/inventory.rs Normal file
View File

@ -0,0 +1,529 @@
use std::cmp::Ordering;
use libpso::character::character;
use crate::ship::items::ClientItemId;
use crate::entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, InventoryEntity, InventoryItemEntity, EquippedEntity};
use std::future::Future;
use crate::entity::character::CharacterEntityId;
use crate::entity::item::tool::ToolType;
use crate::entity::item::mag::Mag;
use crate::ship::shops::{ShopItem, ArmorShopItem, ToolShopItem, WeaponShopItem};
use crate::ship::items::state::ItemStateError;
use crate::ship::items::state::{IndividualItemDetail, StackedItemDetail, AddItemResult};
use crate::ship::items::floor::{FloorItem, FloorItemDetail};
#[derive(Clone, Debug)]
pub enum InventoryItemDetail {
Individual(IndividualItemDetail),
Stacked(StackedItemDetail),
}
impl InventoryItemDetail {
// TODO: rename as_stacked for consistency
pub fn stacked(&self) -> Option<&StackedItemDetail> {
match self {
InventoryItemDetail::Stacked(sitem) => Some(sitem),
_ => None,
}
}
// TODO: rename as_stacked_mut for consistency
pub fn stacked_mut(&mut self) -> Option<&mut StackedItemDetail> {
match self {
InventoryItemDetail::Stacked(sitem) => Some(sitem),
_ => None,
}
}
pub fn as_individual(&self) -> Option<&IndividualItemDetail> {
match self {
InventoryItemDetail::Individual(iitem) => Some(iitem),
_ => None,
}
}
pub fn as_individual_mut(&mut self) -> Option<&mut IndividualItemDetail> {
match self {
InventoryItemDetail::Individual(iitem) => Some(iitem),
_ => None,
}
}
pub fn as_client_bytes(&self) -> [u8; 16] {
match self {
InventoryItemDetail::Individual(item) => {
item.as_client_bytes()
},
InventoryItemDetail::Stacked(item) => {
item.tool.as_stacked_bytes(item.entity_ids.len())
},
}
}
// TODO: this should probably go somewhere a bit more fundamental like ItemDetail
pub fn sell_price(&self) -> Result<u32, ItemStateError> {
match self {
InventoryItemDetail::Individual(individual_item) => {
match &individual_item.item {
// TODO: can wrapped items be sold?
ItemDetail::Weapon(w) => {
if !w.tekked {
return Ok(1u32)
}
if w.is_rare_item() {
return Ok(10u32)
}
Ok((WeaponShopItem::from(w).price() / 8) as u32)
},
ItemDetail::Armor(a) => {
if a.is_rare_item() {
return Ok(10u32)
}
Ok((ArmorShopItem::from(a).price() / 8) as u32)
},
ItemDetail::Shield(s) => {
if s.is_rare_item() {
return Ok(10u32)
}
Ok((ArmorShopItem::from(s).price() / 8) as u32)
},
ItemDetail::Unit(u) => {
if u.is_rare_item() {
return Ok(10u32)
}
Ok((ArmorShopItem::from(u).price() / 8) as u32)
},
ItemDetail::Tool(t) => {
if !matches!(t.tool, ToolType::PhotonDrop | ToolType::PhotonSphere | ToolType::PhotonCrystal) && t.is_rare_item() {
return Ok(10u32)
}
Ok((ToolShopItem::from(t).price() / 8) as u32)
},
ItemDetail::TechniqueDisk(d) => {
Ok((ToolShopItem::from(d).price() / 8) as u32)
},
ItemDetail::Mag(_m) => {
Err(ItemStateError::ItemNotSellable)
},
ItemDetail::ESWeapon(_e) => {
Ok(10u32)
},
}
},
// the number of stacked items sold is handled by the caller. this is just the price of 1
InventoryItemDetail::Stacked(stacked_item) => {
Ok(((ToolShopItem::from(&stacked_item.tool).price() / 8) as u32) * stacked_item.count() as u32)
},
}
}
}
#[derive(Clone, Debug)]
pub struct InventoryItem {
pub item_id: ClientItemId,
pub item: InventoryItemDetail,
}
impl InventoryItem {
pub async fn with_entity_id<F, Fut, T>(&self, mut param: T, mut func: F) -> Result<T, ItemStateError>
where
F: FnMut(T, ItemEntityId) -> Fut,
Fut: Future<Output=Result<T, ItemStateError>>,
{
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?;
}
}
}
Ok(param)
}
pub async fn with_mag<F, Fut, T>(&self, mut param: T, mut func: F) -> Result<T, ItemStateError>
where
F: FnMut(T, ItemEntityId, Mag) -> Fut,
Fut: Future<Output=Result<T, ItemStateError>>,
{
if let InventoryItemDetail::Individual(individual_item) = &self.item {
if let ItemDetail::Mag(mag) = &individual_item.item {
param = func(param, individual_item.entity_id, mag.clone()).await?;
}
}
Ok(param)
}
}
#[derive(Clone, Debug)]
pub struct Inventory(Vec<InventoryItem>);
impl Inventory {
pub fn new(items: Vec<InventoryItem>) -> Inventory {
Inventory(items)
}
}
#[derive(thiserror::Error, Debug)]
pub enum InventoryError {
#[error("inventory full")]
InventoryFull,
#[error("stack full")]
StackFull,
#[error("meseta full")]
MesetaFull,
}
#[derive(Clone)]
pub struct InventoryState {
pub character_id: CharacterEntityId,
pub item_id_counter: u32,
pub inventory: Inventory,
pub equipped: EquippedEntity,
pub meseta: Meseta,
}
impl InventoryState {
pub fn initialize_item_ids(&mut self, base_item_id: u32) {
for (i, item) in self.inventory.0.iter_mut().enumerate() {
item.item_id = ClientItemId(base_item_id + i as u32);
}
self.item_id_counter = base_item_id + self.inventory.0.len() as u32 + 1;
}
pub fn new_item_id(&mut self) -> ClientItemId {
self.item_id_counter += 1;
ClientItemId(self.item_id_counter)
}
pub fn count(&self) -> usize {
self.inventory.0.len()
}
pub fn add_floor_item(&mut self, item: FloorItem) -> Result<AddItemResult, InventoryError> {
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 fn add_item(&mut self, item: InventoryItem) -> Result<(AddItemResult, InventoryItem), InventoryError> {
match &item.item {
InventoryItemDetail::Individual(_) => {
if self.inventory.0.len() >= 30 {
Err(InventoryError::InventoryFull)
}
else {
self.inventory.0.push(item);
Ok((
AddItemResult::NewItem,
self.inventory.0
.last()
.unwrap()
.clone()
))
}
},
InventoryItemDetail::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,
self.inventory.0[self.inventory.0
.iter()
.filter_map(|item| item.item.stacked())
.position(|item| item.tool == sitem.tool)
.unwrap()]
.clone()
))
}
},
None => {
if self.inventory.0.len() >= 30 {
Err(InventoryError::InventoryFull)
}
else {
self.inventory.0.push(item);
Ok((
AddItemResult::NewItem,
self.inventory.0
.last()
.unwrap()
.clone()
))
}
}
}
}
}
}
pub fn take_item(&mut self, item_id: &ClientItemId, amount: u32) -> Option<InventoryItem> {
let idx = self.inventory.0
.iter()
.position(|i| i.item_id == *item_id)?;
match &mut self.inventory.0[idx].item {
InventoryItemDetail::Individual(_individual_item) => {
Some(self.inventory.0.remove(idx))
},
InventoryItemDetail::Stacked(stacked_item) => {
let remove_all = (amount == 0) || match stacked_item.entity_ids.len().cmp(&(amount as usize)) {
Ordering::Equal => true,
Ordering::Greater => false,
Ordering::Less => return None,
};
if remove_all {
Some(self.inventory.0.remove(idx))
}
else {
let entity_ids = stacked_item.entity_ids.drain(..(amount as usize)).collect();
self.item_id_counter += 1;
Some(InventoryItem {
item_id: ClientItemId(self.item_id_counter),
item: InventoryItemDetail::Stacked(StackedItemDetail {
entity_ids,
tool: stacked_item.tool,
})})
}
}
}
}
// TODO: rename get_item_by_client_id
pub fn get_by_client_id(&self, item_id: &ClientItemId) -> Option<&InventoryItem> {
self.inventory.0
.iter()
.find(|i| i.item_id == *item_id)
}
pub fn get_by_client_id_mut(&mut self, item_id: &ClientItemId) -> Option<&mut InventoryItem> {
self.inventory.0
.iter_mut()
.find(|i| i.item_id == *item_id)
}
pub fn add_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> {
if self.meseta.0 == 999999 {
return Err(ItemStateError::FullOfMeseta)
}
self.meseta.0 = std::cmp::min(self.meseta.0 + amount, 999999);
Ok(())
}
pub fn add_meseta_no_overflow(&mut self, amount: u32) -> Result<(), ItemStateError> {
if self.meseta.0 + amount > 999999 {
return Err(ItemStateError::FullOfMeseta)
}
self.meseta.0 += amount;
Ok(())
}
pub fn remove_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> {
if amount > self.meseta.0 {
return Err(ItemStateError::InvalidMesetaRemoval(amount))
}
self.meseta.0 -= amount;
Ok(())
}
pub fn equip(&mut self, item_id: &ClientItemId, equip_slot: u8) {
for item in &self.inventory.0 {
if let InventoryItemDetail::Individual(inventory_item) = &item.item {
if item.item_id == *item_id {
match inventory_item.item {
ItemDetail::Weapon(_) => self.equipped.weapon = Some(inventory_item.entity_id),
ItemDetail::Armor(_) => self.equipped.armor = Some(inventory_item.entity_id),
ItemDetail::Shield(_) => self.equipped.shield = Some(inventory_item.entity_id),
ItemDetail::Unit(_) => {
if let Some(unit) = self.equipped.unit.get_mut(equip_slot as usize) {
*unit = Some(inventory_item.entity_id)
}
}
ItemDetail::Mag(_) => self.equipped.mag = Some(inventory_item.entity_id),
_ => {}
}
}
}
}
}
pub fn unequip(&mut self, item_id: &ClientItemId) {
for item in &self.inventory.0 {
if let InventoryItemDetail::Individual(inventory_item) = &item.item {
if item.item_id == *item_id {
match inventory_item.item {
ItemDetail::Weapon(_) => self.equipped.weapon = None,
ItemDetail::Armor(_) => {
self.equipped.armor = None;
self.equipped.unit = [None; 4];
}
ItemDetail::Shield(_) => self.equipped.shield = None,
ItemDetail::Unit(_) => {
for unit in self.equipped.unit.iter_mut() {
if *unit == Some(inventory_item.entity_id) {
*unit = None
}
}
}
ItemDetail::Mag(_) => self.equipped.mag = Some(inventory_item.entity_id),
_ => {}
}
}
}
}
}
pub fn equipped_mag_mut(&mut self) -> Option<(ItemEntityId, &mut Mag)> {
let mag_id = self.equipped.mag?;
self.inventory.0
.iter_mut()
.filter_map(|i| {
let individual = i.item.as_individual_mut()?;
let entity_id = individual.entity_id;
Some((entity_id, individual.as_mag_mut()?))
})
.find(|(entity_id, _)| *entity_id == mag_id)
}
pub fn sort(&mut self, item_ids: &[ClientItemId]) {
self.inventory.0.sort_by(|a, b| {
let a_index = item_ids.iter().position(|item_id| *item_id == a.item_id);
let b_index = item_ids.iter().position(|item_id| *item_id == b.item_id);
match (a_index, b_index) {
(Some(a_index), Some(b_index)) => {
a_index.cmp(&b_index)
},
_ => Ordering::Equal
}
});
}
pub fn as_inventory_entity(&self, _character_id: &CharacterEntityId) -> InventoryEntity {
InventoryEntity {
items: self.inventory.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()
}
}
pub fn as_equipped_entity(&self) -> EquippedEntity {
self.equipped.clone()
}
pub fn as_client_inventory_items(&self) -> [character::InventoryItem; 30] {
self.inventory.0.iter()
.enumerate()
.fold([character::InventoryItem::default(); 30], |mut inventory, (slot, item)| {
let bytes = item.item.as_client_bytes();
inventory[slot].data1.copy_from_slice(&bytes[0..12]);
inventory[slot].data2.copy_from_slice(&bytes[12..16]);
inventory[slot].item_id = item.item_id.0;
inventory[slot].equipped = 0;
inventory[slot].flags = 0;
if let InventoryItemDetail::Individual(individual_item) = &item.item {
if self.equipped.is_equipped(&individual_item.entity_id) {
if let ItemDetail::Unit(_) = individual_item.item {
inventory[slot].data1[4] = self.equipped.unit.iter()
.enumerate()
.find(|(_, u_id)| **u_id == Some(individual_item.entity_id))
.map(|(a, _)| a)
.unwrap_or(0) as u8
}
inventory[slot].equipped = 1;
inventory[slot].flags |= 8;
}
}
inventory
})
}
}

View File

@ -0,0 +1,137 @@
use std::future::Future;
#[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, S, E> {
_t: std::marker::PhantomData<T>,
_s: std::marker::PhantomData<S>,
_e: std::marker::PhantomData<E>,
}
impl<T, S, E> Default for ItemStateAction<T, S, E> {
fn default() -> ItemStateAction<T, S, E> {
ItemStateAction {
_t: std::marker::PhantomData,
_s: std::marker::PhantomData,
_e: std::marker::PhantomData,
}
}
}
impl<T, S, E> ItemStateAction<T, S, E>
where
T: Send + Sync,
S: Send + Sync,
E: Send + Sync,
{
pub fn act<O, F, Fut>(self, f: F) -> ItemActionStage<O, ItemStateAction<T, S, E>, F, Fut, S, E>
where
F: Fn(S, ()) -> Fut + Send + Sync,
Fut: Future<Output=Result<(S, O), E>> + Send
{
ItemActionStage {
_s: Default::default(),
_e: std::marker::PhantomData,
prev: self,
actionf: f,
}
}
}
pub struct ItemActionStage<O, P, F, Fut, S, E>
where
P: ItemAction,
F: Fn(S, P::Output) -> Fut + Send + Sync,
Fut: Future<Output=Result<(S, O) , E>> + Send,
{
_s: std::marker::PhantomData<S>,
_e: std::marker::PhantomData<E>,
prev: P,
actionf: F,
}
#[async_trait::async_trait]
impl<O, P: ItemAction, F, Fut, S, E> ItemAction for ItemActionStage<O, P, F, Fut, S, E>
where
P: ItemAction + ItemAction<Start = S, Error = E> + Send + Sync,
F: Fn(S, P::Output) -> Fut + Send + Sync,
Fut: Future<Output=Result<(S, O), E>> + 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<O, P: ItemAction, F, Fut, S, E> ItemActionStage<O, P, F, Fut, S, E>
where
P: ItemAction<Start = S, Error = E> + Send + Sync,
F: Fn(S, P::Output) -> Fut + Send + Sync,
Fut: Future<Output=Result<(S, O), E>> + 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<O2, G, GFut>(self, g: G) -> ItemActionStage<O2, ItemActionStage<O, P, F, Fut, S, E>, G, GFut, S, E>
where
S: Send + Sync,
G: Fn(S, <ItemActionStage<O, P, F, Fut, S, E> as ItemAction>::Output) -> GFut + Send + Sync,
GFut: Future<Output=Result<(S, O2), E>> + Send,
O2: Send + Sync,
{
ItemActionStage {
_s: Default::default(),
_e: Default::default(),
prev: self,
actionf: g,
}
}
}
#[async_trait::async_trait]
impl<T, S, E> ItemAction for ItemStateAction<T, S, E>
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, ()))
}
}

View File

@ -1,9 +1,11 @@
pub mod state;
pub mod actions;
pub mod apply_item;
use serde::{Serialize, Deserialize};
pub mod itemstateaction;
pub mod inventory;
pub mod floor;
pub mod bank;
pub mod tasks;
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Serialize, Deserialize, derive_more::Display)]
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize, derive_more::Display)]
pub struct ClientItemId(pub u32);

File diff suppressed because it is too large Load Diff

500
src/ship/items/tasks.rs Normal file
View File

@ -0,0 +1,500 @@
use crate::ship::items::ClientItemId;
use crate::entity::item::Meseta;
use crate::ship::map::MapArea;
use crate::entity::character::{CharacterEntity, CharacterEntityId};
use crate::entity::gateway::EntityGateway;
use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateError, IndividualItemDetail};
use crate::ship::items::itemstateaction::{ItemStateAction, ItemAction};
use crate::ship::items::inventory::InventoryItem;
use crate::ship::items::floor::FloorItem;
use crate::entity::item::ItemModifier;
use crate::ship::shops::ShopItem;
use crate::ship::trade::TradeItem;
use crate::ship::location::AreaClient;
use crate::ship::drops::ItemDrop;
use crate::ship::items::actions;
pub async fn pick_up_item<EG>(
item_state: &mut ItemState,
entity_gateway: &mut EG,
character: &CharacterEntity,
item_id: &ClientItemId)
-> Result<actions::TriggerCreateItem, ItemStateError>
where
EG: EntityGateway,
{
entity_gateway.with_transaction(|transaction| async move {
let item_state_proxy = ItemStateProxy::new(item_state);
let ((item_state_proxy, transaction), result) = ItemStateAction::default()
.act(actions::take_item_from_floor(character.id, *item_id))
.act(actions::add_floor_item_to_inventory(character))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
Ok((transaction, result))
}).await
}
pub async fn drop_item<EG>(
item_state: &mut ItemState,
entity_gateway: &mut EG,
character: &CharacterEntity,
item_id: &ClientItemId,
map_area: MapArea,
drop_position: (f32, f32, f32))
-> Result<FloorItem, ItemStateError>
where
EG: EntityGateway,
{
entity_gateway.with_transaction(|transaction| async move {
let item_state_proxy = ItemStateProxy::new(item_state);
let ((item_state_proxy, transaction), result) = ItemStateAction::default()
.act(actions::take_item_from_inventory(character.id, *item_id, 0))
.act(actions::add_inventory_item_to_shared_floor(character.id, map_area, drop_position))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
Ok((transaction, result))
}).await
}
pub async fn drop_partial_item<'a, EG>(
item_state: &'a mut ItemState,
entity_gateway: &mut EG,
character: &CharacterEntity,
item_id: &ClientItemId,
map_area: MapArea,
drop_position: (f32, f32),
amount: u32)
-> Result<FloorItem, ItemStateError>
where
EG: EntityGateway,
{
entity_gateway.with_transaction(|transaction| async move {
let item_state_proxy = ItemStateProxy::new(item_state);
let ((item_state_proxy, transaction), result) = ItemStateAction::default()
.act(actions::take_item_from_inventory(character.id, *item_id, amount))
.act(actions::add_inventory_item_to_shared_floor(character.id, map_area, (drop_position.0, 0.0, drop_position.1)))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
Ok((transaction, result))
}).await
}
pub async fn drop_meseta<'a, EG>(
item_state: &'a mut ItemState,
entity_gateway: &mut EG,
character: &CharacterEntity,
map_area: MapArea,
drop_position: (f32, f32),
amount: u32)
-> Result<FloorItem, ItemStateError>
where
EG: EntityGateway,
{
entity_gateway.with_transaction(|transaction| async move {
let item_state_proxy = ItemStateProxy::new(item_state);
let ((item_state_proxy, transaction), result) = ItemStateAction::default()
.act(actions::take_meseta_from_inventory(character.id, amount))
.act(actions::add_meseta_to_shared_floor(character.id, amount, map_area, drop_position))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
Ok((transaction, result))
}).await
}
pub async fn withdraw_meseta<'a, EG>(
item_state: &'a mut ItemState,
entity_gateway: &mut EG,
character: &CharacterEntity,
amount: u32)
-> Result<(), ItemStateError>
where
EG: EntityGateway,
{
entity_gateway.with_transaction(|transaction| async move {
let item_state_proxy = ItemStateProxy::new(item_state);
let ((item_state_proxy, transaction), result) = ItemStateAction::default()
.act(actions::take_meseta_from_bank(character.id, amount))
.act(actions::add_meseta_from_bank_to_inventory(character.id, amount))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
Ok((transaction, result))
}).await
}
pub async fn deposit_meseta<'a, EG>(
item_state: &'a mut ItemState,
entity_gateway: &mut EG,
character: &CharacterEntity,
amount: u32)
-> Result<(), ItemStateError>
where
EG: EntityGateway,
{
entity_gateway.with_transaction(|transaction| async move {
let item_state_proxy = ItemStateProxy::new(item_state);
let ((item_state_proxy, transaction), _) = ItemStateAction::default()
.act(actions::take_meseta_from_inventory(character.id, amount))
.act(actions::add_meseta_to_bank(character.id, amount))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
Ok((transaction, ()))
}).await
}
pub async fn withdraw_item<'a, EG>(
item_state: &'a mut ItemState,
entity_gateway: &mut EG,
character: &CharacterEntity,
item_id: &ClientItemId,
amount: u32)
-> Result<InventoryItem, ItemStateError>
where
EG: EntityGateway,
{
entity_gateway.with_transaction(|transaction| async move {
let item_state_proxy = ItemStateProxy::new(item_state);
let ((item_state_proxy, transaction), result) = ItemStateAction::default()
.act(actions::take_item_from_bank(character.id, *item_id, amount))
//.act(bank_item_to_inventory_item)
//.act(add_item_to_inventory)
.act(actions::add_bank_item_to_inventory(character))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
Ok((transaction, result))
}).await
}
pub async fn deposit_item<'a, EG> (
item_state: &'a mut ItemState,
entity_gateway: &mut EG,
character: &CharacterEntity,
item_id: &ClientItemId,
amount: u32)
-> Result<(), ItemStateError>
where
EG: EntityGateway,
{
entity_gateway.with_transaction(|transaction| async move {
let item_state_proxy = ItemStateProxy::new(item_state);
let ((item_state_proxy, transaction), result) = ItemStateAction::default()
.act(actions::take_item_from_inventory(character.id, *item_id, amount))
.act(actions::add_inventory_item_to_bank(character.id))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
Ok((transaction, result))
}).await
}
pub async fn equip_item<'a, EG> (
item_state: &'a mut ItemState,
entity_gateway: &mut EG,
character: &CharacterEntity,
item_id: &ClientItemId,
equip_slot: u8,
) -> Result<(), ItemStateError>
where
EG: EntityGateway,
{
entity_gateway.with_transaction(|transaction| async move {
let item_state_proxy = ItemStateProxy::new(item_state);
let ((item_state_proxy, transaction), result) = ItemStateAction::default()
.act(actions::equip_inventory_item(character.id, *item_id, equip_slot))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
Ok((transaction, result))
}).await
}
pub async fn unequip_item<'a, EG> (
item_state: &'a mut ItemState,
entity_gateway: &mut EG,
character: &CharacterEntity,
item_id: &ClientItemId,
) -> Result<(), ItemStateError>
where
EG: EntityGateway,
{
entity_gateway.with_transaction(|transaction| async move {
let item_state_proxy = ItemStateProxy::new(item_state);
let ((item_state_proxy, transaction), result) = ItemStateAction::default()
.act(actions::unequip_inventory_item(character.id, *item_id))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
Ok((transaction, result))
}).await
}
pub async fn sort_inventory<'a, EG> (
item_state: &'a mut ItemState,
entity_gateway: &mut EG,
character: &CharacterEntity,
item_ids: Vec<ClientItemId>,
) -> Result<(), ItemStateError>
where
EG: EntityGateway,
{
entity_gateway.with_transaction(|transaction| async move {
let item_state_proxy = ItemStateProxy::new(item_state);
let ((item_state_proxy, transaction), result) = ItemStateAction::default()
.act(actions::sort_inventory_items(character.id, item_ids))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
Ok((transaction, result))
}).await
}
pub async fn use_item<'a, EG> (
item_state: &'a mut ItemState,
entity_gateway: &mut EG,
character: &mut CharacterEntity,
item_id: &ClientItemId,
amount: u32,
) -> Result<(), ItemStateError>
where
EG: EntityGateway,
{
entity_gateway.with_transaction(|transaction| async move {
let item_state_proxy = ItemStateProxy::new(item_state);
let ((item_state_proxy, transaction), new_character) = ItemStateAction::default()
.act(actions::take_item_from_inventory(character.id, *item_id, amount))
.act(actions::use_consumed_item(character.clone()))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
*character = new_character;
Ok((transaction, ()))
}).await
}
pub async fn feed_mag<'a, EG> (
item_state: &'a mut ItemState,
entity_gateway: &mut EG,
character: &CharacterEntity,
mag_item_id: &ClientItemId,
tool_item_id: &ClientItemId,
) -> Result<(), ItemStateError>
where
EG: EntityGateway,
{
entity_gateway.with_transaction(|transaction| async move {
let item_state_proxy = ItemStateProxy::new(item_state);
let ((item_state_proxy, transaction), _) = ItemStateAction::default()
.act(actions::take_item_from_inventory(character.id, *tool_item_id, 1))
.act(actions::feed_mag_item(character.clone(), *mag_item_id))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
Ok((transaction, ()))
}).await
}
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(actions::take_meseta_from_inventory(character.id, item_price))
//.act(bought_item_to_inventory_item)
//.act(add_item_to_inventory)
.act(actions::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
}
pub async fn sell_item<'a, EG> (
item_state: &'a mut ItemState,
entity_gateway: &mut EG,
character: &CharacterEntity,
item_id: ClientItemId,
amount: u32,
) -> Result<InventoryItem, ItemStateError>
where
EG: EntityGateway,
{
entity_gateway.with_transaction(|transaction| async move {
let item_state_proxy = ItemStateProxy::new(item_state);
let ((item_state_proxy, transaction), result) = ItemStateAction::default()
.act(actions::take_item_from_inventory(character.id, item_id, amount))
.act(actions::sell_inventory_item(character.id))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
Ok((transaction, result))
}).await
}
pub async fn trade_items<'a, EG> (
item_state: &'a mut ItemState,
entity_gateway: &mut EG,
p1: (&AreaClient, &CharacterEntity, &Vec<TradeItem>, Meseta),
p2: (&AreaClient, &CharacterEntity, &Vec<TradeItem>, Meseta))
-> Result<(Vec<InventoryItem>, Vec<InventoryItem>), ItemStateError>
where
EG: EntityGateway,
{
let p1_trade_items = p1.2
.iter()
.map(|item| {
match item {
TradeItem::Individual(item_id) => (*item_id, 1),
TradeItem::Stacked(item_id, amount) => (*item_id, *amount as u32),
}
})
.collect();
let p2_trade_items = p2.2
.iter()
.map(|item| {
match item {
TradeItem::Individual(item_id) => (*item_id, 1),
TradeItem::Stacked(item_id, amount) => (*item_id, *amount as u32),
}
})
.collect();
entity_gateway.with_transaction(|mut transaction| async move {
let p1_id = p1.1.id;
let p2_id = p2.1.id;
let trade = transaction.gateway().create_trade(&p1_id, &p2_id).await?;
let item_state_proxy = ItemStateProxy::new(item_state);
let ((item_state_proxy, transaction), p1_removed_items) = ItemStateAction::default()
.act(actions::iterate(p1_trade_items, move |p1_trade_item| actions::take_item_from_inventory(p1_id, p1_trade_item.0, p1_trade_item.1) ))
.act(actions::foreach(actions::assign_new_item_id()))
.commit((item_state_proxy, transaction))
.await?;
let ((item_state_proxy, transaction), p2_removed_items) = ItemStateAction::default()
.act(actions::iterate(p2_trade_items, move |p2_trade_item| actions::take_item_from_inventory(p2_id, p2_trade_item.0, p2_trade_item.1) ))
.act(actions::foreach(actions::assign_new_item_id()))
.commit((item_state_proxy, transaction))
.await?;
let ((item_state_proxy, transaction), p2_new_items) = ItemStateAction::default()
.act(actions::insert(p1_removed_items))
.act(actions::foreach(actions::add_item_to_inventory(p2.1.clone())))
.act(actions::record_trade(trade.id, p1_id, p2_id))
.commit((item_state_proxy, transaction))
.await?;
let ((item_state_proxy, transaction), p1_new_items) = ItemStateAction::default()
.act(actions::insert(p2_removed_items))
.act(actions::foreach(actions::add_item_to_inventory(p1.1.clone())))
.act(actions::record_trade(trade.id, p2_id, p1_id))
.commit((item_state_proxy, transaction))
.await?;
let ((item_state_proxy, transaction), _) = ItemStateAction::default()
.act(actions::take_meseta_from_inventory(p1_id, p1.3.0))
.act(actions::take_meseta_from_inventory(p2_id, p2.3.0))
.act(actions::add_meseta_to_inventory(p1_id, p2.3.0))
.act(actions::add_meseta_to_inventory(p2_id, p1.3.0))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
Ok((transaction, (p1_new_items, p2_new_items)))
}).await
}
pub async fn take_meseta<'a, EG> (
item_state: &'a mut ItemState,
entity_gateway: &mut EG,
character_id: &CharacterEntityId,
meseta: Meseta)
-> Result<(), ItemStateError>
where
EG: EntityGateway,
{
entity_gateway.with_transaction(|transaction| async move {
let item_state_proxy = ItemStateProxy::new(item_state);
let ((item_state_proxy, transaction), _) = ItemStateAction::default()
.act(actions::take_meseta_from_inventory(*character_id, meseta.0))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
Ok((transaction, ()))
}).await
}
pub async fn enemy_drops_item<'a, EG> (
item_state: &'a mut ItemState,
entity_gateway: &mut EG,
character_id: CharacterEntityId,
item_drop: ItemDrop)
-> Result<FloorItem, ItemStateError>
where
EG: EntityGateway,
{
entity_gateway.with_transaction(|transaction| async move {
let item_state_proxy = ItemStateProxy::new(item_state);
let ((item_state_proxy, transaction), floor_item) = ItemStateAction::default()
.act(actions::convert_item_drop_to_floor_item(character_id, item_drop))
.act(actions::add_item_to_local_floor(character_id))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
Ok((transaction, floor_item))
}).await
}
pub async fn apply_modifier<'a, EG> (
item_state: &'a mut ItemState,
entity_gateway: &mut EG,
character: &CharacterEntity,
item_id: ClientItemId,
modifier: ItemModifier)
-> Result<IndividualItemDetail, ItemStateError>
where
EG: EntityGateway,
{
entity_gateway.with_transaction(|transaction| async move {
let item_state_proxy = ItemStateProxy::new(item_state);
let ((item_state_proxy, transaction), item) = ItemStateAction::default()
.act(actions::take_item_from_inventory(character.id, item_id, 1))
.act(actions::apply_modifier_to_inventory_item(modifier))
.act(actions::add_item_to_inventory(character.clone()))
.act(actions::as_individual_item())
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
Ok((transaction, item))
}).await
}

View File

@ -4,7 +4,10 @@ use crate::entity::item;
use crate::common::leveltable::CharacterStats;
use crate::ship::ship::{ShipError};
use crate::ship::items::ClientItemId;
use crate::ship::items::state::{InventoryItem, FloorItem, BankState, IndividualItemDetail};
use crate::ship::items::inventory::InventoryItem;
use crate::ship::items::state::IndividualItemDetail;
use crate::ship::items::bank::BankState;
use crate::ship::items::floor::FloorItem;
use crate::ship::location::AreaClient;
use std::convert::TryInto;
use crate::ship::shops::ShopItem;

View File

@ -14,8 +14,10 @@ use crate::entity::item;
use libpso::utf8_to_utf16_array;
use crate::ship::packet::builder;
use crate::ship::shops::{ShopItem, ToolShopItem, ArmorShopItem};
use crate::ship::items::state::{ItemState, ItemStateError, FloorType, FloorItemDetail};
use crate::ship::items::actions::{pick_up_item, withdraw_meseta, deposit_meseta, withdraw_item, deposit_item, buy_shop_item, enemy_drops_item, take_meseta, apply_modifier, TriggerCreateItem};
use crate::ship::items::state::{ItemState, ItemStateError};
use crate::ship::items::floor::{FloorType, FloorItemDetail};
use crate::ship::items::actions::TriggerCreateItem;
use crate::ship::items::tasks::{pick_up_item, withdraw_meseta, deposit_meseta, withdraw_item, deposit_item, buy_shop_item, enemy_drops_item, take_meseta, apply_modifier};
const BANK_ACTION_DEPOSIT: u8 = 0;
const BANK_ACTION_WITHDRAW: u8 = 1;

View File

@ -9,7 +9,7 @@ use crate::ship::location::{ClientLocation, ClientLocationError};
use crate::ship::items::ClientItemId;
use crate::ship::packet::builder;
use crate::ship::items::state::ItemState;
use crate::ship::items::actions::{drop_item, drop_partial_item, drop_meseta, equip_item, unequip_item, sort_inventory, use_item, feed_mag, sell_item, take_meseta};
use crate::ship::items::tasks::{drop_item, drop_partial_item, drop_meseta, equip_item, unequip_item, sort_inventory, use_item, feed_mag, sell_item, take_meseta};
pub async fn request_exp<EG: EntityGateway>(id: ClientId,
request_exp: &RequestExp,

View File

@ -5,11 +5,12 @@ use crate::common::serverstate::ClientId;
use crate::ship::ship::{SendShipPacket, ShipError, Clients};
use crate::ship::location::{ClientLocation, ClientLocationError};
use crate::ship::items::ClientItemId;
use crate::ship::items::state::{ItemState, ItemStateError, InventoryItemDetail};
use crate::ship::items::state::{ItemState, ItemStateError};
use crate::ship::items::inventory::InventoryItemDetail;
use crate::ship::trade::{TradeItem, TradeState, TradeStatus};
use crate::entity::gateway::EntityGateway;
use crate::ship::packet::builder;
use crate::ship::items::actions::trade_items;
use crate::ship::items::tasks::trade_items;
use crate::ship::location::{AreaClient, RoomId};
use crate::entity::item::Meseta;

View File

@ -5,7 +5,8 @@ use elseware::entity::item;
use elseware::ship::ship::{ShipServerState, RecvShipPacket, SendShipPacket, ShipError};
use elseware::entity::item::{Meseta, ItemEntity};
use elseware::ship::packet::handler::trade::TradeError;
use elseware::ship::items::state::{ItemStateError, InventoryError};
use elseware::ship::items::state::ItemStateError;
use elseware::ship::items::inventory::InventoryError;
use libpso::packet::ship::*;
use libpso::packet::messages::*;