From 8f44ca9d18df2790ea41e676310562148e3d570a Mon Sep 17 00:00:00 2001 From: jake Date: Mon, 18 Jul 2022 23:57:54 -0600 Subject: [PATCH] refactor tekking --- src/entity/item/mod.rs | 5 ++ src/ship/items/actions.rs | 64 +++++++++++++++++++++++ src/ship/items/state.rs | 39 ++++++++++---- src/ship/packet/builder/message.rs | 4 +- src/ship/packet/handler/direct_message.rs | 27 +++++----- src/ship/packet/handler/trade.rs | 2 +- src/ship/ship.rs | 4 +- 7 files changed, 116 insertions(+), 29 deletions(-) diff --git a/src/entity/item/mod.rs b/src/entity/item/mod.rs index 6e51550..e89a7a4 100644 --- a/src/entity/item/mod.rs +++ b/src/entity/item/mod.rs @@ -334,3 +334,8 @@ pub struct TradeEntity { pub character1: CharacterEntityId, pub character2: CharacterEntityId, } + +#[derive(Clone, Debug)] +pub enum ItemModifier { + WeaponModifier(weapon::WeaponModifier), +} diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index bd9544a..49c9c7f 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -11,6 +11,8 @@ use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateAction, Item 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::weapon::WeaponModifier; +use crate::entity::item::ItemModifier; use crate::ship::shops::ShopItem; use crate::ship::trade::TradeItem; use crate::ship::location::{AreaClient, RoomId}; @@ -1281,3 +1283,65 @@ where Ok((transaction, floor_item)) }).await } + +fn apply_modifier_to_inventory_item(character_id: CharacterEntityId, modifier: ItemModifier) + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), InventoryItem) + -> Pin, Box), InventoryItem), ItemStateError>> + Send + 'a>> +{ + move |(item_state, mut transaction), mut inventory_item| { + let modifier = modifier.clone(); + Box::pin(async move { + match (&inventory_item.item, modifier) { + (InventoryItemDetail::Individual(IndividualItemDetail{entity_id, item: ItemDetail::Weapon(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)) + }) + } +} + +fn as_individual_item() + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), InventoryItem) + -> Pin, Box), 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)) + }) + } +} + + +pub async fn apply_modifier<'a, EG> ( + item_state: &'a mut ItemState, + entity_gateway: &mut EG, + character: &CharacterEntity, + item_id: ClientItemId, + modifier: ItemModifier) + -> Result +where + EG: EntityGateway, +{ + entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), item) = ItemStateAction::default() + .act(take_item_from_inventory(character.id, item_id, 1)) + .act(apply_modifier_to_inventory_item(character.id, modifier)) + .act(add_item_to_inventory(character.clone())) + .act(as_individual_item()) + .commit((item_state_proxy, transaction)) + .await?; + + item_state_proxy.commit(); + Ok((transaction, item)) + }).await +} diff --git a/src/ship/items/state.rs b/src/ship/items/state.rs index 0a69991..dd39b83 100644 --- a/src/ship/items/state.rs +++ b/src/ship/items/state.rs @@ -10,6 +10,7 @@ use crate::ship::location::{AreaClient, RoomId}; use crate::entity::character::{CharacterEntity, CharacterEntityId}; use crate::entity::gateway::{EntityGateway, GatewayError}; use crate::entity::item::tool::{Tool, ToolType}; +use crate::entity::item::weapon::Weapon; use crate::entity::item::mag::Mag; use crate::ship::drops::ItemDrop; use crate::ship::shops::{ShopItem, ArmorShopItem, ToolShopItem, WeaponShopItem}; @@ -69,6 +70,12 @@ pub enum ItemStateError { #[error("item is not sellable")] ItemNotSellable, + + #[error("could not modify item")] + InvalidModifier, + + #[error("wrong item type ")] + WrongItemType(ClientItemId), } pub enum FloorType { @@ -221,6 +228,13 @@ pub struct IndividualItemDetail { } 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), @@ -234,6 +248,20 @@ impl IndividualItemDetail { _ => 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)] @@ -287,16 +315,7 @@ impl InventoryItemDetail { pub fn as_client_bytes(&self) -> [u8; 16] { match self { InventoryItemDetail::Individual(item) => { - match &item.item { - ItemDetail::Weapon(w) => w.as_bytes(), - ItemDetail::Armor(a) => a.as_bytes(), - ItemDetail::Shield(s) => s.as_bytes(), - ItemDetail::Unit(u) => u.as_bytes(), - ItemDetail::Tool(t) => t.as_individual_bytes(), - ItemDetail::TechniqueDisk(d) => d.as_bytes(), - ItemDetail::Mag(m) => m.as_bytes(), - ItemDetail::ESWeapon(e) => e.as_bytes(), - } + item.as_client_bytes() }, InventoryItemDetail::Stacked(item) => { item.tool.as_stacked_bytes(item.entity_ids.len()) diff --git a/src/ship/packet/builder/message.rs b/src/ship/packet/builder/message.rs index b149508..827aec9 100644 --- a/src/ship/packet/builder/message.rs +++ b/src/ship/packet/builder/message.rs @@ -6,7 +6,7 @@ use crate::ship::ship::{ShipError}; use crate::ship::items::{ClientItemId, InventoryItem, StackedFloorItem, FloorItem, CharacterBank}; use crate::ship::items::state::FloorItem as FloorItem2; use crate::ship::items::state::InventoryItem as InventoryItem2; -use crate::ship::items::state::{BankState}; +use crate::ship::items::state::{BankState, IndividualItemDetail}; use crate::ship::location::AreaClient; use std::convert::TryInto; use crate::ship::shops::ShopItem; @@ -31,7 +31,7 @@ pub fn item_drop(client: u8, target: u8, item_drop: &FloorItem2) -> Result Result { +pub fn create_individual_item(area_client: AreaClient, item_id: ClientItemId, item: &IndividualItemDetail) -> Result { let bytes = item.as_client_bytes(); Ok(CreateItem { client: area_client.local_client.id(), diff --git a/src/ship/packet/handler/direct_message.rs b/src/ship/packet/handler/direct_message.rs index 511f218..e5df411 100644 --- a/src/ship/packet/handler/direct_message.rs +++ b/src/ship/packet/handler/direct_message.rs @@ -15,7 +15,7 @@ use libpso::utf8_to_utf16_array; use crate::ship::packet::builder; use crate::ship::shops::{ShopItem, ToolShopItem, ArmorShopItem}; use crate::ship::items::state::{ItemState, FloorType, FloorItemDetail}; -use crate::ship::items::actions::{pick_up_item, withdraw_meseta, deposit_meseta, withdraw_item, deposit_item, buy_shop_item, enemy_drops_item, TriggerCreateItem}; +use crate::ship::items::actions::{pick_up_item, withdraw_meseta, deposit_meseta, withdraw_item, deposit_item, buy_shop_item, enemy_drops_item, take_meseta, apply_modifier, TriggerCreateItem}; const BANK_ACTION_DEPOSIT: u8 = 0; const BANK_ACTION_WITHDRAW: u8 = 1; @@ -151,7 +151,7 @@ where let (item, floor_type) = item_state.get_floor_item(&client.character.id, &ClientItemId(pickup_item.item_id))?; let remove_item = builder::message::remove_item_from_floor(area_client, item)?; let create_item = match &item.item { - FloorItemDetail::Individual(individual_floor_item) => Some(builder::message::create_individual_item(area_client, item.item_id, &individual_floor_item.item)?), + FloorItemDetail::Individual(individual_floor_item) => Some(builder::message::create_individual_item(area_client, item.item_id, individual_floor_item)?), FloorItemDetail::Stacked(stacked_floor_item) => Some(builder::message::create_stacked_item(area_client, item.item_id, &stacked_floor_item.tool, stacked_floor_item.count())?), FloorItemDetail::Meseta(_) => None, //_ => Some(builder::message::create_item(area_client, &item)?), @@ -411,7 +411,7 @@ pub async fn request_tek_item(id: ClientId, tek_request: &TekRequest, entity_gateway: &mut EG, clients: &mut Clients, - item_manager: &mut ItemManager) + item_state: &mut ItemState) -> Result + Send>, anyhow::Error> where EG: EntityGateway @@ -429,13 +429,14 @@ where client.tek = Some((ClientItemId(tek_request.item_id), special_mod, percent_mod, grind_mod)); - let inventory = item_manager.get_character_inventory(&client.character)?; - let item = inventory.get_item_by_id(ClientItemId(tek_request.item_id)) + let inventory = item_state.get_character_inventory(&client.character)?; + let item = inventory.get_by_client_id(&ClientItemId(tek_request.item_id)) .ok_or(ItemManagerError::WrongItemType(ClientItemId(tek_request.item_id)))?; - let mut weapon = *item.individual() + let mut weapon = item.item.as_individual() .ok_or(ItemManagerError::WrongItemType(ClientItemId(tek_request.item_id)))? - .weapon() - .ok_or(ItemManagerError::WrongItemType(ClientItemId(tek_request.item_id)))?; + .as_weapon() + .ok_or(ItemManagerError::WrongItemType(ClientItemId(tek_request.item_id)))? + .clone(); weapon.apply_modifier(&item::weapon::WeaponModifier::Tekked { special: special_mod, @@ -443,9 +444,7 @@ where grind: grind_mod, }); - let character_meseta = item_manager.get_character_meseta_mut(&client.character.id)?; - character_meseta.0 -= 100; - entity_gateway.set_character_meseta(&client.character.id, *character_meseta).await?; + take_meseta(item_state, entity_gateway, &client.character.id, item::Meseta(100)).await?; let preview_pkt = builder::message::tek_preview(ClientItemId(tek_request.item_id), &weapon)?; @@ -457,7 +456,7 @@ pub async fn accept_tek_item(id: ClientId, entity_gateway: &mut EG, client_location: &ClientLocation, clients: &mut Clients, - item_manager: &mut ItemManager) + item_state: &mut ItemState) -> Result + Send>, anyhow::Error> where EG: EntityGateway @@ -475,9 +474,9 @@ where percent: percent_mod, grind: grind_mod, }; - let weapon = item_manager.replace_item_with_tekked(entity_gateway, &client.character, item_id, modifier).await?; + let weapon = apply_modifier(item_state, entity_gateway, &client.character, item_id, item::ItemModifier::WeaponModifier(modifier)).await?; - let create_item_pkt = builder::message::create_individual_item(area_client, item_id, &item::ItemDetail::Weapon(weapon))?; + let create_item_pkt = builder::message::create_individual_item(area_client, item_id, &weapon)?; let neighbors = client_location.get_client_neighbors(id).map_err(|err| -> ClientLocationError { err.into() })?; Ok(Box::new(neighbors.into_iter() diff --git a/src/ship/packet/handler/trade.rs b/src/ship/packet/handler/trade.rs index 050241f..b6b9faa 100644 --- a/src/ship/packet/handler/trade.rs +++ b/src/ship/packet/handler/trade.rs @@ -512,7 +512,7 @@ where .map(|(client, item)| { match item.item { InventoryItemDetail::Individual(individual_item) => { - GameMessage::CreateItem(builder::message::create_individual_item(client, item.item_id, &individual_item.item).unwrap()) + GameMessage::CreateItem(builder::message::create_individual_item(client, item.item_id, &individual_item).unwrap()) }, InventoryItemDetail::Stacked(stacked_item) => { GameMessage::CreateItem(builder::message::create_stacked_item(client, item.item_id, &stacked_item.tool, stacked_item.count()).unwrap()) diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 69d95fa..55048d3 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -567,10 +567,10 @@ impl ShipServerState { handler::direct_message::buy_item(id, buy_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_state).await? }, GameMessage::TekRequest(tek_request) => { - handler::direct_message::request_tek_item(id, tek_request, &mut self.entity_gateway, &mut self.clients, &mut self.item_manager).await? + handler::direct_message::request_tek_item(id, tek_request, &mut self.entity_gateway, &mut self.clients, &mut self.item_state).await? }, GameMessage::TekAccept(tek_accept) => { - handler::direct_message::accept_tek_item(id, tek_accept, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await? + handler::direct_message::accept_tek_item(id, tek_accept, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_state).await? }, GameMessage::TradeRequest(trade_request) => { handler::trade::trade_request(id, trade_request, target, &block.client_location, &mut self.clients, &mut self.item_state, &mut self.trades).await?