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.

593 lines
22 KiB

1 year ago
  1. use std::cmp::Ordering;
  2. use libpso::character::character;
  3. use crate::ClientItemId;
  4. use entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, InventoryEntity, InventoryItemEntity, EquippedEntity};
  5. use std::future::Future;
  6. use async_std::sync::{Arc, Mutex};
  7. use entity::character::CharacterEntityId;
  8. use entity::item::tool::ToolType;
  9. use entity::item::mag::Mag;
  10. use entity::item::weapon::Weapon;
  11. use shops::{ShopItem, ArmorShopItem, ToolShopItem, WeaponShopItem};
  12. use crate::state::ItemStateError;
  13. use crate::state::{IndividualItemDetail, StackedItemDetail, AddItemResult};
  14. use crate::floor::{FloorItem, FloorItemDetail};
  15. #[derive(Clone, Debug)]
  16. pub enum InventoryItemDetail {
  17. Individual(IndividualItemDetail),
  18. Stacked(StackedItemDetail),
  19. }
  20. impl InventoryItemDetail {
  21. // TODO: rename as_stacked for consistency
  22. pub fn stacked(&self) -> Option<&StackedItemDetail> {
  23. match self {
  24. InventoryItemDetail::Stacked(sitem) => Some(sitem),
  25. _ => None,
  26. }
  27. }
  28. // TODO: rename as_stacked_mut for consistency
  29. pub fn stacked_mut(&mut self) -> Option<&mut StackedItemDetail> {
  30. match self {
  31. InventoryItemDetail::Stacked(sitem) => Some(sitem),
  32. _ => None,
  33. }
  34. }
  35. pub fn as_individual(&self) -> Option<&IndividualItemDetail> {
  36. match self {
  37. InventoryItemDetail::Individual(iitem) => Some(iitem),
  38. _ => None,
  39. }
  40. }
  41. pub fn as_individual_mut(&mut self) -> Option<&mut IndividualItemDetail> {
  42. match self {
  43. InventoryItemDetail::Individual(iitem) => Some(iitem),
  44. _ => None,
  45. }
  46. }
  47. pub fn as_client_bytes(&self) -> [u8; 16] {
  48. match self {
  49. InventoryItemDetail::Individual(item) => {
  50. item.as_client_bytes()
  51. },
  52. InventoryItemDetail::Stacked(item) => {
  53. item.tool.as_stacked_bytes(item.entity_ids.len())
  54. },
  55. }
  56. }
  57. // TODO: this should probably go somewhere a bit more fundamental like ItemDetail
  58. pub fn sell_price(&self) -> Result<u32, anyhow::Error> {
  59. match self {
  60. InventoryItemDetail::Individual(individual_item) => {
  61. match &individual_item.item {
  62. // TODO: can wrapped items be sold?
  63. ItemDetail::Weapon(w) => {
  64. if !w.tekked {
  65. return Ok(1u32)
  66. }
  67. if w.is_rare_item() {
  68. return Ok(10u32)
  69. }
  70. Ok((WeaponShopItem::from(w).price() / 8) as u32)
  71. },
  72. ItemDetail::Armor(a) => {
  73. if a.is_rare_item() {
  74. return Ok(10u32)
  75. }
  76. Ok((ArmorShopItem::from(a).price() / 8) as u32)
  77. },
  78. ItemDetail::Shield(s) => {
  79. if s.is_rare_item() {
  80. return Ok(10u32)
  81. }
  82. Ok((ArmorShopItem::from(s).price() / 8) as u32)
  83. },
  84. ItemDetail::Unit(u) => {
  85. if u.is_rare_item() {
  86. return Ok(10u32)
  87. }
  88. Ok((ArmorShopItem::from(u).price() / 8) as u32)
  89. },
  90. ItemDetail::Tool(t) => {
  91. if !matches!(t.tool, ToolType::PhotonDrop | ToolType::PhotonSphere | ToolType::PhotonCrystal) && t.is_rare_item() {
  92. return Ok(10u32)
  93. }
  94. Ok((ToolShopItem::from(t).price() / 8) as u32)
  95. },
  96. ItemDetail::TechniqueDisk(d) => {
  97. Ok((ToolShopItem::from(d).price() / 8) as u32)
  98. },
  99. ItemDetail::Mag(_m) => {
  100. Err(ItemStateError::ItemNotSellable.into())
  101. },
  102. ItemDetail::ESWeapon(_e) => {
  103. Ok(10u32)
  104. },
  105. }
  106. },
  107. // the number of stacked items sold is handled by the caller. this is just the price of 1
  108. InventoryItemDetail::Stacked(stacked_item) => {
  109. Ok(((ToolShopItem::from(&stacked_item.tool).price() / 8) as u32) * stacked_item.count() as u32)
  110. },
  111. }
  112. }
  113. pub async fn with_entity_id<F, Fut, T>(&self, mut param: T, mut func: F) -> Result<T, anyhow::Error>
  114. where
  115. F: FnMut(T, ItemEntityId) -> Fut,
  116. Fut: Future<Output=Result<T, anyhow::Error>>,
  117. {
  118. match &self {
  119. InventoryItemDetail::Individual(individual_item) => {
  120. param = func(param, individual_item.entity_id).await?;
  121. },
  122. InventoryItemDetail::Stacked(stacked_item) => {
  123. for entity_id in &stacked_item.entity_ids {
  124. param = func(param, *entity_id).await?;
  125. }
  126. }
  127. }
  128. Ok(param)
  129. }
  130. }
  131. #[derive(Clone, Debug)]
  132. pub struct InventoryItem {
  133. pub item_id: ClientItemId,
  134. pub item: InventoryItemDetail,
  135. }
  136. impl InventoryItem {
  137. pub async fn with_entity_id<F, Fut, T>(&self, param: T, func: F) -> Result<T, anyhow::Error>
  138. where
  139. F: FnMut(T, ItemEntityId) -> Fut,
  140. Fut: Future<Output=Result<T, anyhow::Error>>,
  141. {
  142. self.item.with_entity_id(param, func).await
  143. }
  144. pub async fn with_mag<F, Fut, T>(&self, mut param: T, mut func: F) -> Result<T, anyhow::Error>
  145. where
  146. F: FnMut(T, ItemEntityId, Mag) -> Fut,
  147. Fut: Future<Output=Result<T, anyhow::Error>>,
  148. {
  149. if let InventoryItemDetail::Individual(individual_item) = &self.item {
  150. if let ItemDetail::Mag(mag) = &individual_item.item {
  151. param = func(param, individual_item.entity_id, mag.clone()).await?;
  152. }
  153. }
  154. Ok(param)
  155. }
  156. }
  157. #[derive(Clone, Debug)]
  158. pub struct Inventory(Vec<InventoryItem>);
  159. impl Inventory {
  160. pub fn new(items: Vec<InventoryItem>) -> Inventory {
  161. Inventory(items)
  162. }
  163. }
  164. #[derive(thiserror::Error, Debug)]
  165. pub enum InventoryError {
  166. #[error("inventory full")]
  167. InventoryFull,
  168. #[error("stack full")]
  169. StackFull,
  170. #[error("meseta full")]
  171. MesetaFull,
  172. }
  173. #[derive(Clone, Debug)]
  174. pub struct InventoryState {
  175. pub character_id: CharacterEntityId,
  176. pub item_id_counter: Arc<Mutex<u32>>,
  177. pub inventory: Inventory,
  178. pub equipped: EquippedEntity,
  179. pub meseta: Meseta,
  180. }
  181. async fn new_item_id(item_id_counter: &Arc<Mutex<u32>>) -> ClientItemId {
  182. let mut item_id_counter = item_id_counter.lock().await;
  183. let item_id = *item_id_counter;
  184. *item_id_counter += 1;
  185. ClientItemId(item_id)
  186. }
  187. impl InventoryState {
  188. pub async fn initialize_item_ids(&mut self, base_item_id: Arc<Mutex<u32>>) {
  189. self.item_id_counter = base_item_id;
  190. let mut bitem_id = self.item_id_counter.lock().await;
  191. for (i, item) in self.inventory.0.iter_mut().enumerate() {
  192. item.item_id = ClientItemId(*bitem_id + i as u32);
  193. }
  194. *bitem_id += self.inventory.0.len() as u32;
  195. }
  196. pub async fn new_item_id(&mut self) -> ClientItemId {
  197. let mut item_id_counter = self.item_id_counter.lock().await;
  198. let item_id = *item_id_counter;
  199. *item_id_counter += 1;
  200. ClientItemId(item_id)
  201. }
  202. pub fn count(&self) -> usize {
  203. self.inventory.0.len()
  204. }
  205. pub fn add_floor_item(&mut self, item: FloorItem) -> Result<AddItemResult, anyhow::Error> {
  206. match item.item {
  207. FloorItemDetail::Individual(iitem) => {
  208. if self.inventory.0.len() >= 30 {
  209. Err(InventoryError::InventoryFull.into())
  210. }
  211. else {
  212. self.inventory.0.push(InventoryItem {
  213. item_id: item.item_id,
  214. item: InventoryItemDetail::Individual(iitem)
  215. });
  216. Ok(AddItemResult::NewItem)
  217. }
  218. },
  219. FloorItemDetail::Stacked(sitem) => {
  220. let existing_stack = self.inventory.0
  221. .iter_mut()
  222. .filter_map(|item| item.item.stacked_mut())
  223. .find(|item| {
  224. item.tool == sitem.tool
  225. });
  226. match existing_stack {
  227. Some(existing_stack) => {
  228. if existing_stack.entity_ids.len() + sitem.entity_ids.len() > sitem.tool.max_stack() {
  229. Err(InventoryError::StackFull.into())
  230. }
  231. else {
  232. existing_stack.entity_ids.append(&mut sitem.entity_ids.clone());
  233. Ok(AddItemResult::AddToStack)
  234. }
  235. },
  236. None => {
  237. if self.inventory.0.len() >= 30 {
  238. Err(InventoryError::InventoryFull.into())
  239. }
  240. else {
  241. self.inventory.0.push(InventoryItem {
  242. item_id: item.item_id,
  243. item: InventoryItemDetail::Stacked(sitem)
  244. });
  245. Ok(AddItemResult::NewItem)
  246. }
  247. }
  248. }
  249. },
  250. FloorItemDetail::Meseta(meseta) => {
  251. if self.meseta == Meseta(999999) {
  252. Err(InventoryError::MesetaFull.into())
  253. }
  254. else {
  255. self.meseta.0 = std::cmp::min(self.meseta.0 + meseta.0, 999999);
  256. Ok(AddItemResult::Meseta)
  257. }
  258. },
  259. }
  260. }
  261. pub fn add_item(&mut self, item: InventoryItem) -> Result<(AddItemResult, InventoryItem), anyhow::Error> {
  262. match &item.item {
  263. InventoryItemDetail::Individual(_) => {
  264. if self.inventory.0.len() >= 30 {
  265. Err(InventoryError::InventoryFull.into())
  266. }
  267. else {
  268. self.inventory.0.push(item);
  269. Ok((
  270. AddItemResult::NewItem,
  271. self.inventory.0
  272. .last()
  273. .unwrap()
  274. .clone()
  275. ))
  276. }
  277. },
  278. InventoryItemDetail::Stacked(sitem) => {
  279. let existing_stack = self.inventory.0
  280. .iter_mut()
  281. .filter_map(|item| item.item.stacked_mut())
  282. .find(|item| {
  283. item.tool == sitem.tool
  284. });
  285. match existing_stack {
  286. Some(existing_stack) => {
  287. if existing_stack.entity_ids.len() + sitem.entity_ids.len() > sitem.tool.max_stack() {
  288. Err(InventoryError::StackFull.into())
  289. }
  290. else {
  291. existing_stack.entity_ids.append(&mut sitem.entity_ids.clone());
  292. Ok((
  293. AddItemResult::AddToStack,
  294. self.inventory.0[self.inventory.0
  295. .iter()
  296. .filter_map(|item| item.item.stacked())
  297. .position(|item| item.tool == sitem.tool)
  298. .unwrap()]
  299. .clone()
  300. ))
  301. }
  302. },
  303. None => {
  304. if self.inventory.0.len() >= 30 {
  305. Err(InventoryError::InventoryFull.into())
  306. }
  307. else {
  308. self.inventory.0.push(item);
  309. Ok((
  310. AddItemResult::NewItem,
  311. self.inventory.0
  312. .last()
  313. .unwrap()
  314. .clone()
  315. ))
  316. }
  317. }
  318. }
  319. }
  320. }
  321. }
  322. pub async fn remove_item(&mut self, item_id: &ClientItemId, amount: u32) -> Option<InventoryItemDetail> {
  323. let idx = self.inventory.0
  324. .iter()
  325. .position(|i| i.item_id == *item_id)?;
  326. match &mut self.inventory.0[idx].item {
  327. InventoryItemDetail::Individual(_individual_item) => {
  328. Some(self.inventory.0.remove(idx).item)
  329. },
  330. InventoryItemDetail::Stacked(stacked_item) => {
  331. let remove_all = (amount == 0) || match stacked_item.entity_ids.len().cmp(&(amount as usize)) {
  332. Ordering::Equal => true,
  333. Ordering::Greater => false,
  334. Ordering::Less => return None,
  335. };
  336. if remove_all {
  337. Some(self.inventory.0.remove(idx).item)
  338. }
  339. else {
  340. let entity_ids = stacked_item.entity_ids.drain(..(amount as usize)).collect();
  341. Some(InventoryItemDetail::Stacked(StackedItemDetail {
  342. entity_ids,
  343. tool: stacked_item.tool,
  344. }))
  345. }
  346. }
  347. }
  348. }
  349. pub async fn take_item(&mut self, item_id: &ClientItemId, amount: u32) -> Option<InventoryItem> {
  350. let idx = self.inventory.0
  351. .iter()
  352. .position(|i| i.item_id == *item_id)?;
  353. match &mut self.inventory.0[idx].item {
  354. InventoryItemDetail::Individual(_individual_item) => {
  355. Some(self.inventory.0.remove(idx))
  356. },
  357. InventoryItemDetail::Stacked(stacked_item) => {
  358. let remove_all = (amount == 0) || match stacked_item.entity_ids.len().cmp(&(amount as usize)) {
  359. Ordering::Equal => true,
  360. Ordering::Greater => false,
  361. Ordering::Less => return None,
  362. };
  363. if remove_all {
  364. Some(self.inventory.0.remove(idx))
  365. }
  366. else {
  367. let entity_ids = stacked_item.entity_ids.drain(..(amount as usize)).collect();
  368. Some(InventoryItem {
  369. item_id: new_item_id(&self.item_id_counter).await,
  370. item: InventoryItemDetail::Stacked(StackedItemDetail {
  371. entity_ids,
  372. tool: stacked_item.tool,
  373. })})
  374. }
  375. }
  376. }
  377. }
  378. // TODO: rename get_item_by_client_id
  379. pub fn get_by_client_id(&self, item_id: &ClientItemId) -> Option<&InventoryItem> {
  380. self.inventory.0
  381. .iter()
  382. .find(|i| i.item_id == *item_id)
  383. }
  384. pub fn get_by_client_id_mut(&mut self, item_id: &ClientItemId) -> Option<&mut InventoryItem> {
  385. self.inventory.0
  386. .iter_mut()
  387. .find(|i| i.item_id == *item_id)
  388. }
  389. pub fn add_meseta(&mut self, amount: u32) -> Result<(), anyhow::Error> {
  390. if self.meseta.0 == 999999 {
  391. return Err(ItemStateError::FullOfMeseta.into())
  392. }
  393. self.meseta.0 = std::cmp::min(self.meseta.0 + amount, 999999);
  394. Ok(())
  395. }
  396. pub fn add_meseta_no_overflow(&mut self, amount: u32) -> Result<(), anyhow::Error> {
  397. if self.meseta.0 + amount > 999999 {
  398. return Err(ItemStateError::FullOfMeseta.into())
  399. }
  400. self.meseta.0 += amount;
  401. Ok(())
  402. }
  403. pub fn remove_meseta(&mut self, amount: u32) -> Result<(), anyhow::Error> {
  404. if amount > self.meseta.0 {
  405. return Err(ItemStateError::InvalidMesetaRemoval(amount).into())
  406. }
  407. self.meseta.0 -= amount;
  408. Ok(())
  409. }
  410. pub fn equip(&mut self, item_id: &ClientItemId, equip_slot: u8) {
  411. for item in &self.inventory.0 {
  412. if let InventoryItemDetail::Individual(inventory_item) = &item.item {
  413. if item.item_id == *item_id {
  414. match inventory_item.item {
  415. ItemDetail::Weapon(_) => self.equipped.weapon = Some(inventory_item.entity_id),
  416. ItemDetail::Armor(_) => self.equipped.armor = Some(inventory_item.entity_id),
  417. ItemDetail::Shield(_) => self.equipped.shield = Some(inventory_item.entity_id),
  418. ItemDetail::Unit(_) => {
  419. if let Some(unit) = self.equipped.unit.get_mut(equip_slot as usize) {
  420. *unit = Some(inventory_item.entity_id)
  421. }
  422. }
  423. ItemDetail::Mag(_) => self.equipped.mag = Some(inventory_item.entity_id),
  424. _ => {}
  425. }
  426. }
  427. }
  428. }
  429. }
  430. pub fn unequip(&mut self, item_id: &ClientItemId) {
  431. for item in &self.inventory.0 {
  432. if let InventoryItemDetail::Individual(inventory_item) = &item.item {
  433. if item.item_id == *item_id {
  434. match inventory_item.item {
  435. ItemDetail::Weapon(_) => self.equipped.weapon = None,
  436. ItemDetail::Armor(_) => {
  437. self.equipped.armor = None;
  438. self.equipped.unit = [None; 4];
  439. }
  440. ItemDetail::Shield(_) => self.equipped.shield = None,
  441. ItemDetail::Unit(_) => {
  442. for unit in self.equipped.unit.iter_mut() {
  443. if *unit == Some(inventory_item.entity_id) {
  444. *unit = None
  445. }
  446. }
  447. }
  448. ItemDetail::Mag(_) => self.equipped.mag = Some(inventory_item.entity_id),
  449. _ => {}
  450. }
  451. }
  452. }
  453. }
  454. }
  455. pub fn equipped_mag_mut(&mut self) -> Option<(ItemEntityId, &mut Mag)> {
  456. let mag_id = self.equipped.mag?;
  457. self.inventory.0
  458. .iter_mut()
  459. .filter_map(|i| {
  460. let individual = i.item.as_individual_mut()?;
  461. let entity_id = individual.entity_id;
  462. Some((entity_id, individual.as_mag_mut()?))
  463. })
  464. .find(|(entity_id, _)| *entity_id == mag_id)
  465. }
  466. pub fn equipped_weapon_mut(&mut self) -> Option<(ItemEntityId, &mut Weapon)> {
  467. let weapon_id = self.equipped.weapon?;
  468. self.inventory.0
  469. .iter_mut()
  470. .filter_map(|i| {
  471. let individual = i.item.as_individual_mut()?;
  472. let entity_id = individual.entity_id;
  473. Some((entity_id, individual.as_weapon_mut()?))
  474. })
  475. .find(|(entity_id, _)| *entity_id == weapon_id)
  476. }
  477. pub fn sort(&mut self, item_ids: &[ClientItemId]) {
  478. self.inventory.0.sort_by(|a, b| {
  479. let a_index = item_ids.iter().position(|item_id| *item_id == a.item_id);
  480. let b_index = item_ids.iter().position(|item_id| *item_id == b.item_id);
  481. match (a_index, b_index) {
  482. (Some(a_index), Some(b_index)) => {
  483. a_index.cmp(&b_index)
  484. },
  485. _ => Ordering::Equal
  486. }
  487. });
  488. }
  489. pub fn as_inventory_entity(&self, _character_id: &CharacterEntityId) -> InventoryEntity {
  490. InventoryEntity {
  491. items: self.inventory.0.iter()
  492. .map(|item| {
  493. match &item.item {
  494. InventoryItemDetail::Individual(item) => {
  495. InventoryItemEntity::Individual(ItemEntity {
  496. id: item.entity_id,
  497. item: item.item.clone(),
  498. })
  499. },
  500. InventoryItemDetail::Stacked(items) => {
  501. InventoryItemEntity::Stacked(items.entity_ids.iter()
  502. .map(|id| {
  503. ItemEntity {
  504. id: *id,
  505. item: ItemDetail::Tool(items.tool)
  506. }
  507. })
  508. .collect())
  509. },
  510. }
  511. })
  512. .collect()
  513. }
  514. }
  515. pub fn as_equipped_entity(&self) -> EquippedEntity {
  516. self.equipped.clone()
  517. }
  518. pub fn as_client_inventory_items(&self) -> [character::InventoryItem; 30] {
  519. self.inventory.0.iter()
  520. .enumerate()
  521. .fold([character::InventoryItem::default(); 30], |mut inventory, (slot, item)| {
  522. let bytes = item.item.as_client_bytes();
  523. inventory[slot].data1.copy_from_slice(&bytes[0..12]);
  524. inventory[slot].data2.copy_from_slice(&bytes[12..16]);
  525. inventory[slot].item_id = item.item_id.0;
  526. inventory[slot].equipped = 0;
  527. inventory[slot].flags = 0;
  528. if let InventoryItemDetail::Individual(individual_item) = &item.item {
  529. if self.equipped.is_equipped(&individual_item.entity_id) {
  530. if let ItemDetail::Unit(_) = individual_item.item {
  531. inventory[slot].data1[4] = self.equipped.unit.iter()
  532. .enumerate()
  533. .find(|(_, u_id)| **u_id == Some(individual_item.entity_id))
  534. .map(|(a, _)| a)
  535. .unwrap_or(0) as u8
  536. }
  537. inventory[slot].equipped = 1;
  538. inventory[slot].flags |= 8;
  539. }
  540. }
  541. inventory
  542. })
  543. }
  544. }