You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1244 lines
46 KiB

2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
1 year ago
1 year ago
2 years ago
1 year ago
  1. // TODO: replace various u32s and usizes denoting item amounts for ItemAmount(u32) for consistency
  2. use crate::ship::items::ClientItemId;
  3. use crate::entity::item::{Meseta, ItemNote};
  4. use async_std::sync::Arc;
  5. use std::future::Future;
  6. use futures::future::BoxFuture;
  7. use std::pin::Pin;
  8. use std::iter::IntoIterator;
  9. use anyhow::Context;
  10. use libpso::packet::{ship::Message, messages::GameMessage};
  11. use crate::entity::character::{CharacterEntity, CharacterEntityId};
  12. use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction};
  13. use crate::entity::item::{ItemDetail, NewItemEntity, TradeId, ItemModifier};
  14. use crate::entity::item::tool::Tool;
  15. use crate::entity::room::RoomEntityId;
  16. use crate::ship::map::MapArea;
  17. use crate::ship::ship::SendShipPacket;
  18. use crate::ship::items::state::{ItemStateProxy, ItemStateError, AddItemResult, StackedItemDetail, IndividualItemDetail};
  19. use crate::ship::items::bank::{BankItem, BankItemDetail};
  20. use crate::ship::items::inventory::{InventoryItem, InventoryItemDetail};
  21. use crate::ship::items::floor::{FloorItem, FloorItemDetail};
  22. use crate::ship::items::apply_item::{apply_item, ApplyItemAction};
  23. use crate::ship::shops::ShopItem;
  24. use crate::ship::drops::{ItemDrop, ItemDropType};
  25. use crate::ship::packet::builder;
  26. use crate::ship::location::AreaClient;
  27. use crate::ship::monster::MonsterType;
  28. pub enum TriggerCreateItem {
  29. Yes,
  30. No
  31. }
  32. pub(super) fn take_item_from_floor<'a, EG, TR>(
  33. character_id: CharacterEntityId,
  34. item_id: ClientItemId
  35. ) -> impl Fn((ItemStateProxy, TR), ())
  36. -> BoxFuture<'a, Result<((ItemStateProxy, TR), FloorItem), anyhow::Error>> + 'a
  37. where
  38. EG: EntityGateway + Send,
  39. TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
  40. {
  41. move |(mut item_state, transaction): (ItemStateProxy, TR) , _| {
  42. Box::pin(async move {
  43. let mut floor = item_state.floor(&character_id).await?;
  44. let item = floor.take_item(&item_id).ok_or_else(|| ItemStateError::NoFloorItem(item_id))?;
  45. item_state.set_floor(floor).await;
  46. Ok(((item_state, transaction), item))
  47. })
  48. }
  49. }
  50. pub(super) fn add_floor_item_to_inventory<'a, EG, TR>(
  51. character: &CharacterEntity
  52. ) -> impl Fn((ItemStateProxy, TR), FloorItem)
  53. -> BoxFuture<'a, Result<((ItemStateProxy, TR), TriggerCreateItem), anyhow::Error>>
  54. where
  55. EG: EntityGateway,
  56. TR: EntityGatewayTransaction<ParentGateway = EG> + Clone + 'a,
  57. {
  58. let character = character.clone();
  59. move |(mut item_state, transaction), floor_item| {
  60. let character = character.clone();
  61. Box::pin(async move {
  62. let mut inventory = item_state.inventory(&character.id).await?;
  63. let character_id = character.id;
  64. let transaction = floor_item.with_entity_id(transaction, |mut transaction, entity_id| {
  65. async move {
  66. transaction.gateway().add_item_note(&entity_id, ItemNote::Pickup {
  67. character_id
  68. }).await?;
  69. Ok(transaction)
  70. }}).await?;
  71. let mut transaction = floor_item.with_mag(transaction, |mut transaction, entity_id, _mag| {
  72. let character = character.clone();
  73. async move {
  74. transaction.gateway().change_mag_owner(&entity_id, &character).await?;
  75. Ok(transaction)
  76. }}).await?;
  77. let add_result = inventory.add_floor_item(floor_item)?;
  78. transaction.gateway().set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
  79. transaction.gateway().set_character_meseta(&character_id, inventory.meseta).await?;
  80. item_state.set_inventory(inventory).await;
  81. Ok(((item_state, transaction),
  82. match add_result {
  83. AddItemResult::NewItem => TriggerCreateItem::Yes,
  84. AddItemResult::AddToStack => TriggerCreateItem::No,
  85. AddItemResult::Meseta => TriggerCreateItem::No,
  86. }))
  87. })
  88. }
  89. }
  90. pub(super) fn remove_item_from_inventory<'a, EG, TR>(
  91. character_id: CharacterEntityId,
  92. item_id: ClientItemId,
  93. amount: u32,
  94. ) -> impl Fn((ItemStateProxy, TR), ())
  95. -> BoxFuture<'a, Result<((ItemStateProxy, TR), InventoryItemDetail), anyhow::Error>>
  96. where
  97. EG: EntityGateway,
  98. TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
  99. {
  100. move |(mut item_state, mut transaction), _| {
  101. Box::pin(async move {
  102. let mut inventory = item_state.inventory(&character_id).await?;
  103. let item = inventory.remove_item(&item_id, amount)
  104. .await
  105. .ok_or_else(|| ItemStateError::NoInventoryItem(item_id))
  106. .with_context(|| format!("{inventory:#?}"))?;
  107. transaction.gateway().set_character_inventory(&character_id, &inventory.as_inventory_entity(&character_id)).await?;
  108. item_state.set_inventory(inventory).await;
  109. Ok(((item_state, transaction), item))
  110. })
  111. }
  112. }
  113. pub(super) fn take_item_from_inventory<'a, EG, TR>(
  114. character_id: CharacterEntityId,
  115. item_id: ClientItemId,
  116. amount: u32,
  117. ) -> impl Fn((ItemStateProxy, TR), ())
  118. -> BoxFuture<'a, Result<((ItemStateProxy, TR), InventoryItem), anyhow::Error>>
  119. where
  120. EG: EntityGateway + 'a,
  121. TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
  122. {
  123. move |(mut item_state, mut transaction), _| {
  124. Box::pin(async move {
  125. let mut inventory = item_state.inventory(&character_id).await?;
  126. let item = inventory.take_item(&item_id, amount)
  127. .await
  128. .ok_or_else(|| ItemStateError::NoInventoryItem(item_id))
  129. .with_context(|| format!("{inventory:#?}"))?;
  130. transaction.gateway().set_character_inventory(&character_id, &inventory.as_inventory_entity(&character_id)).await?;
  131. item_state.set_inventory(inventory).await;
  132. Ok(((item_state, transaction), item))
  133. })
  134. }
  135. }
  136. pub(super) fn add_inventory_item_to_shared_floor<'a, EG, TR>(
  137. character_id: CharacterEntityId,
  138. map_area: MapArea,
  139. drop_position: (f32, f32, f32),
  140. ) -> impl Fn((ItemStateProxy, TR), InventoryItem)
  141. -> BoxFuture<'a, Result<((ItemStateProxy, TR), FloorItem), anyhow::Error>>
  142. where
  143. EG: EntityGateway,
  144. TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
  145. {
  146. move |(mut item_state, transaction), inventory_item| {
  147. Box::pin(async move {
  148. let transaction = inventory_item.with_entity_id(transaction, |mut transaction, entity_id| {
  149. async move {
  150. transaction.gateway().add_item_note(&entity_id, ItemNote::PlayerDrop {
  151. character_id,
  152. map_area,
  153. x: drop_position.0,
  154. y: drop_position.1,
  155. z: drop_position.2,
  156. }).await?;
  157. Ok(transaction)
  158. }}).await?;
  159. let mut floor = item_state.floor(&character_id).await?;
  160. let floor_item = floor.add_inventory_item(inventory_item, map_area, drop_position).clone();
  161. item_state.set_floor(floor).await;
  162. Ok(((item_state, transaction), floor_item))
  163. })
  164. }
  165. }
  166. pub(super) fn take_meseta_from_inventory<'a, EG, TR>(
  167. character_id: CharacterEntityId,
  168. amount: u32,
  169. ) -> impl Fn((ItemStateProxy, TR), ())
  170. -> BoxFuture<'a, Result<((ItemStateProxy, TR), ()), anyhow::Error>>
  171. where
  172. EG: EntityGateway,
  173. TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
  174. {
  175. move |(mut item_state, mut transaction), _| {
  176. Box::pin(async move {
  177. let mut inventory = item_state.inventory(&character_id).await?;
  178. inventory.remove_meseta(amount)?;
  179. transaction.gateway().set_character_meseta(&character_id, inventory.meseta).await?;
  180. item_state.set_inventory(inventory).await;
  181. Ok(((item_state, transaction), ()))
  182. })
  183. }
  184. }
  185. pub(super) fn add_meseta_to_inventory<'a, EG, TR>(
  186. character_id: CharacterEntityId,
  187. amount: u32
  188. ) -> impl Fn((ItemStateProxy, TR), ())
  189. -> BoxFuture<'a, Result<((ItemStateProxy, TR), ()), anyhow::Error>>
  190. where
  191. EG: EntityGateway,
  192. TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
  193. {
  194. move |(mut item_state, mut transaction), _| {
  195. Box::pin(async move {
  196. let mut inventory = item_state.inventory(&character_id).await?;
  197. inventory.add_meseta(amount)?;
  198. transaction.gateway().set_character_meseta(&character_id, inventory.meseta).await?;
  199. item_state.set_inventory(inventory).await;
  200. Ok(((item_state, transaction), ()))
  201. })
  202. }
  203. }
  204. pub(super) fn add_meseta_to_shared_floor<'a, EG, TR>(
  205. character_id: CharacterEntityId,
  206. amount: u32,
  207. map_area: MapArea,
  208. drop_position: (f32, f32)
  209. ) -> impl Fn((ItemStateProxy, TR), ())
  210. -> BoxFuture<'a, Result<((ItemStateProxy, TR), FloorItem), anyhow::Error>>
  211. where
  212. EG: EntityGateway,
  213. TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
  214. {
  215. move |(mut item_state, transaction), _| {
  216. Box::pin(async move {
  217. let floor_item = FloorItem {
  218. item_id: item_state.new_item_id().await?,
  219. item: FloorItemDetail::Meseta(Meseta(amount)),
  220. map_area,
  221. x: drop_position.0,
  222. y: 0.0,
  223. z: drop_position.1,
  224. };
  225. let mut floor = item_state.floor(&character_id).await?;
  226. let floor_item = floor.add_shared_item(floor_item).clone();
  227. item_state.set_floor(floor).await;
  228. Ok(((item_state, transaction), floor_item))
  229. })
  230. }
  231. }
  232. pub(super) fn take_meseta_from_bank<'a, EG, TR>(
  233. character_id: CharacterEntityId,
  234. amount: u32,
  235. ) -> impl Fn((ItemStateProxy, TR), ())
  236. -> BoxFuture<'a, Result<((ItemStateProxy, TR), ()), anyhow::Error>>
  237. where
  238. EG: EntityGateway,
  239. TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
  240. {
  241. move |(mut item_state, mut transaction), _| {
  242. Box::pin(async move {
  243. let mut bank = item_state.bank(&character_id).await?;
  244. bank.remove_meseta(amount)?;
  245. transaction.gateway().set_bank_meseta(&character_id, &bank.identifier, bank.meseta).await?;
  246. item_state.set_bank(bank).await;
  247. Ok(((item_state, transaction), ()))
  248. })
  249. }
  250. }
  251. pub(super) fn add_meseta_from_bank_to_inventory<'a, EG, TR>(
  252. character_id: CharacterEntityId,
  253. amount: u32,
  254. ) -> impl Fn((ItemStateProxy, TR), ())
  255. -> BoxFuture<'a, Result<((ItemStateProxy, TR), ()), anyhow::Error>>
  256. where
  257. EG: EntityGateway,
  258. TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
  259. {
  260. move |(mut item_state, mut transaction), _| {
  261. Box::pin(async move {
  262. let mut inventory = item_state.inventory(&character_id).await?;
  263. inventory.add_meseta_no_overflow(amount)?;
  264. transaction.gateway().set_character_meseta(&character_id, inventory.meseta).await?;
  265. item_state.set_inventory(inventory).await;
  266. Ok(((item_state, transaction), ()))
  267. })
  268. }
  269. }
  270. pub(super) fn add_meseta_to_bank<'a, EG, TR>(
  271. character_id: CharacterEntityId,
  272. amount: u32,
  273. ) -> impl Fn((ItemStateProxy, TR), ())
  274. -> BoxFuture<'a, Result<((ItemStateProxy, TR), ()), anyhow::Error>>
  275. where
  276. EG: EntityGateway,
  277. TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
  278. {
  279. move |(mut item_state, mut transaction), _| {
  280. Box::pin(async move {
  281. let mut bank = item_state.bank(&character_id).await?;
  282. bank.add_meseta(amount)?;
  283. transaction.gateway().set_bank_meseta(&character_id, &bank.identifier, bank.meseta).await?;
  284. item_state.set_bank(bank).await;
  285. Ok(((item_state, transaction), ()))
  286. })
  287. }
  288. }
  289. pub(super) fn take_item_from_bank<'a, EG, TR>(
  290. character_id: CharacterEntityId,
  291. item_id: ClientItemId,
  292. amount: u32,
  293. ) -> impl Fn((ItemStateProxy, TR), ())
  294. -> BoxFuture<'a, Result<((ItemStateProxy, TR), BankItem), anyhow::Error>>
  295. where
  296. EG: EntityGateway,
  297. TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
  298. {
  299. move |(mut item_state, mut transaction), _| {
  300. Box::pin(async move {
  301. let mut bank = item_state.bank(&character_id).await?;
  302. let item = bank.take_item(&item_id, amount)
  303. .await
  304. .ok_or_else(|| ItemStateError::NoBankItem(item_id))?;
  305. transaction.gateway().set_character_bank(&character_id, &bank.as_bank_entity(), &bank.identifier).await?;
  306. item_state.set_bank(bank).await;
  307. Ok(((item_state, transaction), item))
  308. })
  309. }
  310. }
  311. pub(super) fn add_bank_item_to_inventory<'a, EG, TR>(
  312. character: &CharacterEntity,
  313. ) -> impl Fn((ItemStateProxy, TR), BankItem)
  314. -> BoxFuture<'a, Result<((ItemStateProxy, TR), InventoryItem), anyhow::Error>>
  315. where
  316. EG: EntityGateway,
  317. TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
  318. {
  319. let character = character.clone();
  320. move |(mut item_state, transaction), bank_item| {
  321. let character = character.clone();
  322. Box::pin(async move {
  323. let bank_identifier = item_state.bank(&character.id).await?.identifier;
  324. let mut inventory = item_state.inventory(&character.id).await?;
  325. let character_id = character.id;
  326. let transaction = bank_item.with_entity_id(transaction, |mut transaction, entity_id| {
  327. let bank_identifier = bank_identifier.clone();
  328. async move {
  329. transaction.gateway().add_item_note(&entity_id, ItemNote::Withdraw {
  330. character_id,
  331. bank: bank_identifier,
  332. }).await?;
  333. Ok(transaction)
  334. }}).await?;
  335. let inventory_item = InventoryItem {
  336. item_id: bank_item.item_id,
  337. item: match bank_item.item {
  338. BankItemDetail::Individual(individual_item) => InventoryItemDetail::Individual(individual_item),
  339. BankItemDetail::Stacked(stacked_item) => InventoryItemDetail::Stacked(stacked_item),
  340. },
  341. };
  342. let mut transaction = inventory_item.with_mag(transaction, |mut transaction, entity_id, _mag| {
  343. let character = character.clone();
  344. async move {
  345. transaction.gateway().change_mag_owner(&entity_id, &character).await?;
  346. Ok(transaction)
  347. }}).await?;
  348. inventory.add_item(inventory_item.clone())?;
  349. transaction.gateway().set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
  350. item_state.set_inventory(inventory).await;
  351. Ok(((item_state, transaction), inventory_item))
  352. })
  353. }
  354. }
  355. pub(super) fn add_inventory_item_to_bank<'a, EG, TR>(
  356. character_id: CharacterEntityId,
  357. ) -> impl Fn((ItemStateProxy, TR), InventoryItem)
  358. -> BoxFuture<'a, Result<((ItemStateProxy, TR), ()), anyhow::Error>>
  359. where
  360. EG: EntityGateway,
  361. TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
  362. {
  363. move |(mut item_state, transaction), inventory_item| {
  364. Box::pin(async move {
  365. let mut bank = item_state.bank(&character_id).await?;
  366. let bank_identifier = bank.identifier.clone();
  367. let mut transaction = inventory_item.with_entity_id(transaction, move |mut transaction, entity_id| {
  368. let bank_identifier = bank_identifier.clone();
  369. async move {
  370. transaction.gateway().add_item_note(&entity_id, ItemNote::Deposit {
  371. character_id,
  372. bank: bank_identifier,
  373. }).await?;
  374. Ok(transaction)
  375. }}).await?;
  376. bank.add_inventory_item(inventory_item)?;
  377. transaction.gateway().set_character_bank(&character_id, &bank.as_bank_entity(), &bank.identifier).await?;
  378. item_state.set_bank(bank).await;
  379. Ok(((item_state, transaction), ()))
  380. })
  381. }
  382. }
  383. pub(super) fn equip_inventory_item<'a, EG, TR>(
  384. character_id: CharacterEntityId,
  385. item_id: ClientItemId,
  386. equip_slot: u8,
  387. ) -> impl Fn((ItemStateProxy, TR), ())
  388. -> BoxFuture<'a, Result<((ItemStateProxy, TR), ()), anyhow::Error>>
  389. where
  390. EG: EntityGateway,
  391. TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
  392. {
  393. move |(mut item_state, mut transaction), _| {
  394. Box::pin(async move {
  395. let mut inventory = item_state.inventory(&character_id).await?;
  396. inventory.equip(&item_id, equip_slot);
  397. transaction.gateway().set_character_equips(&character_id, &inventory.as_equipped_entity()).await?;
  398. item_state.set_inventory(inventory).await;
  399. Ok(((item_state, transaction), ()))
  400. })
  401. }
  402. }
  403. pub(super) fn unequip_inventory_item<'a, EG, TR>(
  404. character_id: CharacterEntityId,
  405. item_id: ClientItemId,
  406. ) -> impl Fn((ItemStateProxy, TR), ())
  407. -> BoxFuture<'a, Result<((ItemStateProxy, TR), ()), anyhow::Error>>
  408. where
  409. EG: EntityGateway,
  410. TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
  411. {
  412. move |(mut item_state, mut transaction), _| {
  413. Box::pin(async move {
  414. let mut inventory = item_state.inventory(&character_id).await?;
  415. inventory.unequip(&item_id);
  416. transaction.gateway().set_character_equips(&character_id, &inventory.as_equipped_entity()).await?;
  417. item_state.set_inventory(inventory).await;
  418. Ok(((item_state, transaction), ()))
  419. })
  420. }
  421. }
  422. pub(super) fn sort_inventory_items<'a, EG, TR>(
  423. character_id: CharacterEntityId,
  424. item_ids: Vec<ClientItemId>,
  425. ) -> impl Fn((ItemStateProxy, TR), ())
  426. -> BoxFuture<'a, Result<((ItemStateProxy, TR), ()), anyhow::Error>>
  427. where
  428. EG: EntityGateway,
  429. TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
  430. {
  431. move |(mut item_state, mut transaction), _| {
  432. let item_ids = item_ids.clone();
  433. Box::pin(async move {
  434. let mut inventory = item_state.inventory(&character_id).await?;
  435. inventory.sort(&item_ids);
  436. transaction.gateway().set_character_inventory(&character_id, &inventory.as_inventory_entity(&character_id)).await?;
  437. item_state.set_inventory(inventory).await;
  438. Ok(((item_state, transaction), ()))
  439. })
  440. }
  441. }
  442. pub(super) fn use_consumed_item<'a, EG, TR>(
  443. character: &CharacterEntity,
  444. ) -> impl Fn((ItemStateProxy, TR), InventoryItemDetail)
  445. -> BoxFuture<'a, Result<((ItemStateProxy, TR), Vec<ApplyItemAction>), anyhow::Error>>
  446. where
  447. EG: EntityGateway + Clone + 'a,
  448. TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
  449. {
  450. let character = character.clone();
  451. move |(mut item_state, transaction), inventory_item| {
  452. let mut character = character.clone();
  453. Box::pin(async move {
  454. let mut transaction = inventory_item.with_entity_id(transaction, |mut transaction, entity_id| {
  455. async move {
  456. transaction.gateway().add_item_note(&entity_id, ItemNote::Consumed {
  457. character_id: character.id,
  458. }).await?;
  459. Ok(transaction)
  460. }}).await?;
  461. let apply_item_actions = apply_item(&mut item_state, transaction.gateway(), &mut character, inventory_item).await?;
  462. Ok(((item_state, transaction), apply_item_actions))
  463. })
  464. }
  465. }
  466. pub(super) fn feed_mag_item<'a, EG, TR>(
  467. character: CharacterEntity,
  468. mag_item_id: ClientItemId,
  469. ) -> impl Fn((ItemStateProxy, TR), InventoryItem)
  470. -> BoxFuture<'a, Result<((ItemStateProxy, TR), CharacterEntity), anyhow::Error>>
  471. where
  472. EG: EntityGateway,
  473. TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
  474. {
  475. move |(mut item_state, transaction), tool| {
  476. let character = character.clone();
  477. Box::pin(async move {
  478. let mut inventory = item_state.inventory(&character.id).await?;
  479. let mag_entity = inventory.get_by_client_id_mut(&mag_item_id)
  480. .ok_or_else(|| ItemStateError::InvalidItemId(mag_item_id))?
  481. .item
  482. .as_individual_mut()
  483. .ok_or_else(|| ItemStateError::NotAMag(mag_item_id))?;
  484. let mag_entity_id = mag_entity.entity_id;
  485. let mut transaction = tool.with_entity_id(transaction, |mut transaction, entity_id| {
  486. async move {
  487. transaction.gateway().add_item_note(&entity_id, ItemNote::FedToMag {
  488. character_id: character.id,
  489. mag: mag_entity_id,
  490. }).await?;
  491. transaction.gateway().feed_mag(&mag_entity_id, &entity_id).await?;
  492. Ok(transaction)
  493. }}).await?;
  494. let food_tool = tool
  495. .item
  496. .stacked()
  497. .ok_or_else(|| ItemStateError::NotMagFood(tool.item_id))?
  498. .tool
  499. .tool;
  500. let mag_entity = mag_entity
  501. .as_mag_mut()
  502. .ok_or_else(|| ItemStateError::NotAMag(mag_item_id))?;
  503. mag_entity.feed(food_tool);
  504. transaction.gateway().set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
  505. item_state.set_inventory(inventory).await;
  506. Ok(((item_state, transaction), character))
  507. })
  508. }
  509. }
  510. #[allow(clippy::needless_lifetimes)] // clippy this lifetime is not needless get off my case
  511. pub(super) fn add_bought_item_to_inventory<'a, EG, TR>(
  512. character_id: CharacterEntityId,
  513. shop_item: &'a (dyn ShopItem + Send + Sync),
  514. item_id: ClientItemId,
  515. amount: u32,
  516. ) -> impl Fn((ItemStateProxy, TR), ())
  517. -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy, TR), InventoryItem), anyhow::Error>> + Send + 'a>>
  518. where
  519. EG: EntityGateway,
  520. TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
  521. {
  522. move |(mut item_state, mut transaction), _| {
  523. Box::pin(async move {
  524. let mut inventory = item_state.inventory(&character_id).await?;
  525. let bought_item = shop_item.as_item();
  526. let inventory_item = match bought_item {
  527. ItemDetail::Tool(tool) if tool.is_stackable() => {
  528. let mut item_entities = Vec::new();
  529. for _ in 0..amount {
  530. let item_entity = transaction.gateway().create_item(NewItemEntity {
  531. item: ItemDetail::Tool(tool),
  532. }).await?;
  533. transaction.gateway().add_item_note(&item_entity.id, ItemNote::BoughtAtShop {
  534. character_id,
  535. }).await?;
  536. item_entities.push(item_entity);
  537. }
  538. let inventory_item = InventoryItem {
  539. item_id,
  540. item: InventoryItemDetail::Stacked(StackedItemDetail {
  541. entity_ids: item_entities.into_iter().map(|i| i.id).collect(),
  542. tool,
  543. })
  544. };
  545. inventory.add_item(inventory_item.clone())
  546. .with_context(|| format!("inventory {inventory:?}\nitem {inventory_item:?}"))?.1
  547. },
  548. item_detail => {
  549. let item_entity = transaction.gateway().create_item(NewItemEntity {
  550. item: item_detail.clone(),
  551. }).await?;
  552. transaction.gateway().add_item_note(&item_entity.id, ItemNote::BoughtAtShop {
  553. character_id,
  554. }).await?;
  555. let inventory_item = InventoryItem {
  556. item_id,
  557. item: InventoryItemDetail::Individual(IndividualItemDetail {
  558. entity_id: item_entity.id,
  559. item: item_detail,
  560. })
  561. };
  562. inventory.add_item(inventory_item.clone())
  563. .with_context(|| format!("inventory {inventory:?}\nitem {inventory_item:?}"))?.1
  564. },
  565. };
  566. transaction.gateway().set_character_inventory(&character_id, &inventory.as_inventory_entity(&character_id)).await?;
  567. item_state.set_inventory(inventory).await;
  568. Ok(((item_state, transaction), inventory_item))
  569. })
  570. }
  571. }
  572. pub(super) fn sell_inventory_item<'a, EG, TR>(
  573. character_id: CharacterEntityId,
  574. ) -> impl Fn((ItemStateProxy, TR), InventoryItem)
  575. -> BoxFuture<'a, Result<((ItemStateProxy, TR), InventoryItem), anyhow::Error>>
  576. where
  577. EG: EntityGateway,
  578. TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
  579. {
  580. move |(mut item_state, transaction), inventory_item| {
  581. Box::pin(async move {
  582. let mut inventory = item_state.inventory(&character_id).await?;
  583. let price = inventory_item.item.sell_price()?;
  584. inventory.add_meseta_no_overflow(price)?;
  585. let mut transaction = inventory_item.with_entity_id(transaction, |mut transaction, entity_id| {
  586. async move {
  587. transaction.gateway().add_item_note(&entity_id, ItemNote::SoldToShop {
  588. character_id,
  589. }).await?;
  590. Ok(transaction)
  591. }}).await?;
  592. transaction.gateway().set_character_meseta(&character_id, inventory.meseta).await?;
  593. item_state.set_inventory(inventory).await;
  594. Ok(((item_state, transaction), inventory_item))
  595. })
  596. }
  597. }
  598. #[async_recursion::async_recursion]
  599. async fn iterate_inner<'a, EG, TR, I, O, T, F, FR>(
  600. state: (ItemStateProxy, TR),
  601. mut input: Vec<I>,
  602. func: F,
  603. arg: T,
  604. ) -> Result<((ItemStateProxy, TR), Vec<O>), anyhow::Error>
  605. where
  606. 'a: 'async_recursion,
  607. EG: EntityGateway,
  608. TR: EntityGatewayTransaction<ParentGateway = EG>,
  609. I: Send,
  610. O: Send,
  611. T: Clone + Send + Sync,
  612. F: Fn(I) -> FR + Send + Sync + Clone + 'a,
  613. FR: Fn((ItemStateProxy, TR), T)
  614. -> BoxFuture<'a, Result<((ItemStateProxy, TR), O), anyhow::Error>> + Send + Sync,
  615. {
  616. let item = match input.pop() {
  617. Some(item) => item,
  618. None => return Ok((state, Vec::new()))
  619. };
  620. let (state, mut output) = iterate_inner(state, input, func.clone(), arg.clone()).await.unwrap();
  621. let rfunc = func(item);
  622. let (state, result) = rfunc(state, arg.clone()).await.unwrap();
  623. output.push(result);
  624. Ok((state, output))
  625. }
  626. pub(super) fn iterate<'a, EG, TR, I, O, T, F, FR>(
  627. input: Vec<I>,
  628. func: F,
  629. ) -> impl Fn((ItemStateProxy, TR), T)
  630. -> BoxFuture<'a, Result<((ItemStateProxy, TR), Vec<O>), anyhow::Error>>
  631. where
  632. EG: EntityGateway,
  633. TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
  634. O: Send,
  635. I: Send + Clone + 'static + std::fmt::Debug,
  636. T: Send + Clone + 'static + std::fmt::Debug,
  637. F: Fn(I) -> FR + Send + Sync + Clone + 'a,
  638. FR: Fn((ItemStateProxy, TR), T)
  639. -> BoxFuture<'a, Result<((ItemStateProxy, TR), O), anyhow::Error>> + Send + Sync,
  640. T: Clone + Send + Sync,
  641. {
  642. move |(item_state, transaction), arg| {
  643. let input = input.clone();
  644. let func = func.clone();
  645. Box::pin(async move {
  646. let (state, result) = iterate_inner((item_state, transaction), input, func, arg.clone()).await?;
  647. Ok((state, result))
  648. })
  649. }
  650. }
  651. #[async_recursion::async_recursion]
  652. async fn foreach_inner<'a, EG, TR, O, T, F, I>(
  653. state: (ItemStateProxy, TR),
  654. mut input: I,
  655. func: Arc<F>,
  656. ) -> Result<((ItemStateProxy, TR), Vec<O>), anyhow::Error>
  657. where
  658. 'a: 'async_recursion,
  659. EG: EntityGateway,
  660. TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
  661. O: Send,
  662. T: Send,
  663. F: Fn((ItemStateProxy, TR), T)
  664. -> BoxFuture<'a, Result<((ItemStateProxy, TR), O), anyhow::Error>> + Send + Sync,
  665. I: Iterator<Item = T> + Send + Sync + 'a,
  666. {
  667. let item = match input.next() {
  668. Some(item) => item,
  669. None => return Ok((state, Vec::new()))
  670. };
  671. let (state, mut output) = foreach_inner(state, input, func.clone()).await?;
  672. let (state, result) = func(state, item).await?;
  673. output.push(result);
  674. Ok((state, output))
  675. }
  676. pub(super) fn foreach<'a, EG, TR, O, T, F, I>(
  677. func: F
  678. ) -> impl Fn((ItemStateProxy, TR), I)
  679. -> BoxFuture<'a, Result<((ItemStateProxy, TR), Vec<O>), anyhow::Error>>
  680. where
  681. EG: EntityGateway,
  682. TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
  683. O: Send,
  684. T: Send + Clone + 'static + std::fmt::Debug,
  685. F: Fn((ItemStateProxy, TR), T)
  686. -> BoxFuture<'a, Result<((ItemStateProxy, TR), O), anyhow::Error>> + Send + Sync + 'a,
  687. T: Send + Sync,
  688. I: IntoIterator<Item = T> + Send + Sync + 'a,
  689. I::IntoIter: Send + Sync,
  690. {
  691. let func = Arc::new(func);
  692. move |(item_state, transaction), items| {
  693. let func = func.clone();
  694. let items = items.into_iter();
  695. Box::pin(async move {
  696. let (state, result) = foreach_inner((item_state, transaction), items, func).await?;
  697. Ok((state, result))
  698. })
  699. }
  700. }
  701. pub(super) fn insert<'a, EG, TR, T>(
  702. element: T
  703. ) -> impl Fn((ItemStateProxy, TR), ())
  704. -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy, TR), T), anyhow::Error>> + Send + 'a>>
  705. where
  706. EG: EntityGateway,
  707. TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
  708. T: Send + Clone + 'a,
  709. {
  710. move |state, _| {
  711. let element = element.clone();
  712. Box::pin(async move {
  713. Ok((state, element))
  714. })
  715. }
  716. }
  717. pub(super) fn fork<'a, EG, TR, F1, F2, T, O1, O2>(
  718. func1: F1,
  719. func2: F2,
  720. ) -> impl Fn((ItemStateProxy, TR), T)
  721. -> BoxFuture<'a, Result<((ItemStateProxy, TR), (O1, O2)), anyhow::Error>>
  722. where
  723. EG: EntityGateway,
  724. TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
  725. F1: Fn((ItemStateProxy, TR), T) -> BoxFuture<'a, Result<((ItemStateProxy, TR), O1), anyhow::Error>> + Send + Sync + 'a,
  726. F2: Fn((ItemStateProxy, TR), T) -> BoxFuture<'a, Result<((ItemStateProxy, TR), O2), anyhow::Error>> + Send + Sync + 'a,
  727. T: Send + Sync + Clone + 'a,
  728. O1: Send,
  729. O2: Send,
  730. {
  731. let func1 = Arc::new(func1);
  732. let func2 = Arc::new(func2);
  733. move |(item_state, transaction), input| {
  734. let func1 = func1.clone();
  735. let func2 = func2.clone();
  736. Box::pin(async move {
  737. let ((item_state, transaction), result1) = func1((item_state, transaction), input.clone()).await?;
  738. let ((item_state, transaction), result2) = func2((item_state, transaction), input).await?;
  739. Ok(((item_state, transaction), (result1, result2)))
  740. })
  741. }
  742. }
  743. pub(super) fn add_item_to_inventory<'a, EG, TR>(
  744. character: CharacterEntity,
  745. ) -> impl Fn((ItemStateProxy, TR), InventoryItem)
  746. -> BoxFuture<'a, Result<((ItemStateProxy, TR), InventoryItem), anyhow::Error>> + Clone
  747. where
  748. EG: EntityGateway,
  749. TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
  750. {
  751. move |(mut item_state, transaction), inventory_item| {
  752. let character = character.clone();
  753. Box::pin(async move {
  754. let mut inventory = item_state.inventory(&character.id).await?;
  755. let mut transaction = inventory_item.with_mag(transaction, |mut transaction, entity_id, _mag| {
  756. let character = character.clone();
  757. async move {
  758. transaction.gateway().change_mag_owner(&entity_id, &character).await?;
  759. Ok(transaction)
  760. }}).await?;
  761. inventory.add_item(inventory_item.clone())?;
  762. transaction.gateway().set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
  763. item_state.set_inventory(inventory).await;
  764. Ok(((item_state, transaction), inventory_item))
  765. })
  766. }
  767. }
  768. pub(super) fn record_trade<'a, EG, TR>(
  769. trade_id: TradeId,
  770. character_to: CharacterEntityId,
  771. character_from: CharacterEntityId,
  772. ) -> impl Fn((ItemStateProxy, TR), Vec<InventoryItem>)
  773. -> BoxFuture<'a, Result<((ItemStateProxy, TR), Vec<InventoryItem>), anyhow::Error>> + Clone
  774. where
  775. EG: EntityGateway,
  776. TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
  777. {
  778. move |(item_state, mut transaction), traded_items| {
  779. Box::pin(async move {
  780. for item in &traded_items {
  781. transaction = item.with_entity_id(transaction, |mut transaction, entity_id| {
  782. async move {
  783. transaction.gateway().add_item_note(&entity_id, ItemNote::Trade {
  784. trade_id,
  785. character_to,
  786. character_from,
  787. }).await?;
  788. Ok(transaction)
  789. }}).await?;
  790. }
  791. Ok(((item_state, transaction), traded_items))
  792. })
  793. }
  794. }
  795. pub(super) fn assign_new_item_id<'a, EG, TR>(
  796. ) -> impl Fn((ItemStateProxy, TR), InventoryItem)
  797. -> BoxFuture<'a, Result<((ItemStateProxy, TR), InventoryItem), anyhow::Error>> + Clone
  798. where
  799. EG: EntityGateway,
  800. TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
  801. {
  802. move |(mut item_state, transaction), mut inventory_item| {
  803. Box::pin(async move {
  804. inventory_item.item_id = item_state.new_item_id().await?;
  805. Ok(((item_state, transaction), inventory_item))
  806. })
  807. }
  808. }
  809. pub(super) fn convert_item_drop_to_floor_item<'a, EG, TR>(
  810. item_drop: ItemDrop,
  811. ) -> impl Fn((ItemStateProxy, TR), ())
  812. -> BoxFuture<'a, Result<((ItemStateProxy, TR), FloorItem), anyhow::Error>> + Clone
  813. where
  814. EG: EntityGateway,
  815. TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
  816. {
  817. move |(mut item_state, mut transaction), _| {
  818. let item_drop = item_drop.clone();
  819. Box::pin(async move {
  820. enum ItemOrMeseta {
  821. Individual(ItemDetail),
  822. Stacked(Tool),
  823. Meseta(Meseta)
  824. }
  825. let item = match item_drop.item {
  826. ItemDropType::Weapon(w) => ItemOrMeseta::Individual(ItemDetail::Weapon(w)),
  827. ItemDropType::Armor(w) => ItemOrMeseta::Individual(ItemDetail::Armor(w)),
  828. ItemDropType::Shield(w) => ItemOrMeseta::Individual(ItemDetail::Shield(w)),
  829. ItemDropType::Unit(w) => ItemOrMeseta::Individual(ItemDetail::Unit(w)),
  830. ItemDropType::TechniqueDisk(w) => ItemOrMeseta::Individual(ItemDetail::TechniqueDisk(w)),
  831. ItemDropType::Mag(w) => ItemOrMeseta::Individual(ItemDetail::Mag(w)),
  832. ItemDropType::Tool(t) => {
  833. if t.tool.is_stackable() {
  834. ItemOrMeseta::Stacked(t)
  835. }
  836. else {
  837. ItemOrMeseta::Individual(ItemDetail::Tool(t))
  838. }
  839. },
  840. ItemDropType::Meseta(m) => ItemOrMeseta::Meseta(Meseta(m)),
  841. };
  842. let item_id = item_state.new_item_id().await?;
  843. let floor_item = match item {
  844. ItemOrMeseta::Individual(item_detail) => {
  845. let entity = transaction.gateway().create_item(NewItemEntity {
  846. item: item_detail.clone(),
  847. }).await?;
  848. FloorItem {
  849. item_id,
  850. item: FloorItemDetail::Individual(IndividualItemDetail {
  851. entity_id: entity.id,
  852. item: item_detail,
  853. }),
  854. map_area: item_drop.map_area,
  855. x: item_drop.x,
  856. y: item_drop.y,
  857. z: item_drop.z,
  858. }
  859. },
  860. ItemOrMeseta::Stacked(tool) => {
  861. let entity = transaction.gateway().create_item(NewItemEntity {
  862. item: ItemDetail::Tool(tool),
  863. }).await?;
  864. FloorItem {
  865. item_id,
  866. item: FloorItemDetail::Stacked(StackedItemDetail{
  867. entity_ids: vec![entity.id],
  868. tool,
  869. }),
  870. map_area: item_drop.map_area,
  871. x: item_drop.x,
  872. y: item_drop.y,
  873. z: item_drop.z,
  874. }
  875. },
  876. ItemOrMeseta::Meseta(meseta) => {
  877. FloorItem {
  878. item_id,
  879. item: FloorItemDetail::Meseta(meseta),
  880. map_area: item_drop.map_area,
  881. x: item_drop.x,
  882. y: item_drop.y,
  883. z: item_drop.z,
  884. }
  885. },
  886. };
  887. Ok(((item_state, transaction), floor_item))
  888. })
  889. }
  890. }
  891. pub(super) fn item_note_enemy_drop<'a, EG, TR>(
  892. character_id: CharacterEntityId,
  893. room_id: RoomEntityId,
  894. monster_type: MonsterType,
  895. ) -> impl Fn((ItemStateProxy, TR), FloorItem)
  896. -> BoxFuture<'a, Result<((ItemStateProxy, TR), FloorItem), anyhow::Error>> + Clone
  897. where
  898. EG: EntityGateway,
  899. TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
  900. {
  901. move |(item_state, mut transaction), floor_item| {
  902. Box::pin(async move {
  903. match &floor_item.item {
  904. FloorItemDetail::Individual(individual) => {
  905. transaction.gateway().add_item_note(&individual.entity_id, ItemNote::EnemyDrop {
  906. character_id,
  907. room_id,
  908. monster_type,
  909. map_area: floor_item.map_area,
  910. x: floor_item.x,
  911. y: floor_item.y,
  912. z: floor_item.z,
  913. }).await?;
  914. },
  915. FloorItemDetail::Stacked(stacked) => {
  916. transaction.gateway().add_item_note(&stacked.entity_ids[0], ItemNote::EnemyDrop {
  917. character_id,
  918. room_id,
  919. monster_type,
  920. map_area: floor_item.map_area,
  921. x: floor_item.x,
  922. y: floor_item.y,
  923. z: floor_item.z,
  924. }).await?;
  925. },
  926. _ => {},
  927. }
  928. Ok(((item_state, transaction), floor_item))
  929. })
  930. }
  931. }
  932. pub(super) fn item_note_box_drop<'a, EG, TR>(
  933. character_id: CharacterEntityId,
  934. room_id: RoomEntityId,
  935. ) -> impl Fn((ItemStateProxy, TR), FloorItem)
  936. -> BoxFuture<'a, Result<((ItemStateProxy, TR), FloorItem), anyhow::Error>> + Clone
  937. where
  938. EG: EntityGateway,
  939. TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
  940. {
  941. move |(item_state, mut transaction), floor_item| {
  942. Box::pin(async move {
  943. match &floor_item.item {
  944. FloorItemDetail::Individual(individual) => {
  945. transaction.gateway().add_item_note(&individual.entity_id, ItemNote::BoxDrop {
  946. character_id,
  947. room_id,
  948. map_area: floor_item.map_area,
  949. x: floor_item.x,
  950. y: floor_item.y,
  951. z: floor_item.z,
  952. }).await?;
  953. },
  954. FloorItemDetail::Stacked(stacked) => {
  955. transaction.gateway().add_item_note(&stacked.entity_ids[0], ItemNote::BoxDrop {
  956. character_id,
  957. room_id,
  958. map_area: floor_item.map_area,
  959. x: floor_item.x,
  960. y: floor_item.y,
  961. z: floor_item.z,
  962. }).await?;
  963. },
  964. _ => {},
  965. }
  966. Ok(((item_state, transaction), floor_item))
  967. })
  968. }
  969. }
  970. pub(super) fn add_item_to_local_floor<'a, EG, TR>(
  971. character_id: CharacterEntityId,
  972. ) -> impl Fn((ItemStateProxy, TR), FloorItem)
  973. -> BoxFuture<'a, Result<((ItemStateProxy, TR), FloorItem), anyhow::Error>>
  974. where
  975. EG: EntityGateway,
  976. TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
  977. {
  978. move |(mut item_state, transaction) , floor_item| {
  979. Box::pin(async move {
  980. let mut floor = item_state.floor(&character_id).await?;
  981. let item = floor.add_local_item(floor_item).clone();
  982. item_state.set_floor(floor).await;
  983. Ok(((item_state, transaction), item))
  984. })
  985. }
  986. }
  987. pub(super) fn apply_modifier_to_inventory_item<'a, EG, TR>(
  988. modifier: ItemModifier,
  989. ) -> impl Fn((ItemStateProxy, TR), InventoryItem)
  990. -> BoxFuture<'a, Result<((ItemStateProxy, TR), InventoryItem), anyhow::Error>>
  991. where
  992. EG: EntityGateway,
  993. TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
  994. {
  995. move |(item_state, mut transaction), mut inventory_item| {
  996. let modifier = modifier.clone();
  997. Box::pin(async move {
  998. match (&mut inventory_item.item, modifier) {
  999. (InventoryItemDetail::Individual(IndividualItemDetail{entity_id, item: ItemDetail::Weapon(ref mut weapon), ..}), ItemModifier::WeaponModifier(modifier)) => {
  1000. weapon.apply_modifier(&modifier);
  1001. transaction.gateway().add_weapon_modifier(entity_id, &modifier).await?;
  1002. },
  1003. _ => return Err(ItemStateError::InvalidModifier.into())
  1004. }
  1005. Ok(((item_state, transaction), inventory_item))
  1006. })
  1007. }
  1008. }
  1009. pub(super) fn as_individual_item<'a, EG, TR>(
  1010. ) -> impl Fn((ItemStateProxy, TR), InventoryItem)
  1011. -> BoxFuture<'a, Result<((ItemStateProxy, TR), IndividualItemDetail), anyhow::Error>>
  1012. where
  1013. EG: EntityGateway,
  1014. TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
  1015. {
  1016. move |(item_state, transaction), inventory_item| {
  1017. Box::pin(async move {
  1018. let item = match inventory_item.item {
  1019. InventoryItemDetail::Individual(individual_item) => individual_item,
  1020. _ => return Err(ItemStateError::WrongItemType(inventory_item.item_id).into())
  1021. };
  1022. Ok(((item_state, transaction), item))
  1023. })
  1024. }
  1025. }
  1026. pub(super) fn apply_item_action_packets<'a, EG, TR>(
  1027. character_id: CharacterEntityId,
  1028. area_client: AreaClient,
  1029. ) -> impl Fn((ItemStateProxy, TR), ApplyItemAction)
  1030. -> BoxFuture<'a, Result<((ItemStateProxy, TR), Vec<SendShipPacket>), anyhow::Error>>
  1031. where
  1032. EG: EntityGateway,
  1033. TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
  1034. {
  1035. move |(mut item_state, mut transaction), apply_item_action| {
  1036. Box::pin(async move {
  1037. let pkts = if let ApplyItemAction::CreateItem(item_detail) = apply_item_action {
  1038. let new_item = transaction.gateway().create_item(NewItemEntity {
  1039. item: item_detail.clone(),
  1040. }).await?;
  1041. let item_id = item_state.new_item_id().await?;
  1042. let (inventory_item_detail, create_item) = if item_detail.is_stackable() {
  1043. let tool = item_detail.as_tool().ok_or_else(|| ItemStateError::NotATool(ClientItemId(0xFFFFFFFF)))?;
  1044. let create_item = builder::message::create_stacked_item(area_client, item_id, &tool, 1).map_err(|_err| ItemStateError::Dummy)?;
  1045. let item_detail = StackedItemDetail {
  1046. entity_ids: vec![new_item.id],
  1047. tool
  1048. };
  1049. (InventoryItemDetail::Stacked(item_detail), create_item)
  1050. }
  1051. else {
  1052. let item_detail = IndividualItemDetail {
  1053. entity_id: new_item.id,
  1054. item: item_detail,
  1055. };
  1056. let create_item = builder::message::create_individual_item(area_client, item_id, &item_detail).map_err(|_err| ItemStateError::Dummy)?;
  1057. (InventoryItemDetail::Individual(item_detail), create_item)
  1058. };
  1059. let inventory_item = InventoryItem {
  1060. item_id,
  1061. item: inventory_item_detail,
  1062. };
  1063. let mut inventory = item_state.inventory(&character_id).await?;
  1064. inventory.add_item(inventory_item)?;
  1065. transaction.gateway().set_character_inventory(&character_id, &inventory.as_inventory_entity(&character_id)).await?;
  1066. item_state.set_inventory(inventory).await;
  1067. vec![SendShipPacket::Message(Message::new(GameMessage::CreateItem(create_item)))]
  1068. }
  1069. else {
  1070. Vec::new()
  1071. };
  1072. Ok(((item_state, transaction), pkts))
  1073. })
  1074. }
  1075. }
  1076. pub(super) fn apply_item_action_character<'a, EG, TR>(
  1077. character: &CharacterEntity
  1078. ) -> impl Fn((ItemStateProxy, TR), Vec<ApplyItemAction>)
  1079. -> BoxFuture<'a, Result<((ItemStateProxy, TR), CharacterEntity), anyhow::Error>>
  1080. where
  1081. EG: EntityGateway,
  1082. TR: EntityGatewayTransaction<ParentGateway = EG> + 'a,
  1083. {
  1084. let character = character.clone();
  1085. move |(item_state, transaction), apply_item_actions| {
  1086. let mut character = character.clone();
  1087. Box::pin(async move {
  1088. for action in apply_item_actions {
  1089. if let ApplyItemAction::UpdateCharacter(new_character) = action {
  1090. character = *new_character
  1091. }
  1092. }
  1093. Ok(((item_state, transaction), character))
  1094. })
  1095. }
  1096. }
  1097. pub(super) fn delete_item_from_floor<'a, EG, TR>(
  1098. map_area: MapArea
  1099. ) -> impl Fn((ItemStateProxy, TR), FloorItem)
  1100. -> BoxFuture<'a, Result<((ItemStateProxy, TR), ()), anyhow::Error>>
  1101. where
  1102. EG: EntityGateway,
  1103. TR: EntityGatewayTransaction<ParentGateway = EG> + Clone + 'a,
  1104. {
  1105. move |(item_state, transaction), floor_item| {
  1106. Box::pin(async move {
  1107. let transaction = floor_item.with_entity_id(transaction, |mut transaction, entity_id| {
  1108. async move {
  1109. transaction.gateway().add_item_note(&entity_id, ItemNote::FloorLimitReached {
  1110. map_area
  1111. }).await?;
  1112. Ok(transaction)
  1113. }}).await?;
  1114. Ok(((item_state, transaction), ()))
  1115. })
  1116. }
  1117. }