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
-
1160src/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
-
292src/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
-
6src/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, |
|||
); |
1160
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::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::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)]
|
|||
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)]
|
|||
pub struct MesetaFloorItem {
|
|||
pub struct FloorItem {
|
|||
pub item_id: ClientItemId,
|
|||
pub meseta: Meseta,
|
|||
pub item: FloorItemDetail,
|
|||
pub map_area: MapArea,
|
|||
pub x: f32,
|
|||
pub y: f32,
|
|||
pub z: f32,
|
|||
}
|
|||
|
|||
#[derive(Debug, Clone)]
|
|||
pub enum FloorItem {
|
|||
Individual(IndividualFloorItem),
|
|||
Stacked(StackedFloorItem),
|
|||
Meseta(MesetaFloorItem),
|
|||
}
|
|||
|
|||
impl FloorItem {
|
|||
pub fn item_id(&self) -> ClientItemId {
|
|||
match self {
|
|||
FloorItem::Individual(individual_floor_item) => {
|
|||
individual_floor_item.item_id
|
|||
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.item_id
|
|||
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.item_id
|
|||
}
|
|||
FloorItemDetail::Meseta(_meseta) => {},
|
|||
}
|
|||
}
|
|||
|
|||
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
|
|||
}
|
|||
}
|
|||
Ok(param)
|
|||
}
|
|||
|
|||
pub fn y(&self) -> f32 {
|
|||
match self {
|
|||
FloorItem::Individual(individual_floor_item) => {
|
|||
individual_floor_item.y
|
|||
},
|
|||
FloorItem::Stacked(stacked_floor_item) => {
|
|||
stacked_floor_item.y
|
|||
},
|
|||
FloorItem::Meseta(meseta_floor_item) => {
|
|||
meseta_floor_item.y
|
|||
}
|
|||
}
|
|||
}
|
|||
|
|||
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
|
|||
}
|
|||
}
|
|||
}
|
|||
|
|||
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] {
|
|||
match self {
|
|||
FloorItem::Individual(individual_floor_item) => {
|
|||
match &self.item {
|
|||
FloorItemDetail::Individual(individual_floor_item) => {
|
|||
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 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_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!(),
|
|||
}
|
|||
self.shared.0.push(floor_item);
|
|||
&self.shared.0[self.shared.0.len()-1]
|
|||
}
|
|||
|
|||
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!(),
|
|||
}
|
|||
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]
|
|||
}
|
|||
|
|||
// 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
|
|||
}
|
|||
|
|||
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 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);
|
|||
|
|||
// 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