From faf00a904a8217b9b394a52d1bd543af4dc3176c Mon Sep 17 00:00:00 2001 From: jake Date: Fri, 27 May 2022 01:38:49 -0600 Subject: [PATCH] bare minimum item usage stuff --- src/ship/items/actions.rs | 52 ++++++- src/ship/items/apply_item.rs | 219 +++++++++++++++++++++++++++++ src/ship/items/mod.rs | 1 + src/ship/items/state.rs | 6 + src/ship/packet/handler/message.rs | 10 +- src/ship/ship.rs | 2 +- tests/test_item_use.rs | 6 +- 7 files changed, 287 insertions(+), 9 deletions(-) create mode 100644 src/ship/items/apply_item.rs diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index 51e644c..5fdd398 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -7,7 +7,10 @@ use crate::ship::map::MapArea; use crate::entity::character::{CharacterEntity, CharacterEntityId}; use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction}; use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateAction, ItemAction, ItemStateError, FloorItem, InventoryItem, AddItemResult, FloorItemDetail, - StackedItemDetail, BankItem, BankItemDetail, InventoryItemDetail}; + StackedItemDetail, BankItem, BankItemDetail, InventoryItemDetail, IndividualItemDetail}; +use crate::ship::items::apply_item::apply_item; +use crate::entity::item::ItemDetail; +use crate::entity::item::tool::Tool; @@ -597,3 +600,50 @@ where Ok((transaction, result)) }).await } + + +fn use_consumed_item(character: CharacterEntity) + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), InventoryItem) + -> Pin, Box), CharacterEntity), ItemStateError>> + Send + 'a>> +{ + move |(mut item_state, mut transaction), inventory_item| { + let mut character = character.clone(); + Box::pin(async move { + let mut transaction = inventory_item.with_entity_id(Ok(transaction), |mut transaction: Result<_, ItemStateError>, entity_id| { + async move { + if let Ok(transaction) = &mut transaction { + transaction.gateway().add_item_note(&entity_id, ItemNote::Consumed).await?; + } + transaction + }}).await?; + + apply_item(&mut item_state, transaction.gateway(), &mut character, inventory_item).await?; + + Ok(((item_state, transaction), character)) + }) + } +} + +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(consume_inventory_tool(character.id, *item_id, 1)) + .act(take_item_from_inventory(character.id, *item_id, amount)) + .act(use_consumed_item(character.clone())) + .commit((item_state_proxy, transaction)) + .await?; + item_state_proxy.commit(); + *character = new_character; + Ok((transaction, ())) + }).await +} diff --git a/src/ship/items/apply_item.rs b/src/ship/items/apply_item.rs new file mode 100644 index 0000000..90a1211 --- /dev/null +++ b/src/ship/items/apply_item.rs @@ -0,0 +1,219 @@ +use thiserror::Error; +use crate::entity::gateway::{EntityGateway, GatewayError}; +use crate::entity::character::CharacterEntity; +use crate::entity::item::mag::MagCell; +use crate::entity::item::tool::ToolType; +use crate::entity::item::ItemDetail; +use crate::ship::items::state::{ItemStateProxy, InventoryState, 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), +} + +// TODO: make all these functions not-pub +pub async fn power_material(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(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(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(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(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(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(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(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(()) +} + +pub async fn cell_of_mag_502(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> { + mag_cell(entity_gateway, used_cell, inventory, MagCell::CellOfMag502).await +} + +pub async fn cell_of_mag_213(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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: EntityGateway + ?Sized>(item_state: &mut ItemStateProxy<'a>, entity_gateway: &mut EG, character: &mut CharacterEntity, tool: ToolType) -> Result<(), ApplyItemError> { + 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(()), + // 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> { + let item_detail = match item.item { + InventoryItemDetail::Individual(individual_item) => { + individual_item.item + }, + InventoryItemDetail::Stacked(stacked_item) => { + ItemDetail::Tool(stacked_item.tool) + }, + }; + + match item_detail { + ItemDetail::Tool(tool) => apply_tool(item_state, entity_gateway, character, tool.tool).await, + _ => Err(ApplyItemError::InvalidItem) + } +} diff --git a/src/ship/items/mod.rs b/src/ship/items/mod.rs index b37db38..a3956ba 100644 --- a/src/ship/items/mod.rs +++ b/src/ship/items/mod.rs @@ -6,6 +6,7 @@ pub mod transaction; pub mod use_tool; pub mod state; pub mod actions; +pub mod apply_item; use serde::{Serialize, Deserialize}; #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Serialize, Deserialize, derive_more::Display)] diff --git a/src/ship/items/state.rs b/src/ship/items/state.rs index 92f7a44..df226f1 100644 --- a/src/ship/items/state.rs +++ b/src/ship/items/state.rs @@ -24,6 +24,9 @@ pub enum ItemStateError { #[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), @@ -50,6 +53,9 @@ pub enum ItemStateError { #[error("stacked item")] StackedItemError(Vec), + + #[error("apply item {0}")] + ApplyItemError(#[from] crate::ship::items::apply_item::ApplyItemError), } pub enum FloorType { diff --git a/src/ship/packet/handler/message.rs b/src/ship/packet/handler/message.rs index 875ce66..60dd075 100644 --- a/src/ship/packet/handler/message.rs +++ b/src/ship/packet/handler/message.rs @@ -8,7 +8,7 @@ use crate::ship::location::{ClientLocation, ClientLocationError}; use crate::ship::items::{ItemManager, ClientItemId}; use crate::ship::packet::builder; use crate::ship::items::state::ItemState; -use crate::ship::items::actions::{drop_item, drop_partial_item, drop_meseta, equip_item, unequip_item, sort_inventory}; +use crate::ship::items::actions::{drop_item, drop_partial_item, drop_meseta, equip_item, unequip_item, sort_inventory, use_item}; pub async fn request_exp(id: ClientId, request_exp: &RequestExp, @@ -278,20 +278,18 @@ where } } -pub async fn use_item(id: ClientId, +pub async fn player_uses_item(id: ClientId, player_use_tool: &PlayerUseItem, 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 { let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; - let item_used_type = item_manager.player_consumes_tool(entity_gateway, &mut client.character, ClientItemId(player_use_tool.item_id), 1).await?; - - item_manager.use_item(item_used_type, entity_gateway, &mut client.character).await?; + use_item(item_state, entity_gateway, &mut client.character, &ClientItemId(player_use_tool.item_id), 1).await?; Ok(Box::new(None.into_iter())) // TODO: should probably tell other players we used an item } diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 1d3b66e..2197bd9 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -502,7 +502,7 @@ impl ShipServerState { }, GameMessage::PlayerUseItem(player_use_item) => { let block = self.blocks.with_client(id, &self.clients)?; - handler::message::use_item(id, player_use_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await? + handler::message::player_uses_item(id, player_use_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_state).await? }, GameMessage::PlayerUsedMedicalCenter(player_used_medical_center) => { handler::message::player_used_medical_center(id, player_used_medical_center, &mut self.entity_gateway, &mut self.clients, &mut self.item_manager).await? diff --git a/tests/test_item_use.rs b/tests/test_item_use.rs index 9151a0c..199dce7 100644 --- a/tests/test_item_use.rs +++ b/tests/test_item_use.rs @@ -164,7 +164,7 @@ async fn test_use_nonstackable_tool() { item::NewItemEntity { item: item::ItemDetail::Tool( item::tool::Tool { - tool: item::tool::ToolType::MagicStoneIritista, + tool: item::tool::ToolType::HuntersReport, } ), }).await.unwrap()); @@ -251,6 +251,9 @@ async fn test_use_materials() { assert!(char.materials.luck == 2); } +// TODO: tests for ALL ITEMS WOW + +/* #[async_std::test] pub async fn test_learn_new_tech() {} @@ -268,3 +271,4 @@ pub async fn test_char_cannot_learn_high_level_tech() {} #[async_std::test] pub async fn test_android_cannot_learn_tech() {} +*/