Browse Source
Merge pull request 'the final itemrefactor (probably)' (#113) from peak_itemrefactor into master
Merge pull request 'the final itemrefactor (probably)' (#113) from peak_itemrefactor into master
Reviewed-on: #113pull/112/head^2
jake
2 years ago
57 changed files with 4764 additions and 3100 deletions
-
354Cargo.lock
-
5Cargo.toml
-
6src/bin/main.rs
-
2src/common/leveltable.rs
-
62src/entity/gateway/entitygateway.rs
-
180src/entity/gateway/inmemory.rs
-
2src/entity/gateway/mod.rs
-
5src/entity/gateway/postgres/migrations/V0005__trade.sql
-
63src/entity/gateway/postgres/models.rs
-
1162src/entity/gateway/postgres/postgres.rs
-
4src/entity/item/armor.rs
-
4src/entity/item/esweapon.rs
-
33src/entity/item/mag.rs
-
38src/entity/item/mod.rs
-
2src/entity/item/shield.rs
-
2src/entity/item/tool.rs
-
4src/entity/item/unit.rs
-
14src/entity/item/weapon.rs
-
1src/lib.rs
-
67src/login/character.rs
-
13src/login/login.rs
-
12src/ship/character.rs
-
2src/ship/drops/mod.rs
-
852src/ship/items/actions.rs
-
293src/ship/items/apply_item.rs
-
450src/ship/items/bank.rs
-
294src/ship/items/floor.rs
-
1083src/ship/items/inventory.rs
-
137src/ship/items/itemstateaction.rs
-
56src/ship/items/manager.rs
-
23src/ship/items/mod.rs
-
391src/ship/items/state.rs
-
500src/ship/items/tasks.rs
-
337src/ship/items/transaction.rs
-
163src/ship/items/use_tool.rs
-
30src/ship/location.rs
-
2src/ship/map/area.rs
-
2src/ship/map/variant.rs
-
10src/ship/packet/builder/lobby.rs
-
52src/ship/packet/builder/message.rs
-
9src/ship/packet/builder/mod.rs
-
8src/ship/packet/builder/room.rs
-
6src/ship/packet/handler/auth.rs
-
123src/ship/packet/handler/direct_message.rs
-
27src/ship/packet/handler/lobby.rs
-
127src/ship/packet/handler/message.rs
-
12src/ship/packet/handler/room.rs
-
152src/ship/packet/handler/trade.rs
-
105src/ship/ship.rs
-
10src/ship/trade.rs
-
2tests/common.rs
-
120tests/test_bank.rs
-
4tests/test_item_actions.rs
-
2tests/test_item_pickup.rs
-
6tests/test_item_use.rs
-
4tests/test_shops.rs
-
435tests/test_trade.rs
@ -0,0 +1,5 @@ |
|||||
|
create table trades ( |
||||
|
id serial primary key not null, |
||||
|
character1 integer references character (id) not null, |
||||
|
character2 integer references character (id) not null, |
||||
|
); |
1162
src/entity/gateway/postgres/postgres.rs
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,852 @@ |
|||||
|
// 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;
|
||||
|
use std::pin::Pin;
|
||||
|
|
||||
|
use crate::ship::map::MapArea;
|
||||
|
use crate::entity::character::{CharacterEntity, CharacterEntityId};
|
||||
|
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::drops::{ItemDrop, ItemDropType};
|
||||
|
|
||||
|
pub enum TriggerCreateItem {
|
||||
|
Yes,
|
||||
|
No
|
||||
|
}
|
||||
|
|
||||
|
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>>
|
||||
|
{
|
||||
|
move |(mut item_state, transaction): (ItemStateProxy<'_>, Box<dyn EntityGatewayTransaction + '_>) , _| {
|
||||
|
Box::pin(async move {
|
||||
|
let mut floor = item_state.floor(&character_id)?;
|
||||
|
let item = floor.take_item(&item_id).ok_or(ItemStateError::NoFloorItem(item_id))?;
|
||||
|
item_state.set_floor(floor);
|
||||
|
|
||||
|
Ok(((item_state, transaction), item))
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
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>>
|
||||
|
{
|
||||
|
let character = character.clone();
|
||||
|
move |(mut item_state, transaction), floor_item| {
|
||||
|
let character = character.clone();
|
||||
|
Box::pin(async move {
|
||||
|
let mut inventory = item_state.inventory(&character.id)?;
|
||||
|
|
||||
|
let character_id = character.id;
|
||||
|
let transaction = floor_item.with_entity_id(transaction, |mut transaction, entity_id| {
|
||||
|
async move {
|
||||
|
transaction.gateway().add_item_note(&entity_id, ItemNote::Pickup {
|
||||
|
character_id
|
||||
|
}).await?;
|
||||
|
Ok(transaction)
|
||||
|
}}).await?;
|
||||
|
|
||||
|
let mut transaction = floor_item.with_mag(transaction, |mut transaction, entity_id, _mag| {
|
||||
|
let character = character.clone();
|
||||
|
async move {
|
||||
|
transaction.gateway().change_mag_owner(&entity_id, &character).await?;
|
||||
|
Ok(transaction)
|
||||
|
}}).await?;
|
||||
|
|
||||
|
let add_result = inventory.add_floor_item(floor_item)?;
|
||||
|
transaction.gateway().set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
|
||||
|
transaction.gateway().set_character_meseta(&character_id, inventory.meseta).await?;
|
||||
|
item_state.set_inventory(inventory);
|
||||
|
|
||||
|
Ok(((item_state, transaction),
|
||||
|
match add_result {
|
||||
|
AddItemResult::NewItem => TriggerCreateItem::Yes,
|
||||
|
AddItemResult::AddToStack => TriggerCreateItem::No,
|
||||
|
AddItemResult::Meseta => TriggerCreateItem::No,
|
||||
|
}))
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
|
||||
|
|
||||
|
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>>
|
||||
|
{
|
||||
|
move |(mut item_state, mut transaction), _| {
|
||||
|
Box::pin(async move {
|
||||
|
let mut inventory = item_state.inventory(&character_id)?;
|
||||
|
let item = inventory.take_item(&item_id, amount).ok_or (ItemStateError::NoFloorItem(item_id))?;
|
||||
|
|
||||
|
transaction.gateway().set_character_inventory(&character_id, &inventory.as_inventory_entity(&character_id)).await?;
|
||||
|
item_state.set_inventory(inventory);
|
||||
|
|
||||
|
Ok(((item_state, transaction), item))
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
|
||||
|
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>>
|
||||
|
{
|
||||
|
move |(mut item_state, transaction), inventory_item| {
|
||||
|
Box::pin(async move {
|
||||
|
let transaction = inventory_item.with_entity_id(transaction, |mut transaction, entity_id| {
|
||||
|
async move {
|
||||
|
transaction.gateway().add_item_note(&entity_id, ItemNote::PlayerDrop {
|
||||
|
character_id,
|
||||
|
map_area,
|
||||
|
x: drop_position.0,
|
||||
|
y: drop_position.1,
|
||||
|
z: drop_position.2,
|
||||
|
}).await?;
|
||||
|
Ok(transaction)
|
||||
|
}}).await?;
|
||||
|
|
||||
|
let mut floor = item_state.floor(&character_id)?;
|
||||
|
let floor_item = floor.add_inventory_item(inventory_item, map_area, drop_position).clone();
|
||||
|
item_state.set_floor(floor);
|
||||
|
|
||||
|
Ok(((item_state, transaction), floor_item))
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
|
||||
|
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>>
|
||||
|
{
|
||||
|
move |(mut item_state, mut transaction), _| {
|
||||
|
Box::pin(async move {
|
||||
|
let mut inventory = item_state.inventory(&character_id)?;
|
||||
|
inventory.remove_meseta(amount)?;
|
||||
|
transaction.gateway().set_character_meseta(&character_id, inventory.meseta).await?;
|
||||
|
item_state.set_inventory(inventory);
|
||||
|
|
||||
|
Ok(((item_state, transaction), ()))
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
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>>
|
||||
|
{
|
||||
|
move |(mut item_state, mut transaction), _| {
|
||||
|
Box::pin(async move {
|
||||
|
let mut inventory = item_state.inventory(&character_id)?;
|
||||
|
inventory.add_meseta(amount)?;
|
||||
|
transaction.gateway().set_character_meseta(&character_id, inventory.meseta).await?;
|
||||
|
item_state.set_inventory(inventory);
|
||||
|
|
||||
|
Ok(((item_state, transaction), ()))
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
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>>
|
||||
|
{
|
||||
|
|
||||
|
move |(mut item_state, transaction), _| {
|
||||
|
Box::pin(async move {
|
||||
|
let floor_item = FloorItem {
|
||||
|
item_id: item_state.new_item_id()?,
|
||||
|
item: FloorItemDetail::Meseta(Meseta(amount)),
|
||||
|
map_area,
|
||||
|
x: drop_position.0,
|
||||
|
y: 0.0,
|
||||
|
z: drop_position.1,
|
||||
|
};
|
||||
|
|
||||
|
let mut floor = item_state.floor(&character_id)?;
|
||||
|
let floor_item = floor.add_shared_item(floor_item).clone();
|
||||
|
item_state.set_floor(floor);
|
||||
|
|
||||
|
Ok(((item_state, transaction), floor_item))
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
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>>
|
||||
|
{
|
||||
|
move |(mut item_state, mut transaction), _| {
|
||||
|
Box::pin(async move {
|
||||
|
let mut bank = item_state.bank(&character_id)?;
|
||||
|
bank.remove_meseta(amount)?;
|
||||
|
transaction.gateway().set_bank_meseta(&character_id, &bank.name, bank.meseta).await?;
|
||||
|
|
||||
|
Ok(((item_state, transaction), ()))
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
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>>
|
||||
|
{
|
||||
|
move |(mut item_state, mut transaction), _| {
|
||||
|
Box::pin(async move {
|
||||
|
let mut inventory = item_state.inventory(&character_id)?;
|
||||
|
inventory.add_meseta_no_overflow(amount)?;
|
||||
|
transaction.gateway().set_character_meseta(&character_id, inventory.meseta).await?;
|
||||
|
|
||||
|
Ok(((item_state, transaction), ()))
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
|
||||
|
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>>
|
||||
|
{
|
||||
|
move |(mut item_state, mut transaction), _| {
|
||||
|
Box::pin(async move {
|
||||
|
let mut bank = item_state.bank(&character_id)?;
|
||||
|
bank.add_meseta(amount)?;
|
||||
|
transaction.gateway().set_bank_meseta(&character_id, &bank.name, bank.meseta).await?;
|
||||
|
|
||||
|
Ok(((item_state, transaction), ()))
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
|
||||
|
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>>
|
||||
|
{
|
||||
|
move |(mut item_state, mut transaction), _| {
|
||||
|
Box::pin(async move {
|
||||
|
let mut bank = item_state.bank(&character_id)?;
|
||||
|
let item = bank.take_item(&item_id, amount).ok_or(ItemStateError::NoBankItem(item_id))?;
|
||||
|
transaction.gateway().set_character_bank(&character_id, &bank.as_bank_entity(), &bank.name).await?;
|
||||
|
item_state.set_bank(bank);
|
||||
|
|
||||
|
Ok(((item_state, transaction), item))
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
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>>
|
||||
|
{
|
||||
|
let character = character.clone();
|
||||
|
move |(mut item_state, transaction), bank_item| {
|
||||
|
let character = character.clone();
|
||||
|
Box::pin(async move {
|
||||
|
let bank_name = item_state.bank(&character.id)?.name;
|
||||
|
let mut inventory = item_state.inventory(&character.id)?;
|
||||
|
|
||||
|
let character_id = character.id;
|
||||
|
let transaction = bank_item.with_entity_id(transaction, |mut transaction, entity_id| {
|
||||
|
let bank_name = bank_name.clone();
|
||||
|
async move {
|
||||
|
transaction.gateway().add_item_note(&entity_id, ItemNote::Withdraw {
|
||||
|
character_id,
|
||||
|
bank: bank_name,
|
||||
|
}).await?;
|
||||
|
Ok(transaction)
|
||||
|
}}).await?;
|
||||
|
|
||||
|
let inventory_item = InventoryItem {
|
||||
|
item_id: bank_item.item_id,
|
||||
|
item: match bank_item.item {
|
||||
|
BankItemDetail::Individual(individual_item) => InventoryItemDetail::Individual(individual_item),
|
||||
|
BankItemDetail::Stacked(stacked_item) => InventoryItemDetail::Stacked(stacked_item),
|
||||
|
},
|
||||
|
};
|
||||
|
|
||||
|
let mut transaction = inventory_item.with_mag(transaction, |mut transaction, entity_id, _mag| {
|
||||
|
let character = character.clone();
|
||||
|
async move {
|
||||
|
transaction.gateway().change_mag_owner(&entity_id, &character).await?;
|
||||
|
Ok(transaction)
|
||||
|
}}).await?;
|
||||
|
|
||||
|
inventory.add_item(inventory_item.clone())?;
|
||||
|
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(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>>
|
||||
|
{
|
||||
|
move |(mut item_state, transaction), inventory_item| {
|
||||
|
Box::pin(async move {
|
||||
|
let mut bank = item_state.bank(&character_id)?;
|
||||
|
let bank_name = bank.name.clone();
|
||||
|
let mut transaction = inventory_item.with_entity_id(transaction, move |mut transaction, entity_id| {
|
||||
|
let bank_name = bank_name.clone();
|
||||
|
async move {
|
||||
|
transaction.gateway().add_item_note(&entity_id, ItemNote::Deposit {
|
||||
|
character_id,
|
||||
|
bank: bank_name,
|
||||
|
}).await?;
|
||||
|
Ok(transaction)
|
||||
|
}}).await?;
|
||||
|
|
||||
|
bank.add_inventory_item(inventory_item)?;
|
||||
|
|
||||
|
transaction.gateway().set_character_bank(&character_id, &bank.as_bank_entity(), &bank.name).await?;
|
||||
|
item_state.set_bank(bank);
|
||||
|
|
||||
|
|
||||
|
Ok(((item_state, transaction), ()))
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
|
||||
|
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>>
|
||||
|
{
|
||||
|
move |(mut item_state, mut transaction), _| {
|
||||
|
Box::pin(async move {
|
||||
|
let mut inventory = item_state.inventory(&character_id)?;
|
||||
|
inventory.equip(&item_id, equip_slot);
|
||||
|
transaction.gateway().set_character_equips(&character_id, &inventory.as_equipped_entity()).await?;
|
||||
|
item_state.set_inventory(inventory);
|
||||
|
|
||||
|
Ok(((item_state, transaction), ()))
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
|
||||
|
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>>
|
||||
|
{
|
||||
|
move |(mut item_state, mut transaction), _| {
|
||||
|
Box::pin(async move {
|
||||
|
let mut inventory = item_state.inventory(&character_id)?;
|
||||
|
inventory.unequip(&item_id);
|
||||
|
transaction.gateway().set_character_equips(&character_id, &inventory.as_equipped_entity()).await?;
|
||||
|
item_state.set_inventory(inventory);
|
||||
|
|
||||
|
Ok(((item_state, transaction), ()))
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
|
||||
|
|
||||
|
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>>
|
||||
|
{
|
||||
|
move |(mut item_state, mut transaction), _| {
|
||||
|
let item_ids = item_ids.clone();
|
||||
|
Box::pin(async move {
|
||||
|
let mut inventory = item_state.inventory(&character_id)?;
|
||||
|
inventory.sort(&item_ids);
|
||||
|
transaction.gateway().set_character_inventory(&character_id, &inventory.as_inventory_entity(&character_id)).await?;
|
||||
|
item_state.set_inventory(inventory);
|
||||
|
|
||||
|
Ok(((item_state, transaction), ()))
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
|
||||
|
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>>
|
||||
|
{
|
||||
|
move |(mut item_state, transaction), inventory_item| {
|
||||
|
let mut character = character.clone();
|
||||
|
Box::pin(async move {
|
||||
|
let mut transaction = inventory_item.with_entity_id(transaction, |mut transaction, entity_id| {
|
||||
|
async move {
|
||||
|
transaction.gateway().add_item_note(&entity_id, ItemNote::Consumed).await?;
|
||||
|
Ok(transaction)
|
||||
|
}}).await?;
|
||||
|
|
||||
|
apply_item(&mut item_state, transaction.gateway(), &mut character, inventory_item).await?;
|
||||
|
|
||||
|
Ok(((item_state, transaction), character))
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
|
||||
|
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>>
|
||||
|
{
|
||||
|
move |(mut item_state, transaction), tool| {
|
||||
|
let character = character.clone();
|
||||
|
Box::pin(async move {
|
||||
|
let mut inventory = item_state.inventory(&character.id)?;
|
||||
|
let mag_entity = inventory.get_by_client_id_mut(&mag_item_id)
|
||||
|
.ok_or(ItemStateError::InvalidItemId(mag_item_id))?
|
||||
|
.item
|
||||
|
.as_individual_mut()
|
||||
|
.ok_or(ItemStateError::NotAMag(mag_item_id))?;
|
||||
|
let mag_entity_id = mag_entity.entity_id;
|
||||
|
|
||||
|
let mut transaction = tool.with_entity_id(transaction, |mut transaction, entity_id| {
|
||||
|
async move {
|
||||
|
transaction.gateway().add_item_note(&entity_id, ItemNote::FedToMag {
|
||||
|
//character_id: character.id,
|
||||
|
mag: mag_entity_id,
|
||||
|
}).await?;
|
||||
|
transaction.gateway().feed_mag(&mag_entity_id, &entity_id).await?;
|
||||
|
Ok(transaction)
|
||||
|
}}).await?;
|
||||
|
|
||||
|
let food_tool = tool
|
||||
|
.item
|
||||
|
.stacked()
|
||||
|
.ok_or(ItemStateError::NotMagFood(tool.item_id))?
|
||||
|
.tool
|
||||
|
.tool;
|
||||
|
|
||||
|
let mag_entity = mag_entity
|
||||
|
.as_mag_mut()
|
||||
|
.ok_or(ItemStateError::NotAMag(mag_item_id))?;
|
||||
|
|
||||
|
mag_entity.feed(food_tool);
|
||||
|
|
||||
|
transaction.gateway().set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
|
||||
|
item_state.set_inventory(inventory);
|
||||
|
|
||||
|
Ok(((item_state, transaction), character))
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
|
||||
|
pub(super) 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,
|
||||
|
}).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,
|
||||
|
})
|
||||
|
};
|
||||
|
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,
|
||||
|
}).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(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>>
|
||||
|
{
|
||||
|
move |(mut item_state, transaction), inventory_item| {
|
||||
|
Box::pin(async move {
|
||||
|
let mut inventory = item_state.inventory(&character_id)?;
|
||||
|
let price = inventory_item.item.sell_price()?;
|
||||
|
inventory.add_meseta_no_overflow(price)?;
|
||||
|
|
||||
|
let mut transaction = inventory_item.with_entity_id(transaction, |mut transaction, entity_id| {
|
||||
|
async move {
|
||||
|
transaction.gateway().add_item_note(&entity_id, ItemNote::SoldToShop).await?;
|
||||
|
Ok(transaction)
|
||||
|
}}).await?;
|
||||
|
transaction.gateway().set_character_meseta(&character_id, inventory.meseta).await?;
|
||||
|
item_state.set_inventory(inventory);
|
||||
|
Ok(((item_state, transaction), inventory_item))
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
|
||||
|
#[async_recursion::async_recursion]
|
||||
|
async fn iterate_inner<'a, I, O, T, F, FR>(state: (ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>),
|
||||
|
mut input: Vec<I>,
|
||||
|
func: F,
|
||||
|
arg: T)
|
||||
|
-> Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), Vec<O>), ItemStateError>
|
||||
|
where
|
||||
|
'a: 'async_recursion,
|
||||
|
I: Send,
|
||||
|
O: Send,
|
||||
|
T: Clone + Send + Sync,
|
||||
|
F: Fn(I) -> FR + Send + Sync + Clone + 'static,
|
||||
|
FR: Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), T)
|
||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), O), ItemStateError>> + Send + 'a>> + Send + Sync,
|
||||
|
{
|
||||
|
let item = match input.pop() {
|
||||
|
Some(item) => item,
|
||||
|
None => return Ok((state, Vec::new()))
|
||||
|
};
|
||||
|
|
||||
|
let (state, mut output) = iterate_inner(state, input, func.clone(), arg.clone()).await.unwrap();
|
||||
|
let rfunc = func(item);
|
||||
|
let (state, result) = rfunc(state, arg.clone()).await.unwrap();
|
||||
|
|
||||
|
output.push(result);
|
||||
|
|
||||
|
Ok((state, output))
|
||||
|
}
|
||||
|
|
||||
|
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)
|
||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), Vec<O>), ItemStateError>> + Send + 'a>>
|
||||
|
where
|
||||
|
O: Send,
|
||||
|
I: Send + Clone + 'static + std::fmt::Debug,
|
||||
|
T: Send + Clone + 'static + std::fmt::Debug,
|
||||
|
F: Fn(I) -> FR + Send + Sync + Clone + 'static,
|
||||
|
FR: for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), T)
|
||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), O), ItemStateError>> + Send + 'a>> + Send + Sync,
|
||||
|
T: Clone + Send + Sync,
|
||||
|
{
|
||||
|
move |(item_state, transaction), arg| {
|
||||
|
let input = input.clone();
|
||||
|
let func = func.clone();
|
||||
|
println!("i {:?} {:?}", input, arg);
|
||||
|
Box::pin(async move {
|
||||
|
let (state, result) = iterate_inner((item_state, transaction), input, func, arg.clone()).await?;
|
||||
|
Ok((state, result))
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
|
||||
|
#[async_recursion::async_recursion]
|
||||
|
async fn foreach_inner<'a, O, T, F>(state: (ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>),
|
||||
|
mut input: Vec<T>,
|
||||
|
func: F)
|
||||
|
-> Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), Vec<O>), ItemStateError>
|
||||
|
where
|
||||
|
'a: 'async_recursion,
|
||||
|
O: Send,
|
||||
|
T: Clone + Send,
|
||||
|
F: Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), T)
|
||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), O), ItemStateError>> + Send + 'a>> + Send + Sync,
|
||||
|
F: Clone,
|
||||
|
{
|
||||
|
let item = match input.pop() {
|
||||
|
Some(item) => item,
|
||||
|
None => return Ok((state, Vec::new()))
|
||||
|
};
|
||||
|
|
||||
|
let (state, mut output) = foreach_inner(state, input, func.clone()).await?;
|
||||
|
let (state, result) = func(state, item).await?;
|
||||
|
|
||||
|
output.push(result);
|
||||
|
|
||||
|
Ok((state, output))
|
||||
|
}
|
||||
|
|
||||
|
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
|
||||
|
O: Send,
|
||||
|
T: Send + Clone + 'static + std::fmt::Debug,
|
||||
|
F: for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), T)
|
||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), O), ItemStateError>> + Send + 'a>> + Send + Sync + 'static,
|
||||
|
F: Clone,
|
||||
|
T: Clone + Send + Sync,
|
||||
|
{
|
||||
|
move |(item_state, transaction), items| {
|
||||
|
println!("fe {:?}", items);
|
||||
|
let func = func.clone();
|
||||
|
Box::pin(async move {
|
||||
|
let (state, result) = foreach_inner((item_state, transaction), items, func).await?;
|
||||
|
Ok((state, result))
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
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>>
|
||||
|
{
|
||||
|
move |state, _| {
|
||||
|
let element = element.clone();
|
||||
|
Box::pin(async move {
|
||||
|
Ok((state, element))
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
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
|
||||
|
{
|
||||
|
move |(mut item_state, transaction), inventory_item| {
|
||||
|
let character = character.clone();
|
||||
|
Box::pin(async move {
|
||||
|
let mut inventory = item_state.inventory(&character.id)?;
|
||||
|
let mut transaction = inventory_item.with_mag(transaction, |mut transaction, entity_id, _mag| {
|
||||
|
let character = character.clone();
|
||||
|
async move {
|
||||
|
transaction.gateway().change_mag_owner(&entity_id, &character).await?;
|
||||
|
Ok(transaction)
|
||||
|
}}).await?;
|
||||
|
|
||||
|
inventory.add_item(inventory_item.clone())?;
|
||||
|
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(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
|
||||
|
{
|
||||
|
move |(item_state, mut transaction), traded_items| {
|
||||
|
Box::pin(async move {
|
||||
|
for item in &traded_items {
|
||||
|
transaction = item.with_entity_id(transaction, |mut transaction, entity_id| {
|
||||
|
async move {
|
||||
|
transaction.gateway().add_item_note(&entity_id, ItemNote::Trade {
|
||||
|
trade_id,
|
||||
|
character_to,
|
||||
|
character_from,
|
||||
|
}).await?;
|
||||
|
Ok(transaction)
|
||||
|
}}).await?;
|
||||
|
}
|
||||
|
Ok(((item_state, transaction), traded_items))
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
|
||||
|
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
|
||||
|
{
|
||||
|
move |(mut item_state, transaction), mut inventory_item| {
|
||||
|
Box::pin(async move {
|
||||
|
inventory_item.item_id = item_state.new_item_id()?;
|
||||
|
Ok(((item_state, transaction), inventory_item))
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
|
||||
|
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
|
||||
|
{
|
||||
|
move |(mut item_state, mut transaction), _| {
|
||||
|
let item_drop = item_drop.clone();
|
||||
|
Box::pin(async move {
|
||||
|
enum ItemOrMeseta {
|
||||
|
Individual(ItemDetail),
|
||||
|
Stacked(Tool),
|
||||
|
Meseta(Meseta)
|
||||
|
}
|
||||
|
|
||||
|
let item = match item_drop.item {
|
||||
|
ItemDropType::Weapon(w) => ItemOrMeseta::Individual(ItemDetail::Weapon(w)),
|
||||
|
ItemDropType::Armor(w) => ItemOrMeseta::Individual(ItemDetail::Armor(w)),
|
||||
|
ItemDropType::Shield(w) => ItemOrMeseta::Individual(ItemDetail::Shield(w)),
|
||||
|
ItemDropType::Unit(w) => ItemOrMeseta::Individual(ItemDetail::Unit(w)),
|
||||
|
ItemDropType::TechniqueDisk(w) => ItemOrMeseta::Individual(ItemDetail::TechniqueDisk(w)),
|
||||
|
ItemDropType::Mag(w) => ItemOrMeseta::Individual(ItemDetail::Mag(w)),
|
||||
|
ItemDropType::Tool(t) => {
|
||||
|
if t.tool.is_stackable() {
|
||||
|
ItemOrMeseta::Stacked(t)
|
||||
|
}
|
||||
|
else {
|
||||
|
ItemOrMeseta::Individual(ItemDetail::Tool(t))
|
||||
|
}
|
||||
|
},
|
||||
|
ItemDropType::Meseta(m) => ItemOrMeseta::Meseta(Meseta(m)),
|
||||
|
};
|
||||
|
|
||||
|
let item_id = item_state.new_item_id()?;
|
||||
|
|
||||
|
let floor_item = match item {
|
||||
|
ItemOrMeseta::Individual(item_detail) => {
|
||||
|
let entity = transaction.gateway().create_item(NewItemEntity {
|
||||
|
item: item_detail.clone(),
|
||||
|
}).await?;
|
||||
|
transaction.gateway().add_item_note(&entity.id, ItemNote::EnemyDrop {
|
||||
|
character_id,
|
||||
|
map_area: item_drop.map_area,
|
||||
|
x: item_drop.x,
|
||||
|
y: item_drop.y,
|
||||
|
z: item_drop.z,
|
||||
|
}).await?;
|
||||
|
FloorItem {
|
||||
|
item_id,
|
||||
|
item: FloorItemDetail::Individual(IndividualItemDetail {
|
||||
|
entity_id: entity.id,
|
||||
|
item: item_detail,
|
||||
|
}),
|
||||
|
map_area: item_drop.map_area,
|
||||
|
x: item_drop.x,
|
||||
|
y: item_drop.y,
|
||||
|
z: item_drop.z,
|
||||
|
}
|
||||
|
},
|
||||
|
ItemOrMeseta::Stacked(tool) => {
|
||||
|
let entity = transaction.gateway().create_item(NewItemEntity {
|
||||
|
item: ItemDetail::Tool(tool),
|
||||
|
}).await?;
|
||||
|
transaction.gateway().add_item_note(&entity.id, ItemNote::EnemyDrop {
|
||||
|
character_id,
|
||||
|
map_area: item_drop.map_area,
|
||||
|
x: item_drop.x,
|
||||
|
y: item_drop.y,
|
||||
|
z: item_drop.z,
|
||||
|
}).await?;
|
||||
|
FloorItem {
|
||||
|
item_id,
|
||||
|
item: FloorItemDetail::Stacked(StackedItemDetail{
|
||||
|
entity_ids: vec![entity.id],
|
||||
|
tool,
|
||||
|
}),
|
||||
|
map_area: item_drop.map_area,
|
||||
|
x: item_drop.x,
|
||||
|
y: item_drop.y,
|
||||
|
z: item_drop.z,
|
||||
|
}
|
||||
|
},
|
||||
|
ItemOrMeseta::Meseta(meseta) => {
|
||||
|
FloorItem {
|
||||
|
item_id,
|
||||
|
item: FloorItemDetail::Meseta(meseta),
|
||||
|
map_area: item_drop.map_area,
|
||||
|
x: item_drop.x,
|
||||
|
y: item_drop.y,
|
||||
|
z: item_drop.z,
|
||||
|
}
|
||||
|
},
|
||||
|
};
|
||||
|
|
||||
|
Ok(((item_state, transaction), floor_item))
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
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>>
|
||||
|
{
|
||||
|
move |(mut item_state, transaction) , floor_item| {
|
||||
|
Box::pin(async move {
|
||||
|
let mut floor = item_state.floor(&character_id)?;
|
||||
|
let item = floor.add_local_item(floor_item).clone();
|
||||
|
item_state.set_floor(floor);
|
||||
|
|
||||
|
Ok(((item_state, transaction), item))
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
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>>
|
||||
|
{
|
||||
|
move |(item_state, mut transaction), mut inventory_item| {
|
||||
|
let modifier = modifier.clone();
|
||||
|
Box::pin(async move {
|
||||
|
match (&mut inventory_item.item, modifier) {
|
||||
|
(InventoryItemDetail::Individual(IndividualItemDetail{entity_id, item: ItemDetail::Weapon(ref mut weapon), ..}), ItemModifier::WeaponModifier(modifier)) => {
|
||||
|
weapon.apply_modifier(&modifier);
|
||||
|
transaction.gateway().add_weapon_modifier(entity_id, modifier).await?;
|
||||
|
},
|
||||
|
_ => return Err(ItemStateError::InvalidModifier)
|
||||
|
}
|
||||
|
|
||||
|
Ok(((item_state, transaction), inventory_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>>
|
||||
|
{
|
||||
|
move |(item_state, transaction), inventory_item| {
|
||||
|
Box::pin(async move {
|
||||
|
let item = match inventory_item.item {
|
||||
|
InventoryItemDetail::Individual(individual_item) => individual_item,
|
||||
|
_ => return Err(ItemStateError::WrongItemType(inventory_item.item_id))
|
||||
|
};
|
||||
|
|
||||
|
Ok(((item_state, transaction), item))
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,293 @@ |
|||||
|
use thiserror::Error;
|
||||
|
use std::convert::TryInto;
|
||||
|
use crate::entity::gateway::{EntityGateway, GatewayError};
|
||||
|
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, ItemStateError};
|
||||
|
use crate::ship::items::inventory::{InventoryItem, InventoryItemDetail};
|
||||
|
|
||||
|
|
||||
|
#[derive(Error, Debug)]
|
||||
|
pub enum ApplyItemError {
|
||||
|
#[error("no character")]
|
||||
|
NoCharacter,
|
||||
|
#[error("item not equipped")]
|
||||
|
ItemNotEquipped,
|
||||
|
#[error("invalid item")]
|
||||
|
InvalidItem,
|
||||
|
#[error("gateway error {0}")]
|
||||
|
GatewayError(#[from] GatewayError),
|
||||
|
|
||||
|
#[error("itemstate error {0}")]
|
||||
|
ItemStateError(Box<ItemStateError>),
|
||||
|
|
||||
|
#[error("magcell error {0}")]
|
||||
|
MagCellError(#[from] MagCellError),
|
||||
|
}
|
||||
|
|
||||
|
impl From<ItemStateError> for ApplyItemError {
|
||||
|
fn from(other: ItemStateError) -> ApplyItemError {
|
||||
|
ApplyItemError::ItemStateError(Box::new(other))
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
// TODO: make all these functions not-pub
|
||||
|
pub async fn power_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<(), ApplyItemError> {
|
||||
|
character.materials.power += 1;
|
||||
|
entity_gateway.save_character(character).await?;
|
||||
|
Ok(())
|
||||
|
}
|
||||
|
|
||||
|
pub async fn mind_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<(), ApplyItemError> {
|
||||
|
character.materials.mind += 1;
|
||||
|
entity_gateway.save_character(character).await.unwrap();
|
||||
|
Ok(())
|
||||
|
}
|
||||
|
|
||||
|
pub async fn evade_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<(), ApplyItemError> {
|
||||
|
character.materials.evade += 1;
|
||||
|
entity_gateway.save_character(character).await.unwrap();
|
||||
|
Ok(())
|
||||
|
}
|
||||
|
|
||||
|
pub async fn def_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<(), ApplyItemError> {
|
||||
|
character.materials.def += 1;
|
||||
|
entity_gateway.save_character(character).await.unwrap();
|
||||
|
Ok(())
|
||||
|
}
|
||||
|
|
||||
|
pub async fn luck_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<(), ApplyItemError> {
|
||||
|
character.materials.luck += 1;
|
||||
|
entity_gateway.save_character(character).await.unwrap();
|
||||
|
Ok(())
|
||||
|
}
|
||||
|
|
||||
|
pub async fn hp_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<(), ApplyItemError> {
|
||||
|
character.materials.hp += 1;
|
||||
|
entity_gateway.save_character(character).await.unwrap();
|
||||
|
Ok(())
|
||||
|
}
|
||||
|
|
||||
|
pub async fn tp_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<(), ApplyItemError> {
|
||||
|
character.materials.tp += 1;
|
||||
|
entity_gateway.save_character(character).await.unwrap();
|
||||
|
Ok(())
|
||||
|
}
|
||||
|
|
||||
|
/*
|
||||
|
async fn mag_cell<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory, mag_cell_type: MagCell) -> Result<(), ApplyItemError> {
|
||||
|
let mut mag_handle = inventory.get_equipped_mag_handle().ok_or(ApplyItemError::ItemNotEquipped)?;
|
||||
|
let mag_item = mag_handle.item_mut()
|
||||
|
.ok_or(ApplyItemError::InvalidItem)?;
|
||||
|
let actual_mag = mag_item
|
||||
|
.individual_mut()
|
||||
|
.ok_or(ApplyItemError::InvalidItem)?
|
||||
|
.mag_mut()
|
||||
|
.ok_or(ApplyItemError::InvalidItem)?;
|
||||
|
actual_mag.apply_mag_cell(mag_cell_type);
|
||||
|
for mag_entity_id in mag_item.entity_ids() {
|
||||
|
for cell_entity_id in used_cell.entity_ids() {
|
||||
|
entity_gateway.use_mag_cell(&mag_entity_id, &cell_entity_id).await.unwrap();
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
Ok(())
|
||||
|
}
|
||||
|
*/
|
||||
|
|
||||
|
|
||||
|
async fn mag_cell<'a, EG>(item_state: &mut ItemStateProxy<'a>,
|
||||
|
entity_gateway: &mut EG,
|
||||
|
character: &CharacterEntity,
|
||||
|
cell_entity_id: ItemEntityId,
|
||||
|
mag_cell_type: MagCell)
|
||||
|
-> Result<(), ApplyItemError>
|
||||
|
where
|
||||
|
EG: EntityGateway + ?Sized,
|
||||
|
{
|
||||
|
let mut inventory = item_state.inventory(&character.id)?;
|
||||
|
|
||||
|
let (mag_entity_id, mag) = inventory.equipped_mag_mut()
|
||||
|
.ok_or(ApplyItemError::ItemNotEquipped)?;
|
||||
|
mag.apply_mag_cell(mag_cell_type)?;
|
||||
|
|
||||
|
entity_gateway.use_mag_cell(&mag_entity_id, &cell_entity_id).await?;
|
||||
|
entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
|
||||
|
item_state.set_inventory(inventory);
|
||||
|
|
||||
|
Ok(())
|
||||
|
}
|
||||
|
|
||||
|
/*
|
||||
|
pub async fn cell_of_mag_502<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||
|
mag_cell(entity_gateway, inventory, MagCell::CellOfMag502).await
|
||||
|
}
|
||||
|
|
||||
|
pub async fn cell_of_mag_213<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::CellOfMag213).await
|
||||
|
}
|
||||
|
|
||||
|
pub async fn parts_of_robochao<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::PartsOfRobochao).await
|
||||
|
}
|
||||
|
|
||||
|
pub async fn heart_of_opaopa<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfOpaOpa).await
|
||||
|
}
|
||||
|
|
||||
|
pub async fn heart_of_pian<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfPian).await
|
||||
|
}
|
||||
|
|
||||
|
pub async fn heart_of_chao<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfChao).await
|
||||
|
}
|
||||
|
|
||||
|
pub async fn heart_of_angel<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfAngel).await
|
||||
|
}
|
||||
|
|
||||
|
pub async fn kit_of_hamburger<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfHamburger).await
|
||||
|
}
|
||||
|
|
||||
|
pub async fn panthers_spirit<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::PanthersSpirit).await
|
||||
|
}
|
||||
|
|
||||
|
pub async fn kit_of_mark3<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfMark3).await
|
||||
|
}
|
||||
|
|
||||
|
pub async fn kit_of_master_system<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfMasterSystem).await
|
||||
|
}
|
||||
|
|
||||
|
pub async fn kit_of_genesis<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfGenesis).await
|
||||
|
}
|
||||
|
|
||||
|
pub async fn kit_of_sega_saturn<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfSegaSaturn).await
|
||||
|
}
|
||||
|
|
||||
|
pub async fn kit_of_dreamcast<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfDreamcast).await
|
||||
|
}
|
||||
|
|
||||
|
pub async fn tablet<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::Tablet).await
|
||||
|
}
|
||||
|
|
||||
|
pub async fn dragon_scale<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::DragonScale).await
|
||||
|
}
|
||||
|
|
||||
|
pub async fn heaven_striker_coat<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::HeavenStrikerCoat).await
|
||||
|
}
|
||||
|
|
||||
|
pub async fn pioneer_parts<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::PioneerParts).await
|
||||
|
}
|
||||
|
|
||||
|
pub async fn amities_memo<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::AmitiesMemo).await
|
||||
|
}
|
||||
|
|
||||
|
pub async fn heart_of_morolian<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfMorolian).await
|
||||
|
}
|
||||
|
|
||||
|
pub async fn rappys_beak<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::RappysBeak).await
|
||||
|
}
|
||||
|
|
||||
|
pub async fn yahoos_engine<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::YahoosEngine).await
|
||||
|
}
|
||||
|
|
||||
|
pub async fn d_photon_core<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::DPhotonCore).await
|
||||
|
}
|
||||
|
|
||||
|
pub async fn liberta_kit<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::LibertaKit).await
|
||||
|
}
|
||||
|
*/
|
||||
|
|
||||
|
async fn apply_tool<'a, EG>(item_state: &mut ItemStateProxy<'a>,
|
||||
|
entity_gateway: &mut EG,
|
||||
|
character: &mut CharacterEntity,
|
||||
|
entity_id: ItemEntityId,
|
||||
|
tool: ToolType)
|
||||
|
-> Result<(), ApplyItemError>
|
||||
|
where
|
||||
|
EG: EntityGateway + ?Sized,
|
||||
|
{
|
||||
|
match tool {
|
||||
|
ToolType::PowerMaterial => power_material(entity_gateway, character).await,
|
||||
|
ToolType::MindMaterial => mind_material(entity_gateway, character).await,
|
||||
|
ToolType::EvadeMaterial => evade_material(entity_gateway, character).await,
|
||||
|
ToolType::DefMaterial => def_material(entity_gateway, character).await,
|
||||
|
ToolType::LuckMaterial => luck_material(entity_gateway, character).await,
|
||||
|
ToolType::HpMaterial => hp_material(entity_gateway, character).await,
|
||||
|
ToolType::TpMaterial => tp_material(entity_gateway, character).await,
|
||||
|
ToolType::Monomate => Ok(()),
|
||||
|
ToolType::Dimate => Ok(()),
|
||||
|
ToolType::Trimate => Ok(()),
|
||||
|
ToolType::Monofluid => Ok(()),
|
||||
|
ToolType::Difluid => Ok(()),
|
||||
|
ToolType::Trifluid => Ok(()),
|
||||
|
ToolType::HuntersReport => Ok(()),
|
||||
|
ToolType::CellOfMag502
|
||||
|
| ToolType::CellOfMag213
|
||||
|
| ToolType::PartsOfRobochao
|
||||
|
| ToolType::HeartOfOpaOpa
|
||||
|
| ToolType::HeartOfPian
|
||||
|
| ToolType::HeartOfChao
|
||||
|
| ToolType::HeartOfAngel
|
||||
|
| ToolType::KitOfHamburger
|
||||
|
| ToolType::PanthersSpirit
|
||||
|
| ToolType::KitOfMark3
|
||||
|
| ToolType::KitOfMasterSystem
|
||||
|
| ToolType::KitOfGenesis
|
||||
|
| ToolType::KitOfSegaSaturn
|
||||
|
| ToolType::KitOfDreamcast
|
||||
|
| ToolType::Tablet
|
||||
|
| ToolType::DragonScale
|
||||
|
| ToolType::HeavenStrikerCoat
|
||||
|
| ToolType::PioneerParts
|
||||
|
| ToolType::AmitiesMemo
|
||||
|
| ToolType::HeartOfMorolian
|
||||
|
| ToolType::RappysBeak
|
||||
|
| ToolType::YahoosEngine
|
||||
|
| ToolType::DPhotonCore
|
||||
|
| ToolType::LibertaKit => {
|
||||
|
mag_cell(item_state, entity_gateway, character, entity_id, tool.try_into()?).await
|
||||
|
}
|
||||
|
// TODO: rest of these
|
||||
|
_ => Err(ApplyItemError::InvalidItem)
|
||||
|
}
|
||||
|
|
||||
|
}
|
||||
|
|
||||
|
|
||||
|
pub async fn apply_item<'a, EG: EntityGateway + ?Sized>(item_state: &mut ItemStateProxy<'a>, entity_gateway: &mut EG, character: &mut CharacterEntity, item: InventoryItem) -> Result<(), ApplyItemError> {
|
||||
|
match item.item {
|
||||
|
InventoryItemDetail::Individual(individual_item) => {
|
||||
|
match individual_item.item {
|
||||
|
ItemDetail::Tool(tool) => apply_tool(item_state, entity_gateway, character, individual_item.entity_id, tool.tool).await,
|
||||
|
_ => Err(ApplyItemError::InvalidItem)
|
||||
|
}
|
||||
|
},
|
||||
|
InventoryItemDetail::Stacked(stacked_item) => {
|
||||
|
for entity_id in stacked_item.entity_ids {
|
||||
|
apply_tool(item_state, entity_gateway, character, entity_id, stacked_item.tool.tool).await?
|
||||
|
}
|
||||
|
Ok(())
|
||||
|
},
|
||||
|
}
|
||||
|
}
|
@ -1,253 +1,139 @@ |
|||||
use crate::ship::items::ClientItemId;
|
use crate::ship::items::ClientItemId;
|
||||
use crate::entity::item::{ItemEntityId, ItemDetail};
|
|
||||
use crate::entity::item::Meseta;
|
|
||||
use crate::entity::item::tool::Tool;
|
|
||||
|
use crate::entity::item::{Meseta, ItemEntityId, ItemDetail};
|
||||
|
use std::future::Future;
|
||||
|
|
||||
use crate::ship::map::MapArea;
|
use crate::ship::map::MapArea;
|
||||
use crate::ship::items::inventory::{IndividualInventoryItem, StackedInventoryItem, InventoryItemHandle};
|
|
||||
|
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};
|
||||
|
|
||||
#[derive(Debug, Clone)]
|
|
||||
pub struct IndividualFloorItem {
|
|
||||
pub entity_id: ItemEntityId,
|
|
||||
pub item_id: ClientItemId,
|
|
||||
pub item: ItemDetail,
|
|
||||
pub map_area: MapArea,
|
|
||||
pub x: f32,
|
|
||||
pub y: f32,
|
|
||||
pub z: f32,
|
|
||||
|
pub enum FloorType {
|
||||
|
Local,
|
||||
|
Shared,
|
||||
}
|
}
|
||||
|
|
||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||
pub struct StackedFloorItem {
|
|
||||
pub entity_ids: Vec<ItemEntityId>,
|
|
||||
pub item_id: ClientItemId,
|
|
||||
pub tool: Tool,
|
|
||||
pub map_area: MapArea,
|
|
||||
pub x: f32,
|
|
||||
pub y: f32,
|
|
||||
pub z: f32,
|
|
||||
}
|
|
||||
|
|
||||
impl StackedFloorItem {
|
|
||||
pub fn count(&self) -> usize {
|
|
||||
self.entity_ids.len()
|
|
||||
}
|
|
||||
|
|
||||
pub fn as_client_bytes(&self) -> [u8; 16] {
|
|
||||
self.tool.as_stacked_bytes(self.count())
|
|
||||
}
|
|
||||
|
pub enum FloorItemDetail {
|
||||
|
Individual(IndividualItemDetail),
|
||||
|
Stacked(StackedItemDetail),
|
||||
|
Meseta(Meseta),
|
||||
}
|
}
|
||||
|
|
||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||
pub struct MesetaFloorItem {
|
|
||||
|
pub struct FloorItem {
|
||||
pub item_id: ClientItemId,
|
pub item_id: ClientItemId,
|
||||
pub meseta: Meseta,
|
|
||||
|
pub item: FloorItemDetail,
|
||||
pub map_area: MapArea,
|
pub map_area: MapArea,
|
||||
pub x: f32,
|
pub x: f32,
|
||||
pub y: f32,
|
pub y: f32,
|
||||
pub z: f32,
|
pub z: f32,
|
||||
}
|
}
|
||||
|
|
||||
#[derive(Debug, Clone)]
|
|
||||
pub enum FloorItem {
|
|
||||
Individual(IndividualFloorItem),
|
|
||||
Stacked(StackedFloorItem),
|
|
||||
Meseta(MesetaFloorItem),
|
|
||||
}
|
|
||||
|
|
||||
impl FloorItem {
|
impl FloorItem {
|
||||
pub fn item_id(&self) -> ClientItemId {
|
|
||||
match self {
|
|
||||
FloorItem::Individual(individual_floor_item) => {
|
|
||||
individual_floor_item.item_id
|
|
||||
},
|
|
||||
FloorItem::Stacked(stacked_floor_item) => {
|
|
||||
stacked_floor_item.item_id
|
|
||||
},
|
|
||||
FloorItem::Meseta(meseta_floor_item) => {
|
|
||||
meseta_floor_item.item_id
|
|
||||
}
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
pub fn x(&self) -> f32 {
|
|
||||
match self {
|
|
||||
FloorItem::Individual(individual_floor_item) => {
|
|
||||
individual_floor_item.x
|
|
||||
},
|
|
||||
FloorItem::Stacked(stacked_floor_item) => {
|
|
||||
stacked_floor_item.x
|
|
||||
},
|
|
||||
FloorItem::Meseta(meseta_floor_item) => {
|
|
||||
meseta_floor_item.x
|
|
||||
}
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
pub fn y(&self) -> f32 {
|
|
||||
match self {
|
|
||||
FloorItem::Individual(individual_floor_item) => {
|
|
||||
individual_floor_item.y
|
|
||||
|
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?;
|
||||
},
|
},
|
||||
FloorItem::Stacked(stacked_floor_item) => {
|
|
||||
stacked_floor_item.y
|
|
||||
|
FloorItemDetail::Stacked(stacked_item) => {
|
||||
|
for entity_id in &stacked_item.entity_ids {
|
||||
|
param = func(param, *entity_id).await?;
|
||||
|
}
|
||||
},
|
},
|
||||
FloorItem::Meseta(meseta_floor_item) => {
|
|
||||
meseta_floor_item.y
|
|
||||
}
|
|
||||
|
FloorItemDetail::Meseta(_meseta) => {},
|
||||
}
|
}
|
||||
}
|
|
||||
|
|
||||
pub fn z(&self) -> f32 {
|
|
||||
match self {
|
|
||||
FloorItem::Individual(individual_floor_item) => {
|
|
||||
individual_floor_item.z
|
|
||||
},
|
|
||||
FloorItem::Stacked(stacked_floor_item) => {
|
|
||||
stacked_floor_item.z
|
|
||||
},
|
|
||||
FloorItem::Meseta(meseta_floor_item) => {
|
|
||||
meseta_floor_item.z
|
|
||||
}
|
|
||||
}
|
|
||||
|
Ok(param)
|
||||
}
|
}
|
||||
|
|
||||
pub fn map_area(&self) -> MapArea {
|
|
||||
match self {
|
|
||||
FloorItem::Individual(individual_floor_item) => {
|
|
||||
individual_floor_item.map_area
|
|
||||
},
|
|
||||
FloorItem::Stacked(stacked_floor_item) => {
|
|
||||
stacked_floor_item.map_area
|
|
||||
},
|
|
||||
FloorItem::Meseta(meseta_floor_item) => {
|
|
||||
meseta_floor_item.map_area
|
|
||||
|
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] {
|
pub fn as_client_bytes(&self) -> [u8; 16] {
|
||||
match self {
|
|
||||
FloorItem::Individual(individual_floor_item) => {
|
|
||||
|
match &self.item {
|
||||
|
FloorItemDetail::Individual(individual_floor_item) => {
|
||||
individual_floor_item.item.as_client_bytes()
|
individual_floor_item.item.as_client_bytes()
|
||||
},
|
},
|
||||
FloorItem::Stacked(stacked_floor_item) => {
|
|
||||
stacked_floor_item.as_client_bytes()
|
|
||||
|
FloorItemDetail::Stacked(stacked_floor_item) => {
|
||||
|
stacked_floor_item.tool.as_stacked_bytes(stacked_floor_item.entity_ids.len())
|
||||
},
|
},
|
||||
FloorItem::Meseta(meseta_floor_item) => {
|
|
||||
meseta_floor_item.meseta.as_bytes()
|
|
||||
|
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>);
|
||||
|
|
||||
|
|
||||
pub struct FloorItemHandle<'a> {
|
|
||||
floor: &'a mut RoomFloorItems,
|
|
||||
index: usize,
|
|
||||
}
|
|
||||
|
|
||||
impl<'a> FloorItemHandle<'a> {
|
|
||||
pub fn item(&'a self) -> Option<&'a FloorItem> {
|
|
||||
self.floor.0.get(self.index)
|
|
||||
}
|
|
||||
|
|
||||
pub fn remove_from_floor(self) {
|
|
||||
self.floor.0.remove(self.index);
|
|
||||
}
|
|
||||
|
#[derive(Debug)]
|
||||
|
pub struct FloorState {
|
||||
|
pub character_id: CharacterEntityId,
|
||||
|
pub local: LocalFloor,
|
||||
|
pub shared: SharedFloor,
|
||||
}
|
}
|
||||
|
|
||||
// TODO: floors should keep track of their own item_ids
|
|
||||
#[derive(Debug, Default)]
|
|
||||
pub struct RoomFloorItems(Vec<FloorItem>);
|
|
||||
|
|
||||
impl RoomFloorItems {
|
|
||||
pub fn add_item(&mut self, item: FloorItem) {
|
|
||||
self.0.push(item);
|
|
||||
}
|
|
||||
|
|
||||
pub fn remove_item(&mut self, item_id: &ClientItemId) {
|
|
||||
self.0.retain(|item| item.item_id() != *item_id);
|
|
||||
}
|
|
||||
|
|
||||
// TODO: &ClientItemId
|
|
||||
pub fn get_item_by_id(&self, item_id: ClientItemId) -> Option<&FloorItem> {
|
|
||||
self.0.iter().find(|item| item.item_id() == item_id)
|
|
||||
}
|
|
||||
|
|
||||
// TODO: &ClientItemId
|
|
||||
pub fn get_item_handle_by_id(&mut self, item_id: ClientItemId) -> Option<FloorItemHandle> {
|
|
||||
let index = self.0.iter().position(|item| item.item_id() == item_id)?;
|
|
||||
Some(FloorItemHandle {
|
|
||||
floor: self,
|
|
||||
index,
|
|
||||
|
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 take_item_by_id(&mut self, item_id: ClientItemId) -> Option<FloorItem> {
|
|
||||
self.0
|
|
||||
.drain_filter(|i| i.item_id() == item_id)
|
|
||||
.next()
|
|
||||
}
|
|
||||
|
|
||||
pub fn drop_individual_inventory_item(&mut self, individual_inventory_item: IndividualInventoryItem, item_drop_location: (MapArea, f32, f32, f32)) -> &IndividualFloorItem {
|
|
||||
self.0.push(FloorItem::Individual(IndividualFloorItem {
|
|
||||
entity_id: individual_inventory_item.entity_id,
|
|
||||
item_id: individual_inventory_item.item_id,
|
|
||||
item: individual_inventory_item.item,
|
|
||||
map_area: item_drop_location.0,
|
|
||||
x: item_drop_location.1,
|
|
||||
y: item_drop_location.2,
|
|
||||
z: item_drop_location.3,
|
|
||||
}));
|
|
||||
|
|
||||
match self.0.last().unwrap() {
|
|
||||
FloorItem::Individual(item) => item,
|
|
||||
_ => unreachable!(),
|
|
||||
}
|
|
||||
}
|
|
||||
|
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,
|
||||
|
};
|
||||
|
|
||||
pub fn drop_stacked_inventory_item(&mut self, stacked_inventory_item: StackedInventoryItem, item_drop_location: (MapArea, f32, f32, f32)) -> &StackedFloorItem {
|
|
||||
self.0.push(FloorItem::Stacked(StackedFloorItem {
|
|
||||
entity_ids: stacked_inventory_item.entity_ids,
|
|
||||
item_id: stacked_inventory_item.item_id,
|
|
||||
tool: stacked_inventory_item.tool,
|
|
||||
map_area: item_drop_location.0,
|
|
||||
x: item_drop_location.1,
|
|
||||
y: item_drop_location.2,
|
|
||||
z: item_drop_location.3,
|
|
||||
}));
|
|
||||
|
|
||||
match self.0.last().unwrap() {
|
|
||||
FloorItem::Stacked(item) => item,
|
|
||||
_ => unreachable!(),
|
|
||||
}
|
|
||||
|
self.shared.0.push(floor_item);
|
||||
|
&self.shared.0[self.shared.0.len()-1]
|
||||
}
|
}
|
||||
|
|
||||
// TODO: Result
|
|
||||
// TODO: if consumed_item is not a tool items do not get placed back into inventory (should I care?)
|
|
||||
pub fn drop_partial_stacked_inventory_item(&mut self, inventory_item: InventoryItemHandle, amount: usize, new_item_id: ClientItemId, item_drop_location: (MapArea, f32, f32, f32)) -> Option<&StackedFloorItem> {
|
|
||||
let consumed_item = inventory_item.consume(amount).ok()?;
|
|
||||
|
|
||||
if let ItemDetail::Tool(tool) = consumed_item.item() {
|
|
||||
self.0.push(FloorItem::Stacked(StackedFloorItem {
|
|
||||
entity_ids: consumed_item.entity_ids(),
|
|
||||
item_id: new_item_id,
|
|
||||
tool,
|
|
||||
map_area: item_drop_location.0,
|
|
||||
x: item_drop_location.1,
|
|
||||
y: item_drop_location.2,
|
|
||||
z: item_drop_location.3,
|
|
||||
}))
|
|
||||
}
|
|
||||
else {
|
|
||||
return None
|
|
||||
}
|
|
||||
|
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]
|
||||
|
}
|
||||
|
|
||||
match self.0.last().unwrap() {
|
|
||||
FloorItem::Stacked(item) => Some(item),
|
|
||||
_ => unreachable!(),
|
|
||||
}
|
|
||||
|
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]
|
||||
}
|
}
|
||||
}
|
}
|
||||
|
|
1083
src/ship/items/inventory.rs
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
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, ()))
|
||||
|
}
|
||||
|
}
|
@ -1,18 +1,11 @@ |
|||||
mod bank;
|
|
||||
mod floor;
|
|
||||
|
pub mod state;
|
||||
|
pub mod actions;
|
||||
|
pub mod apply_item;
|
||||
|
pub mod itemstateaction;
|
||||
pub mod inventory;
|
pub mod inventory;
|
||||
pub mod manager;
|
|
||||
pub mod transaction;
|
|
||||
pub mod use_tool;
|
|
||||
use serde::{Serialize, Deserialize};
|
|
||||
|
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);
|
pub struct ClientItemId(pub u32);
|
||||
|
|
||||
// TODO: remove these and fix use statements in the rest of the codebase
|
|
||||
pub use inventory::*;
|
|
||||
pub use floor::*;
|
|
||||
pub use bank::*;
|
|
||||
pub use manager::*;
|
|
||||
|
|
||||
|
|
@ -0,0 +1,391 @@ |
|||||
|
use std::collections::HashMap;
|
||||
|
use crate::ship::items::ClientItemId;
|
||||
|
use crate::entity::item::{ItemEntityId, ItemDetail, ItemEntity, InventoryItemEntity, BankItemEntity, BankName};
|
||||
|
|
||||
|
use crate::ship::location::{AreaClient, RoomId};
|
||||
|
use crate::entity::character::{CharacterEntity, CharacterEntityId};
|
||||
|
use crate::entity::gateway::{EntityGateway, GatewayError};
|
||||
|
use crate::entity::item::tool::Tool;
|
||||
|
use crate::entity::item::weapon::Weapon;
|
||||
|
use crate::entity::item::mag::Mag;
|
||||
|
use crate::ship::drops::ItemDrop;
|
||||
|
|
||||
|
use crate::ship::items::inventory::{Inventory, InventoryItem, InventoryItemDetail, InventoryError, InventoryState};
|
||||
|
use crate::ship::items::floor::{FloorState, FloorItem, LocalFloor, SharedFloor, FloorType};
|
||||
|
use crate::ship::items::bank::{Bank, BankState, BankItem, BankItemDetail, BankError};
|
||||
|
|
||||
|
#[derive(thiserror::Error, Debug)]
|
||||
|
pub enum ItemStateError {
|
||||
|
#[error("character {0} not found")]
|
||||
|
NoCharacter(CharacterEntityId),
|
||||
|
#[error("room {0} not found")]
|
||||
|
NoRoom(RoomId),
|
||||
|
#[error("floor item {0} not found")]
|
||||
|
NoFloorItem(ClientItemId),
|
||||
|
|
||||
|
#[error("expected {0} to be a tool")]
|
||||
|
NotATool(ClientItemId),
|
||||
|
|
||||
|
#[error("bank item {0} not found")]
|
||||
|
NoBankItem(ClientItemId),
|
||||
|
|
||||
|
#[error("inventory error {0}")]
|
||||
|
InventoryError(#[from] InventoryError),
|
||||
|
|
||||
|
#[error("bank error {0}")]
|
||||
|
BankError(#[from] BankError),
|
||||
|
|
||||
|
#[error("invalid item id {0}")]
|
||||
|
InvalidItemId(ClientItemId),
|
||||
|
|
||||
|
#[error("invalid drop? {0:?} (this shouldn't occur)")]
|
||||
|
BadItemDrop(ItemDrop),
|
||||
|
|
||||
|
#[error("idk")]
|
||||
|
Dummy,
|
||||
|
|
||||
|
#[error("gateway")]
|
||||
|
GatewayError(#[from] GatewayError),
|
||||
|
|
||||
|
#[error("tried to remove more meseta than exists: {0}")]
|
||||
|
InvalidMesetaRemoval(u32),
|
||||
|
|
||||
|
#[error("tried to add meseta when there is no more room")]
|
||||
|
FullOfMeseta,
|
||||
|
|
||||
|
#[error("stacked item")]
|
||||
|
StackedItemError(Vec<ItemEntity>),
|
||||
|
|
||||
|
#[error("apply item {0}")]
|
||||
|
ApplyItemError(#[from] crate::ship::items::apply_item::ApplyItemError),
|
||||
|
|
||||
|
#[error("item is not a mag {0}")]
|
||||
|
NotAMag(ClientItemId),
|
||||
|
|
||||
|
#[error("item is not mag food {0}")]
|
||||
|
NotMagFood(ClientItemId),
|
||||
|
|
||||
|
#[error("item is not sellable")]
|
||||
|
ItemNotSellable,
|
||||
|
|
||||
|
#[error("could not modify item")]
|
||||
|
InvalidModifier,
|
||||
|
|
||||
|
#[error("wrong item type ")]
|
||||
|
WrongItemType(ClientItemId),
|
||||
|
}
|
||||
|
|
||||
|
|
||||
|
#[derive(Clone, Debug)]
|
||||
|
pub struct IndividualItemDetail {
|
||||
|
pub entity_id: ItemEntityId,
|
||||
|
pub item: ItemDetail,
|
||||
|
}
|
||||
|
|
||||
|
impl IndividualItemDetail {
|
||||
|
pub fn as_weapon(&self) -> Option<&Weapon> {
|
||||
|
match &self.item {
|
||||
|
ItemDetail::Weapon(weapon) => Some(weapon),
|
||||
|
_ => None
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
pub fn as_mag(&self) -> Option<&Mag> {
|
||||
|
match &self.item {
|
||||
|
ItemDetail::Mag(mag) => Some(mag),
|
||||
|
_ => None
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
pub fn as_mag_mut(&mut self) -> Option<&mut Mag> {
|
||||
|
match &mut self.item {
|
||||
|
ItemDetail::Mag(mag) => Some(mag),
|
||||
|
_ => None
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
pub fn as_client_bytes(&self) -> [u8; 16] {
|
||||
|
match &self.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(),
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
}
|
||||
|
|
||||
|
#[derive(Clone, Debug)]
|
||||
|
pub struct StackedItemDetail {
|
||||
|
pub entity_ids: Vec<ItemEntityId>,
|
||||
|
pub tool: Tool,
|
||||
|
}
|
||||
|
|
||||
|
impl StackedItemDetail {
|
||||
|
pub fn count(&self) -> usize {
|
||||
|
self.entity_ids.len()
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
|
||||
|
|
||||
|
#[derive(Clone)]
|
||||
|
pub enum AddItemResult {
|
||||
|
NewItem,
|
||||
|
AddToStack,
|
||||
|
Meseta,
|
||||
|
}
|
||||
|
|
||||
|
|
||||
|
pub struct ItemState {
|
||||
|
character_inventory: HashMap<CharacterEntityId, InventoryState>,
|
||||
|
character_bank: HashMap<CharacterEntityId, BankState>,
|
||||
|
|
||||
|
character_room: HashMap<CharacterEntityId, RoomId>,
|
||||
|
character_floor: HashMap<CharacterEntityId, LocalFloor>,
|
||||
|
room_floor: HashMap<RoomId, SharedFloor>,
|
||||
|
|
||||
|
room_item_id_counter: u32,
|
||||
|
}
|
||||
|
|
||||
|
impl Default for ItemState {
|
||||
|
fn default() -> ItemState {
|
||||
|
ItemState {
|
||||
|
character_inventory: HashMap::new(),
|
||||
|
character_bank: HashMap::new(),
|
||||
|
character_room: HashMap::new(),
|
||||
|
character_floor: HashMap::new(),
|
||||
|
room_floor: HashMap::new(),
|
||||
|
room_item_id_counter: 0x00810000,
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl ItemState {
|
||||
|
pub fn get_character_inventory(&self, character: &CharacterEntity) -> Result<&InventoryState, ItemStateError> {
|
||||
|
self.character_inventory.get(&character.id)
|
||||
|
.ok_or(ItemStateError::NoCharacter(character.id))
|
||||
|
}
|
||||
|
|
||||
|
pub fn get_character_bank(&self, character: &CharacterEntity) -> Result<&BankState, ItemStateError> {
|
||||
|
self.character_bank.get(&character.id)
|
||||
|
.ok_or(ItemStateError::NoCharacter(character.id))
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl ItemState {
|
||||
|
fn new_item_id(&mut self) -> Result<ClientItemId, ItemStateError> {
|
||||
|
self.room_item_id_counter += 1;
|
||||
|
Ok(ClientItemId(self.room_item_id_counter))
|
||||
|
}
|
||||
|
|
||||
|
pub async fn load_character<EG: EntityGateway>(&mut self, entity_gateway: &mut EG, character: &CharacterEntity) -> Result<(), ItemStateError> {
|
||||
|
let inventory = entity_gateway.get_character_inventory(&character.id).await?;
|
||||
|
let bank = entity_gateway.get_character_bank(&character.id, &BankName("".into())).await?;
|
||||
|
let equipped = entity_gateway.get_character_equips(&character.id).await?;
|
||||
|
|
||||
|
let inventory_items = inventory.items.into_iter()
|
||||
|
.map(|item| -> Result<InventoryItem, ItemStateError> {
|
||||
|
Ok(match item {
|
||||
|
InventoryItemEntity::Individual(item) => {
|
||||
|
InventoryItem {
|
||||
|
item_id: ClientItemId(0),
|
||||
|
item: InventoryItemDetail::Individual(IndividualItemDetail {
|
||||
|
entity_id: item.id,
|
||||
|
item: item.item,
|
||||
|
}),
|
||||
|
}
|
||||
|
},
|
||||
|
InventoryItemEntity::Stacked(items) => {
|
||||
|
InventoryItem {
|
||||
|
item_id: ClientItemId(0),
|
||||
|
item: InventoryItemDetail::Stacked(StackedItemDetail {
|
||||
|
entity_ids: items.iter().map(|i| i.id).collect(),
|
||||
|
tool: items.get(0)
|
||||
|
.ok_or_else(|| ItemStateError::StackedItemError(items.clone()))?
|
||||
|
.item
|
||||
|
.clone()
|
||||
|
.as_tool()
|
||||
|
.ok_or_else(|| ItemStateError::StackedItemError(items.clone()))?
|
||||
|
})
|
||||
|
}
|
||||
|
},
|
||||
|
})
|
||||
|
})
|
||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
|
||||
|
let character_meseta = entity_gateway.get_character_meseta(&character.id).await?;
|
||||
|
let inventory_state = InventoryState {
|
||||
|
character_id: character.id,
|
||||
|
item_id_counter: 0,
|
||||
|
inventory: Inventory::new(inventory_items),
|
||||
|
equipped,
|
||||
|
meseta: character_meseta,
|
||||
|
};
|
||||
|
|
||||
|
let bank_items = bank.items.into_iter()
|
||||
|
.map(|item| -> Result<BankItem, ItemStateError> {
|
||||
|
Ok(match item {
|
||||
|
BankItemEntity::Individual(item) => {
|
||||
|
BankItem {
|
||||
|
item_id: self.new_item_id()?,
|
||||
|
item: BankItemDetail::Individual(IndividualItemDetail {
|
||||
|
entity_id: item.id,
|
||||
|
item: item.item,
|
||||
|
})
|
||||
|
}
|
||||
|
},
|
||||
|
BankItemEntity::Stacked(items) => {
|
||||
|
BankItem {
|
||||
|
item_id: self.new_item_id()?,
|
||||
|
item: BankItemDetail::Stacked(StackedItemDetail {
|
||||
|
entity_ids: items.iter().map(|i| i.id).collect(),
|
||||
|
tool: items.get(0)
|
||||
|
.ok_or_else(|| ItemStateError::StackedItemError(items.clone()))?
|
||||
|
.item
|
||||
|
.clone()
|
||||
|
.as_tool()
|
||||
|
.ok_or_else(|| ItemStateError::StackedItemError(items.clone()))?
|
||||
|
})
|
||||
|
}
|
||||
|
},
|
||||
|
})
|
||||
|
})
|
||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
|
||||
|
let bank_meseta = entity_gateway.get_bank_meseta(&character.id, &BankName("".into())).await?;
|
||||
|
let bank_state = BankState::new(character.id, BankName("".into()), Bank::new(bank_items), bank_meseta);
|
||||
|
|
||||
|
self.character_inventory.insert(character.id, inventory_state);
|
||||
|
self.character_bank.insert(character.id, bank_state);
|
||||
|
Ok(())
|
||||
|
}
|
||||
|
|
||||
|
pub fn add_character_to_room(&mut self, room_id: RoomId, character: &CharacterEntity, area_client: AreaClient) {
|
||||
|
let base_inventory_id = ((area_client.local_client.id() as u32) << 21) | 0x10000;
|
||||
|
let inventory = self.character_inventory.get_mut(&character.id).unwrap();
|
||||
|
inventory.initialize_item_ids(base_inventory_id);
|
||||
|
let base_bank_id = ((area_client.local_client.id() as u32) << 21) | 0x20000;
|
||||
|
let default_bank = self.character_bank.get_mut(&character.id);
|
||||
|
if let Some(default_bank ) = default_bank {
|
||||
|
default_bank.initialize_item_ids(base_bank_id);
|
||||
|
}
|
||||
|
self.character_room.insert(character.id, room_id);
|
||||
|
self.character_floor.insert(character.id, LocalFloor::default());
|
||||
|
self.room_floor.entry(room_id).or_insert_with(SharedFloor::default);
|
||||
|
}
|
||||
|
|
||||
|
pub fn remove_character_from_room(&mut self, character: &CharacterEntity) {
|
||||
|
self.character_inventory.remove(&character.id);
|
||||
|
self.character_floor.remove(&character.id);
|
||||
|
if let Some(room) = self.character_room.remove(&character.id).as_ref() {
|
||||
|
if self.character_room.iter().any(|(_, r)| r == room) {
|
||||
|
self.room_floor.remove(room);
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
pub fn get_floor_item(&self, character_id: &CharacterEntityId, item_id: &ClientItemId) -> Result<(&FloorItem, FloorType), ItemStateError> {
|
||||
|
let local_floor = self.character_floor.get(character_id).ok_or(ItemStateError::NoCharacter(*character_id))?;
|
||||
|
let room = self.character_room.get(character_id).ok_or(ItemStateError::NoCharacter(*character_id))?;
|
||||
|
let shared_floor = self.room_floor.get(room).ok_or(ItemStateError::NoCharacter(*character_id))?;
|
||||
|
|
||||
|
local_floor.0
|
||||
|
.iter()
|
||||
|
.find(|item| item.item_id == *item_id)
|
||||
|
.map(|item| (item, FloorType::Local))
|
||||
|
.or_else(|| {
|
||||
|
shared_floor.0
|
||||
|
.iter()
|
||||
|
.find(|item| item.item_id == *item_id)
|
||||
|
.map(|item| (item, FloorType::Shared))
|
||||
|
})
|
||||
|
.ok_or(ItemStateError::NoFloorItem(*item_id))
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
|
||||
|
#[derive(Default)]
|
||||
|
struct ProxiedItemState {
|
||||
|
character_inventory: HashMap<CharacterEntityId, InventoryState>,
|
||||
|
character_bank: HashMap<CharacterEntityId, BankState>,
|
||||
|
|
||||
|
character_room: HashMap<CharacterEntityId, RoomId>,
|
||||
|
character_floor: HashMap<CharacterEntityId, LocalFloor>,
|
||||
|
room_floor: HashMap<RoomId, SharedFloor>,
|
||||
|
}
|
||||
|
|
||||
|
pub struct ItemStateProxy<'a> {
|
||||
|
item_state: &'a mut ItemState,
|
||||
|
proxied_state: ProxiedItemState,
|
||||
|
}
|
||||
|
|
||||
|
impl<'a> ItemStateProxy<'a> {
|
||||
|
pub fn commit(self) {
|
||||
|
self.item_state.character_inventory.extend(self.proxied_state.character_inventory.clone());
|
||||
|
self.item_state.character_bank.extend(self.proxied_state.character_bank.clone());
|
||||
|
self.item_state.character_room.extend(self.proxied_state.character_room.clone());
|
||||
|
self.item_state.character_floor.extend(self.proxied_state.character_floor.clone());
|
||||
|
self.item_state.room_floor.extend(self.proxied_state.room_floor.clone());
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
|
||||
|
fn get_or_clone<K, V>(master: &HashMap<K, V>, proxy: &mut HashMap<K, V>, key: K, err: fn(K) -> ItemStateError) -> Result<V, ItemStateError>
|
||||
|
where
|
||||
|
K: Eq + std::hash::Hash + Copy,
|
||||
|
V: Clone |
||||
|
{
|
||||
|
let existing_element = master.get(&key).ok_or_else(|| err(key))?;
|
||||
|
Ok(proxy.entry(key)
|
||||
|
.or_insert_with(|| existing_element.clone()).clone())
|
||||
|
|
||||
|
}
|
||||
|
|
||||
|
impl<'a> ItemStateProxy<'a> {
|
||||
|
pub fn new(item_state: &'a mut ItemState) -> Self {
|
||||
|
ItemStateProxy {
|
||||
|
item_state,
|
||||
|
proxied_state: Default::default(),
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
pub fn inventory(&mut self, character_id: &CharacterEntityId) -> Result<InventoryState, ItemStateError> {
|
||||
|
get_or_clone(&self.item_state.character_inventory, &mut self.proxied_state.character_inventory, *character_id, ItemStateError::NoCharacter)
|
||||
|
}
|
||||
|
|
||||
|
pub fn set_inventory(&mut self, inventory: InventoryState) {
|
||||
|
self.proxied_state.character_inventory.insert(inventory.character_id, inventory);
|
||||
|
}
|
||||
|
|
||||
|
pub fn bank(&mut self, character_id: &CharacterEntityId) -> Result<BankState, ItemStateError> {
|
||||
|
get_or_clone(&self.item_state.character_bank, &mut self.proxied_state.character_bank, *character_id, ItemStateError::NoCharacter)
|
||||
|
}
|
||||
|
|
||||
|
pub fn set_bank(&mut self, bank: BankState) {
|
||||
|
self.proxied_state.character_bank.insert(bank.character_id, bank);
|
||||
|
}
|
||||
|
|
||||
|
pub fn floor(&mut self, character_id: &CharacterEntityId) -> Result<FloorState, ItemStateError> {
|
||||
|
let room_id = get_or_clone(&self.item_state.character_room, &mut self.proxied_state.character_room, *character_id, ItemStateError::NoCharacter)?;
|
||||
|
Ok(FloorState {
|
||||
|
character_id: *character_id,
|
||||
|
local: get_or_clone(&self.item_state.character_floor, &mut self.proxied_state.character_floor, *character_id, ItemStateError::NoCharacter)?,
|
||||
|
shared: get_or_clone(&self.item_state.room_floor, &mut self.proxied_state.room_floor, room_id, ItemStateError::NoRoom)?,
|
||||
|
})
|
||||
|
}
|
||||
|
|
||||
|
pub fn set_floor(&mut self, floor: FloorState) {
|
||||
|
let room_id = get_or_clone(&self.item_state.character_room, &mut self.proxied_state.character_room, floor.character_id, ItemStateError::NoCharacter).unwrap();
|
||||
|
self.proxied_state.character_floor.insert(floor.character_id, floor.local);
|
||||
|
self.proxied_state.room_floor.insert(room_id, floor.shared);
|
||||
|
}
|
||||
|
|
||||
|
pub fn new_item_id(&mut self) -> Result<ClientItemId, ItemStateError> {
|
||||
|
self.item_state.new_item_id()
|
||||
|
}
|
||||
|
}
|
@ -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
|
||||
|
}
|
@ -1,337 +0,0 @@ |
|||||
use crate::entity::gateway::EntityGateway;
|
|
||||
use thiserror::Error;
|
|
||||
use crate::ship::items::manager::{ItemManager, ItemManagerError};
|
|
||||
use crate::entity::gateway::GatewayError;
|
|
||||
|
|
||||
#[derive(Error, Debug)]
|
|
||||
pub enum TransactionCommitError {
|
|
||||
#[error("transaction commit gateway error {0}")]
|
|
||||
Gateway(#[from] GatewayError),
|
|
||||
#[error("transaction commit itemmanager error {0}")]
|
|
||||
ItemManager(#[from] ItemManagerError),
|
|
||||
}
|
|
||||
|
|
||||
#[async_trait::async_trait]
|
|
||||
pub trait ItemAction<EG: EntityGateway>: std::marker::Send + std::marker::Sync + std::fmt::Debug {
|
|
||||
async fn commit(&self, manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError>;
|
|
||||
}
|
|
||||
|
|
||||
pub struct ItemTransactionActions<'a, EG: EntityGateway> {
|
|
||||
action_queue: Vec<Box<dyn ItemAction<EG>>>,
|
|
||||
pub manager: &'a ItemManager,
|
|
||||
}
|
|
||||
|
|
||||
|
|
||||
impl<'a, EG: EntityGateway> ItemTransactionActions<'a, EG> {
|
|
||||
fn new(manager: &'a ItemManager) -> ItemTransactionActions<'a, EG> {
|
|
||||
ItemTransactionActions {
|
|
||||
action_queue: Vec::new(),
|
|
||||
manager
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
pub fn action(&mut self, action: Box<dyn ItemAction<EG>>) {
|
|
||||
self.action_queue.push(action)
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
|
|
||||
pub struct ItemTransaction<'a, T, EG: EntityGateway> {
|
|
||||
data: T,
|
|
||||
actions: ItemTransactionActions<'a, EG>,
|
|
||||
}
|
|
||||
|
|
||||
impl<'a, T, EG: EntityGateway> ItemTransaction<'a, T, EG> {
|
|
||||
pub fn new(manager: &'a ItemManager, arg: T) -> ItemTransaction<'a, T, EG> {
|
|
||||
ItemTransaction {
|
|
||||
data: arg,
|
|
||||
actions: ItemTransactionActions::new(manager),
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
pub fn act<E: std::fmt::Debug, U>(mut self, action: fn(&mut ItemTransactionActions<EG>, &T) -> Result<U, E>) -> FinalizedItemTransaction<U, E, EG> {
|
|
||||
match action(&mut self.actions, &self.data) {
|
|
||||
Ok(k) => {
|
|
||||
FinalizedItemTransaction {
|
|
||||
value: Ok(k),
|
|
||||
action_queue: self.actions.action_queue,
|
|
||||
}
|
|
||||
},
|
|
||||
Err(err) => {
|
|
||||
FinalizedItemTransaction {
|
|
||||
value: Err(err),
|
|
||||
action_queue: Vec::new(),
|
|
||||
}
|
|
||||
}
|
|
||||
}
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
|
|
||||
#[derive(Error, Debug)]
|
|
||||
pub enum TransactionError<E: std::fmt::Debug> {
|
|
||||
#[error("transaction action error {0:?}")]
|
|
||||
Action(E),
|
|
||||
#[error("transaction commit error {0}")]
|
|
||||
Commit(#[from] TransactionCommitError),
|
|
||||
|
|
||||
}
|
|
||||
|
|
||||
// this only exists to drop the ItemManager borrow of ItemTransaction so a mutable ItemTransaction can be passed in later
|
|
||||
pub struct FinalizedItemTransaction<T, E: std::fmt::Debug, EG: EntityGateway> {
|
|
||||
value: Result<T, E>,
|
|
||||
action_queue: Vec<Box<dyn ItemAction<EG>>>,
|
|
||||
}
|
|
||||
|
|
||||
impl<T, E: std::fmt::Debug, EG: EntityGateway> FinalizedItemTransaction<T, E, EG> {
|
|
||||
pub async fn commit(self, item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<T, TransactionError<E>> {
|
|
||||
match self.value {
|
|
||||
Ok(value) => {
|
|
||||
for action in self.action_queue.into_iter() {
|
|
||||
// TODO: better handle rolling back if this ever errors out
|
|
||||
action.commit(item_manager, entity_gateway).await.map_err(|err| TransactionError::Commit(err))?;
|
|
||||
}
|
|
||||
Ok(value)
|
|
||||
},
|
|
||||
Err(err) => Err(TransactionError::Action(err)),
|
|
||||
}
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
|
|
||||
#[cfg(test)]
|
|
||||
mod test {
|
|
||||
use super::*;
|
|
||||
use crate::entity::account::{UserAccountId, NewUserAccountEntity, UserAccountEntity};
|
|
||||
use crate::entity::character::{NewCharacterEntity, CharacterEntity};
|
|
||||
use crate::entity::gateway::GatewayError;
|
|
||||
use thiserror::Error;
|
|
||||
|
|
||||
#[async_std::test]
|
|
||||
async fn test_item_transaction() {
|
|
||||
#[derive(Debug)]
|
|
||||
struct DummyAction1 {
|
|
||||
name: String,
|
|
||||
}
|
|
||||
#[derive(Debug)]
|
|
||||
struct DummyAction2 {
|
|
||||
value: u32,
|
|
||||
}
|
|
||||
|
|
||||
#[derive(Error, Debug)]
|
|
||||
#[error("")]
|
|
||||
enum DummyError {
|
|
||||
Error
|
|
||||
}
|
|
||||
|
|
||||
#[derive(Default, Clone)]
|
|
||||
struct DummyGateway {
|
|
||||
d1_set: String,
|
|
||||
d2_inc: u32,
|
|
||||
}
|
|
||||
|
|
||||
#[async_trait::async_trait]
|
|
||||
impl EntityGateway for DummyGateway {
|
|
||||
async fn create_user(&mut self, user: NewUserAccountEntity) -> Result<UserAccountEntity, GatewayError> {
|
|
||||
self.d1_set = user.username;
|
|
||||
Ok(UserAccountEntity::default())
|
|
||||
}
|
|
||||
|
|
||||
async fn create_character(&mut self, char: NewCharacterEntity) -> Result<CharacterEntity, GatewayError> {
|
|
||||
self.d2_inc += char.slot;
|
|
||||
Ok(CharacterEntity::default())
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
|
|
||||
#[async_trait::async_trait]
|
|
||||
impl<EG: EntityGateway> ItemAction<EG> for DummyAction1 {
|
|
||||
async fn commit(&self, item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> {
|
|
||||
item_manager.id_counter = 55555;
|
|
||||
entity_gateway.create_user(NewUserAccountEntity {
|
|
||||
username: self.name.clone(),
|
|
||||
..NewUserAccountEntity::default()
|
|
||||
})
|
|
||||
.await?;
|
|
||||
Ok(())
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
#[async_trait::async_trait]
|
|
||||
impl<EG: EntityGateway> ItemAction<EG> for DummyAction2 {
|
|
||||
async fn commit(&self, item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> {
|
|
||||
item_manager.id_counter += self.value;
|
|
||||
entity_gateway.create_character(NewCharacterEntity {
|
|
||||
slot: self.value,
|
|
||||
..NewCharacterEntity::new(UserAccountId(0), 1) // TODO: handle different keyboard_config_presets
|
|
||||
})
|
|
||||
.await?;
|
|
||||
Ok(())
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
let mut item_manager = ItemManager::default();
|
|
||||
let mut entity_gateway = DummyGateway::default();
|
|
||||
|
|
||||
let result = ItemTransaction::new(&item_manager, 12)
|
|
||||
.act(|it, k| {
|
|
||||
it.action(Box::new(DummyAction1 {name: "asdf".into()}));
|
|
||||
it.action(Box::new(DummyAction2 {value: 11}));
|
|
||||
it.action(Box::new(DummyAction2 {value: *k}));
|
|
||||
if *k == 99 {
|
|
||||
return Err(DummyError::Error)
|
|
||||
}
|
|
||||
Ok(String::from("hello"))
|
|
||||
})
|
|
||||
.commit(&mut item_manager, &mut entity_gateway)
|
|
||||
.await;
|
|
||||
|
|
||||
assert!(entity_gateway.d1_set == "asdf");
|
|
||||
assert!(entity_gateway.d2_inc == 23);
|
|
||||
assert!(item_manager.id_counter == 55578);
|
|
||||
assert!(result.unwrap() == "hello");
|
|
||||
}
|
|
||||
|
|
||||
#[async_std::test]
|
|
||||
async fn test_item_transaction_with_action_error() {
|
|
||||
#[derive(Debug)]
|
|
||||
struct DummyAction1 {
|
|
||||
}
|
|
||||
#[derive(Debug)]
|
|
||||
struct DummyAction2 {
|
|
||||
}
|
|
||||
|
|
||||
#[derive(Error, Debug, PartialEq, Eq)]
|
|
||||
#[error("")]
|
|
||||
enum DummyError {
|
|
||||
Error
|
|
||||
}
|
|
||||
|
|
||||
#[derive(Default, Clone)]
|
|
||||
struct DummyGateway {
|
|
||||
_d1_set: String,
|
|
||||
d2_inc: u32,
|
|
||||
}
|
|
||||
|
|
||||
#[async_trait::async_trait]
|
|
||||
impl EntityGateway for DummyGateway {
|
|
||||
async fn create_character(&mut self, char: NewCharacterEntity) -> Result<CharacterEntity, GatewayError> {
|
|
||||
self.d2_inc += char.slot;
|
|
||||
Ok(CharacterEntity::default())
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
|
|
||||
#[async_trait::async_trait]
|
|
||||
impl<EG: EntityGateway> ItemAction<EG> for DummyAction1 {
|
|
||||
async fn commit(&self, _item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> {
|
|
||||
entity_gateway.create_character(NewCharacterEntity {
|
|
||||
slot: 1,
|
|
||||
..NewCharacterEntity::new(UserAccountId(0), 1) // TODO: handle different keyboard_config_presets
|
|
||||
})
|
|
||||
.await?;
|
|
||||
Ok(())
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
#[async_trait::async_trait]
|
|
||||
impl<EG: EntityGateway> ItemAction<EG> for DummyAction2 {
|
|
||||
async fn commit(&self, _item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> {
|
|
||||
entity_gateway.create_character(NewCharacterEntity {
|
|
||||
slot: 1,
|
|
||||
..NewCharacterEntity::new(UserAccountId(0), 1) // TODO: handle different keyboard_config_presets
|
|
||||
})
|
|
||||
.await?;
|
|
||||
Ok(())
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
let mut item_manager = ItemManager::default();
|
|
||||
let mut entity_gateway = DummyGateway::default();
|
|
||||
|
|
||||
let result = ItemTransaction::new(&item_manager, 12)
|
|
||||
.act(|it, _| -> Result<(), _> {
|
|
||||
it.action(Box::new(DummyAction1 {}));
|
|
||||
it.action(Box::new(DummyAction2 {}));
|
|
||||
it.action(Box::new(DummyAction2 {}));
|
|
||||
Err(DummyError::Error)
|
|
||||
})
|
|
||||
.commit(&mut item_manager, &mut entity_gateway)
|
|
||||
.await;
|
|
||||
|
|
||||
assert!(entity_gateway.d2_inc == 0);
|
|
||||
assert!(matches!(result, Err(TransactionError::Action(DummyError::Error))));
|
|
||||
}
|
|
||||
|
|
||||
#[async_std::test]
|
|
||||
async fn test_item_transaction_with_commit_error() {
|
|
||||
#[derive(Debug)]
|
|
||||
struct DummyAction1 {
|
|
||||
}
|
|
||||
#[derive(Debug)]
|
|
||||
struct DummyAction2 {
|
|
||||
}
|
|
||||
|
|
||||
#[derive(Error, Debug, PartialEq, Eq)]
|
|
||||
#[error("")]
|
|
||||
enum DummyError {
|
|
||||
}
|
|
||||
|
|
||||
#[derive(Default, Clone)]
|
|
||||
struct DummyGateway {
|
|
||||
_d1_set: String,
|
|
||||
d2_inc: u32,
|
|
||||
}
|
|
||||
|
|
||||
#[async_trait::async_trait]
|
|
||||
impl EntityGateway for DummyGateway {
|
|
||||
async fn create_character(&mut self, char: NewCharacterEntity) -> Result<CharacterEntity, GatewayError> {
|
|
||||
self.d2_inc += char.slot;
|
|
||||
Ok(CharacterEntity::default())
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
|
|
||||
#[async_trait::async_trait]
|
|
||||
impl<EG: EntityGateway> ItemAction<EG> for DummyAction1 {
|
|
||||
async fn commit(&self, _item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> {
|
|
||||
entity_gateway.create_character(NewCharacterEntity {
|
|
||||
slot: 1,
|
|
||||
..NewCharacterEntity::new(UserAccountId(0), 1) // TODO: handle different keyboard_config_presets
|
|
||||
})
|
|
||||
.await?;
|
|
||||
Err(GatewayError::Error.into())
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
#[async_trait::async_trait]
|
|
||||
impl<EG: EntityGateway> ItemAction<EG> for DummyAction2 {
|
|
||||
async fn commit(&self, _item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> {
|
|
||||
entity_gateway.create_character(NewCharacterEntity {
|
|
||||
slot: 1,
|
|
||||
..NewCharacterEntity::new(UserAccountId(0), 1) // TODO: handle different keyboard_config_presets
|
|
||||
})
|
|
||||
.await?;
|
|
||||
Ok(())
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
let mut item_manager = ItemManager::default();
|
|
||||
let mut entity_gateway = DummyGateway::default();
|
|
||||
|
|
||||
let result = ItemTransaction::new(&item_manager, 12)
|
|
||||
.act(|it, _| -> Result<_, DummyError> {
|
|
||||
it.action(Box::new(DummyAction1 {}));
|
|
||||
it.action(Box::new(DummyAction2 {}));
|
|
||||
it.action(Box::new(DummyAction2 {}));
|
|
||||
Ok(())
|
|
||||
})
|
|
||||
.commit(&mut item_manager, &mut entity_gateway)
|
|
||||
.await;
|
|
||||
|
|
||||
// in an ideal world this would be 0 as rollbacks would occur
|
|
||||
assert!(entity_gateway.d2_inc == 1);
|
|
||||
assert!(matches!(result, Err(TransactionError::Commit(TransactionCommitError::Gateway(GatewayError::Error)))));
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
@ -1,163 +0,0 @@ |
|||||
use thiserror::Error;
|
|
||||
use crate::entity::gateway::EntityGateway;
|
|
||||
use crate::entity::character::CharacterEntity;
|
|
||||
use crate::entity::item::mag::MagCell;
|
|
||||
use crate::ship::items::{CharacterInventory, ConsumedItem};
|
|
||||
|
|
||||
#[derive(Error, Debug)]
|
|
||||
#[error("")]
|
|
||||
pub enum UseItemError {
|
|
||||
NoCharacter,
|
|
||||
ItemNotEquipped,
|
|
||||
InvalidItem,
|
|
||||
}
|
|
||||
|
|
||||
pub async fn power_material<EG: EntityGateway>(entity_gateway: &mut EG, character: &mut CharacterEntity) {
|
|
||||
character.materials.power += 1;
|
|
||||
entity_gateway.save_character(character).await.unwrap();
|
|
||||
}
|
|
||||
|
|
||||
pub async fn mind_material<EG: EntityGateway>(entity_gateway: &mut EG, character: &mut CharacterEntity) {
|
|
||||
character.materials.mind += 1;
|
|
||||
entity_gateway.save_character(character).await.unwrap();
|
|
||||
}
|
|
||||
|
|
||||
pub async fn evade_material<EG: EntityGateway>(entity_gateway: &mut EG, character: &mut CharacterEntity) {
|
|
||||
character.materials.evade += 1;
|
|
||||
entity_gateway.save_character(character).await.unwrap();
|
|
||||
}
|
|
||||
|
|
||||
pub async fn def_material<EG: EntityGateway>(entity_gateway: &mut EG, character: &mut CharacterEntity) {
|
|
||||
character.materials.def += 1;
|
|
||||
entity_gateway.save_character(character).await.unwrap();
|
|
||||
}
|
|
||||
|
|
||||
pub async fn luck_material<EG: EntityGateway>(entity_gateway: &mut EG, character: &mut CharacterEntity) {
|
|
||||
character.materials.luck += 1;
|
|
||||
entity_gateway.save_character(character).await.unwrap();
|
|
||||
}
|
|
||||
|
|
||||
pub async fn hp_material<EG: EntityGateway>(entity_gateway: &mut EG, character: &mut CharacterEntity) {
|
|
||||
character.materials.hp += 1;
|
|
||||
entity_gateway.save_character(character).await.unwrap();
|
|
||||
}
|
|
||||
|
|
||||
pub async fn tp_material<EG: EntityGateway>(entity_gateway: &mut EG, character: &mut CharacterEntity) {
|
|
||||
character.materials.tp += 1;
|
|
||||
entity_gateway.save_character(character).await.unwrap();
|
|
||||
}
|
|
||||
|
|
||||
async fn mag_cell<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory, mag_cell_type: MagCell) -> Result<(), UseItemError> {
|
|
||||
let mut mag_handle = inventory.get_equipped_mag_handle().ok_or(UseItemError::ItemNotEquipped)?;
|
|
||||
let mag_item = mag_handle.item_mut()
|
|
||||
.ok_or(UseItemError::InvalidItem)?;
|
|
||||
let actual_mag = mag_item
|
|
||||
.individual_mut()
|
|
||||
.ok_or(UseItemError::InvalidItem)?
|
|
||||
.mag_mut()
|
|
||||
.ok_or(UseItemError::InvalidItem)?;
|
|
||||
actual_mag.apply_mag_cell(mag_cell_type);
|
|
||||
for mag_entity_id in mag_item.entity_ids() {
|
|
||||
for cell_entity_id in used_cell.entity_ids() {
|
|
||||
entity_gateway.use_mag_cell(&mag_entity_id, &cell_entity_id).await.unwrap();
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
Ok(())
|
|
||||
}
|
|
||||
|
|
||||
pub async fn cell_of_mag_502<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::CellOfMag502).await
|
|
||||
}
|
|
||||
|
|
||||
pub async fn cell_of_mag_213<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::CellOfMag213).await
|
|
||||
}
|
|
||||
|
|
||||
pub async fn parts_of_robochao<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::PartsOfRobochao).await
|
|
||||
}
|
|
||||
|
|
||||
pub async fn heart_of_opaopa<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfOpaOpa).await
|
|
||||
}
|
|
||||
|
|
||||
pub async fn heart_of_pian<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfPian).await
|
|
||||
}
|
|
||||
|
|
||||
pub async fn heart_of_chao<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfChao).await
|
|
||||
}
|
|
||||
|
|
||||
pub async fn heart_of_angel<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfAngel).await
|
|
||||
}
|
|
||||
|
|
||||
pub async fn kit_of_hamburger<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfHamburger).await
|
|
||||
}
|
|
||||
|
|
||||
pub async fn panthers_spirit<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::PanthersSpirit).await
|
|
||||
}
|
|
||||
|
|
||||
pub async fn kit_of_mark3<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfMark3).await
|
|
||||
}
|
|
||||
|
|
||||
pub async fn kit_of_master_system<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfMasterSystem).await
|
|
||||
}
|
|
||||
|
|
||||
pub async fn kit_of_genesis<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfGenesis).await
|
|
||||
}
|
|
||||
|
|
||||
pub async fn kit_of_sega_saturn<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfSegaSaturn).await
|
|
||||
}
|
|
||||
|
|
||||
pub async fn kit_of_dreamcast<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfDreamcast).await
|
|
||||
}
|
|
||||
|
|
||||
pub async fn tablet<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::Tablet).await
|
|
||||
}
|
|
||||
|
|
||||
pub async fn dragon_scale<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::DragonScale).await
|
|
||||
}
|
|
||||
|
|
||||
pub async fn heaven_striker_coat<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::HeavenStrikerCoat).await
|
|
||||
}
|
|
||||
|
|
||||
pub async fn pioneer_parts<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::PioneerParts).await
|
|
||||
}
|
|
||||
|
|
||||
pub async fn amities_memo<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::AmitiesMemo).await
|
|
||||
}
|
|
||||
|
|
||||
pub async fn heart_of_morolian<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfMorolian).await
|
|
||||
}
|
|
||||
|
|
||||
pub async fn rappys_beak<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::RappysBeak).await
|
|
||||
}
|
|
||||
|
|
||||
pub async fn yahoos_engine<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::YahoosEngine).await
|
|
||||
}
|
|
||||
|
|
||||
pub async fn d_photon_core<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::DPhotonCore).await
|
|
||||
}
|
|
||||
|
|
||||
pub async fn liberta_kit<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::LibertaKit).await
|
|
||||
}
|
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue