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.

554 lines
25 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
4 years ago
4 years ago
4 years ago
1 year ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
1 year ago
4 years ago
4 years ago
4 years ago
1 year ago
4 years ago
1 year ago
4 years ago
4 years ago
1 year ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
1 year ago
4 years ago
4 years ago
4 years ago
1 year ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. use log::warn;
  2. use rand::Rng;
  3. use rand::seq::SliceRandom;
  4. use libpso::packet::ship::*;
  5. use libpso::packet::messages::*;
  6. use crate::common::leveltable::LEVEL_TABLE;
  7. use crate::common::serverstate::ClientId;
  8. use crate::ship::ship::{SendShipPacket, ShipError, Clients, ItemShops};
  9. use crate::ship::location::ClientLocation;
  10. use crate::ship::drops::ItemDrop;
  11. use crate::ship::room::Rooms;
  12. use crate::ship::items::ClientItemId;
  13. use crate::entity::gateway::EntityGateway;
  14. use crate::entity::item;
  15. use libpso::utf8_to_utf16_array;
  16. use crate::ship::packet::builder;
  17. use crate::ship::shops::{ShopItem, ToolShopItem, ArmorShopItem};
  18. use crate::ship::items::state::{ItemState, ItemStateError};
  19. use crate::ship::items::floor::{FloorType, FloorItemDetail};
  20. use crate::ship::items::actions::TriggerCreateItem;
  21. use crate::ship::items::tasks::{pick_up_item, withdraw_meseta, deposit_meseta, withdraw_item, deposit_item, buy_shop_item, enemy_drops_item, box_drops_item, take_meseta, apply_modifier};
  22. const BANK_ACTION_DEPOSIT: u8 = 0;
  23. const BANK_ACTION_WITHDRAW: u8 = 1;
  24. const SHOP_OPTION_TOOL: u8 = 0;
  25. const SHOP_OPTION_WEAPON: u8 = 1;
  26. const SHOP_OPTION_ARMOR: u8 = 2;
  27. #[derive(thiserror::Error, Debug)]
  28. pub enum MessageError {
  29. #[error("invalid tek {0}")]
  30. InvalidTek(ClientItemId),
  31. #[error("mismatched tek {0} {1}")]
  32. MismatchedTekIds(ClientItemId, ClientItemId),
  33. }
  34. async fn send_to_client(id: ClientId,
  35. target: u8,
  36. msg: DirectMessage,
  37. client_location: &ClientLocation)
  38. -> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
  39. Ok(client_location.get_all_clients_by_client(id)
  40. .await?
  41. .into_iter()
  42. .filter(move |client| client.local_client.id() == target)
  43. .map(move |client| {
  44. (client.client, SendShipPacket::DirectMessage(msg.clone()))
  45. })
  46. .collect())
  47. }
  48. pub async fn guildcard_send(id: ClientId,
  49. guildcard_send: GuildcardSend,
  50. target: u32,
  51. client_location: &ClientLocation,
  52. clients: &Clients)
  53. -> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
  54. let msg = clients.with(id, |client| Box::pin(async move {
  55. DirectMessage{
  56. flag: target,
  57. msg: GameMessage::GuildcardRecv(GuildcardRecv {
  58. client: guildcard_send.client,
  59. target: guildcard_send.target,
  60. guildcard: client.user.id.0,
  61. name: utf8_to_utf16_array!(client.character.name, 0x18),
  62. team: [0; 0x10], // TODO: teams not yet implemented
  63. desc: utf8_to_utf16_array!(client.character.guildcard.description, 0x58),
  64. one: 1,
  65. language: 0, // TODO: add language flag to character
  66. section_id: client.character.section_id.into(),
  67. class: client.character.char_class.into(),
  68. }),
  69. }
  70. })).await?;
  71. send_to_client(id, target as u8, msg, client_location).await
  72. }
  73. pub async fn request_item<EG>(id: ClientId,
  74. request_item: RequestItem,
  75. entity_gateway: &mut EG,
  76. client_location: &ClientLocation,
  77. clients: &Clients,
  78. rooms: &Rooms,
  79. item_state: &mut ItemState)
  80. -> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
  81. where
  82. EG: EntityGateway + 'static,
  83. {
  84. let room_id = client_location.get_room(id).await?;
  85. let (room_entity_id, monster) = rooms.with(room_id, |room| Box::pin(async move {
  86. Ok::<_, anyhow::Error>((room.room_id, room.maps.enemy_by_id(request_item.enemy_id as usize)?))
  87. })).await??;
  88. if monster.dropped_item {
  89. return Err(ShipError::MonsterAlreadyDroppedItem(id, request_item.enemy_id).into())
  90. }
  91. let clients_in_area = client_location.get_clients_in_room(room_id).await?;
  92. let client_and_drop = rooms.with_mut(room_id, |room| Box::pin(async move {
  93. clients_in_area.into_iter()
  94. .filter_map(move |area_client| {
  95. room.drop_table.get_drop(&monster.map_area, &monster.monster).map(|item_drop_type| {
  96. (area_client, item_drop_type)
  97. })
  98. })
  99. .collect::<Vec<_>>()
  100. })).await?;
  101. let mut item_drop_packets = Vec::new();
  102. for (area_client, item_drop) in client_and_drop {
  103. let item_drop = ItemDrop {
  104. map_area: monster.map_area,
  105. x: request_item.x,
  106. y: request_item.y,
  107. z: request_item.z,
  108. item: item_drop,
  109. };
  110. let character_id = clients.with(area_client.client, |client| Box::pin(async move {
  111. client.character.id
  112. })).await?;
  113. let floor_item = enemy_drops_item(item_state, entity_gateway, character_id, room_entity_id, monster.monster, item_drop).await?;
  114. let item_drop_msg = builder::message::item_drop(request_item.client, request_item.target, &floor_item)?;
  115. item_drop_packets.push((area_client.client, SendShipPacket::Message(Message::new(GameMessage::ItemDrop(item_drop_msg)))));
  116. }
  117. Ok(item_drop_packets)
  118. }
  119. pub async fn pickup_item<EG>(id: ClientId,
  120. pickup_item: PickupItem,
  121. entity_gateway: &mut EG,
  122. client_location: &ClientLocation,
  123. clients: &Clients,
  124. item_state: &mut ItemState)
  125. -> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
  126. where
  127. EG: EntityGateway + Clone + 'static,
  128. {
  129. let area_client = client_location.get_local_client(id).await?;
  130. let room_id = client_location.get_room(id).await?;
  131. let clients_in_area = client_location.get_clients_in_room(room_id).await?;
  132. clients.with(id, |client| {
  133. let mut entity_gateway = entity_gateway.clone();
  134. let mut item_state = item_state.clone();
  135. Box::pin(async move {
  136. let (item, floor_type) = item_state.get_floor_item(&client.character.id, &ClientItemId(pickup_item.item_id)).await?;
  137. let remove_item = builder::message::remove_item_from_floor(area_client, &item)?;
  138. let create_item = match &item.item {
  139. FloorItemDetail::Individual(individual_floor_item) => Some(builder::message::create_individual_item(area_client, item.item_id, individual_floor_item)?),
  140. FloorItemDetail::Stacked(stacked_floor_item) => Some(builder::message::create_stacked_item(area_client, item.item_id, &stacked_floor_item.tool, stacked_floor_item.count())?),
  141. FloorItemDetail::Meseta(_) => None,
  142. };
  143. match pick_up_item(&mut item_state, &mut entity_gateway, &client.character, &ClientItemId(pickup_item.item_id)).await {
  144. Ok(trigger_create_item) => {
  145. let remove_packets: Box<dyn Iterator<Item=(ClientId, SendShipPacket)> + Send> = match floor_type {
  146. FloorType::Local => {
  147. Box::new(vec![(id, SendShipPacket::Message(Message::new(GameMessage::RemoveItemFromFloor(remove_item.clone()))))].into_iter())
  148. },
  149. FloorType::Shared => {
  150. Box::new(clients_in_area.clone().into_iter()
  151. .map(move |c| {
  152. (c.client, SendShipPacket::Message(Message::new(GameMessage::RemoveItemFromFloor(remove_item.clone()))))
  153. }))
  154. },
  155. };
  156. Ok(remove_packets
  157. .chain(clients_in_area.into_iter()
  158. .filter_map(move |c| {
  159. match trigger_create_item {
  160. TriggerCreateItem::Yes => create_item.clone().map(|ci| (c.client, SendShipPacket::Message(Message::new(GameMessage::CreateItem(ci))))),
  161. _ => None
  162. }
  163. }))
  164. .collect())
  165. },
  166. Err(err) => {
  167. warn!("character {:?} could not pick up item: {:?}", client.character.id, err);
  168. Ok(Vec::new())
  169. },
  170. }
  171. })}).await?
  172. }
  173. pub async fn request_box_item<EG>(id: ClientId,
  174. box_drop_request: BoxDropRequest,
  175. entity_gateway: &mut EG,
  176. client_location: &ClientLocation,
  177. clients: &Clients,
  178. rooms: &Rooms,
  179. item_state: &mut ItemState)
  180. -> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
  181. where
  182. EG: EntityGateway + Clone + 'static
  183. {
  184. let room_id = client_location.get_room(id).await?;
  185. let (room_entity_id, box_object) = rooms.with(room_id, |room| Box::pin(async move {
  186. Ok::<_, anyhow::Error>((room.room_id, room.maps.object_by_id(box_drop_request.object_id as usize)?))
  187. })).await??;
  188. if box_object.dropped_item {
  189. return Err(ShipError::BoxAlreadyDroppedItem(id, box_drop_request.object_id).into())
  190. }
  191. let clients_in_area = client_location.get_clients_in_room(room_id).await?;
  192. let client_and_drop = rooms.with_mut(room_id, |room| Box::pin(async move {
  193. clients_in_area.into_iter()
  194. .filter_map(move |area_client| {
  195. room.drop_table.get_box_drop(&box_object.map, &box_object).map(|item_drop_type| {
  196. (area_client, item_drop_type)
  197. })
  198. })
  199. .collect::<Vec<_>>()
  200. })).await?;
  201. let mut item_drop_packets = Vec::new();
  202. for (area_client, item_drop) in client_and_drop {
  203. let item_drop = ItemDrop {
  204. map_area: box_object.map,
  205. x: box_drop_request.x,
  206. y: 0.0,
  207. z: box_drop_request.z,
  208. item: item_drop,
  209. };
  210. //let client = clients.get_mut(&area_client.client).ok_or(ShipError::ClientNotFound(area_client.client))?;
  211. let character_id = clients.with(area_client.client, |client| Box::pin(async move {
  212. client.character.id
  213. })).await?;
  214. let floor_item = box_drops_item(item_state, entity_gateway, character_id, room_entity_id, item_drop).await?;
  215. //let floor_item = enemy_drops_item(item_state, &mut entity_gateway, client.character.id, item_drop).await?;
  216. let item_drop_msg = builder::message::item_drop(box_drop_request.client, box_drop_request.target, &floor_item)?;
  217. item_drop_packets.push((area_client.client, SendShipPacket::Message(Message::new(GameMessage::ItemDrop(item_drop_msg)))))
  218. }
  219. Ok(item_drop_packets)
  220. }
  221. pub async fn send_bank_list(id: ClientId,
  222. clients: &Clients,
  223. item_state: &mut ItemState)
  224. -> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
  225. {
  226. let bank = clients.with(id, |client| {
  227. let item_state = item_state.clone();
  228. Box::pin(async move {
  229. item_state.get_character_bank(&client.character).await
  230. })
  231. }).await??;
  232. let bank_items_pkt = builder::message::bank_item_list(&bank);
  233. Ok(vec![(id, SendShipPacket::BankItemList(bank_items_pkt))])
  234. }
  235. pub async fn bank_interaction<EG>(id: ClientId,
  236. bank_interaction: BankInteraction,
  237. entity_gateway: &mut EG,
  238. client_location: &ClientLocation,
  239. clients: &Clients,
  240. item_state: &mut ItemState)
  241. -> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
  242. where
  243. EG: EntityGateway + Clone + 'static,
  244. {
  245. let area_client = client_location.get_local_client(id).await?;
  246. let other_clients_in_area = client_location.get_all_clients_by_client(id).await?;
  247. let bank_action_pkts = clients.with(id, |client| {
  248. let mut entity_gateway = entity_gateway.clone();
  249. let mut item_state = item_state.clone();
  250. Box::pin(async move {
  251. Ok::<_, anyhow::Error>(match bank_interaction.action {
  252. BANK_ACTION_DEPOSIT => {
  253. if bank_interaction.item_id == 0xFFFFFFFF {
  254. deposit_meseta(&mut item_state, &mut entity_gateway, &client.character, bank_interaction.meseta_amount).await?;
  255. Vec::new()
  256. }
  257. else {
  258. deposit_item(&mut item_state, &mut entity_gateway, &client.character, &ClientItemId(bank_interaction.item_id), bank_interaction.item_amount as u32).await?;
  259. let player_no_longer_has_item = builder::message::player_no_longer_has_item(area_client, ClientItemId(bank_interaction.item_id), bank_interaction.item_amount as u32);
  260. vec![SendShipPacket::Message(Message::new(GameMessage::PlayerNoLongerHasItem(player_no_longer_has_item)))]
  261. }
  262. },
  263. BANK_ACTION_WITHDRAW => {
  264. if bank_interaction.item_id == 0xFFFFFFFF {
  265. withdraw_meseta(&mut item_state, &mut entity_gateway, &client.character, bank_interaction.meseta_amount).await?;
  266. Vec::new()
  267. }
  268. else {
  269. let item_added_to_inventory = withdraw_item(&mut item_state, &mut entity_gateway, &client.character, &ClientItemId(bank_interaction.item_id), bank_interaction.item_amount as u32).await?;
  270. let item_created = builder::message::create_withdrawn_inventory_item2(area_client, &item_added_to_inventory)?;
  271. vec![SendShipPacket::Message(Message::new(GameMessage::CreateItem(item_created)))]
  272. }
  273. },
  274. _ => { // TODO: error?
  275. Vec::new()
  276. }
  277. })
  278. })
  279. }).await??;
  280. Ok(other_clients_in_area.into_iter()
  281. .flat_map(move |c| {
  282. bank_action_pkts.clone().into_iter()
  283. .map(move |pkt| {
  284. (c.client, pkt)
  285. })
  286. })
  287. .collect()
  288. )
  289. }
  290. pub async fn shop_request(id: ClientId,
  291. shop_request: ShopRequest,
  292. client_location: &ClientLocation,
  293. clients: &Clients,
  294. rooms: &Rooms,
  295. shops: &ItemShops)
  296. -> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
  297. {
  298. //let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
  299. let room_id = client_location.get_room(id).await?;
  300. /*
  301. let room = rooms.get(room_id.0)
  302. .ok_or(ShipError::InvalidRoom(room_id.0 as u32))?
  303. .as_ref()
  304. .ok_or(ShipError::InvalidRoom(room_id.0 as u32))?;
  305. */
  306. let difficulty = rooms.with(room_id, |room| Box::pin(async move {
  307. room.mode.difficulty()
  308. })).await?;
  309. let shop_list = clients.with_mut(id, |client| {
  310. let mut shops = shops.clone();
  311. Box::pin(async move {
  312. let level = LEVEL_TABLE.get_level_from_exp(client.character.char_class, client.character.exp) as usize;
  313. match shop_request.shop_type {
  314. SHOP_OPTION_WEAPON => {
  315. client.weapon_shop = shops.weapon_shop.get_mut(&(difficulty, client.character.section_id))
  316. .ok_or(ShipError::ShopError)?
  317. .lock()
  318. .await
  319. .generate_weapon_list(level);
  320. Ok(builder::message::shop_list(shop_request.shop_type, &client.weapon_shop))
  321. },
  322. SHOP_OPTION_TOOL => {
  323. client.tool_shop = shops.tool_shop
  324. .lock()
  325. .await
  326. .generate_tool_list(level);
  327. Ok(builder::message::shop_list(shop_request.shop_type, &client.tool_shop))
  328. },
  329. SHOP_OPTION_ARMOR => {
  330. client.armor_shop = shops.armor_shop
  331. .lock()
  332. .await
  333. .generate_armor_list(level);
  334. Ok(builder::message::shop_list(shop_request.shop_type, &client.armor_shop))
  335. },
  336. _ => {
  337. Err(ShipError::ShopError)
  338. }
  339. }
  340. })}).await??;
  341. /*
  342. let shop_list = match shop_request.shop_type {
  343. SHOP_OPTION_WEAPON => {
  344. client.weapon_shop = shops.weapon_shop.get_mut(&(room.mode.difficulty(), client.character.section_id))
  345. .ok_or(ShipError::ShopError)?
  346. .generate_weapon_list(level);
  347. builder::message::shop_list(shop_request.shop_type, &client.weapon_shop)
  348. },
  349. SHOP_OPTION_TOOL => {
  350. client.tool_shop = shops.tool_shop.generate_tool_list(level);
  351. builder::message::shop_list(shop_request.shop_type, &client.tool_shop)
  352. },
  353. SHOP_OPTION_ARMOR => {
  354. client.armor_shop = shops.armor_shop.generate_armor_list(level);
  355. builder::message::shop_list(shop_request.shop_type, &client.armor_shop)
  356. },
  357. _ => {
  358. return Err(ShipError::ShopError.into())
  359. }
  360. };
  361. */
  362. Ok(vec![(id, SendShipPacket::Message(Message::new(GameMessage::ShopList(shop_list))))])
  363. }
  364. pub async fn buy_item<EG>(id: ClientId,
  365. buy_item: BuyItem,
  366. entity_gateway: &mut EG,
  367. client_location: &ClientLocation,
  368. clients: &Clients,
  369. item_state: &mut ItemState)
  370. -> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
  371. where
  372. EG: EntityGateway + Clone + 'static,
  373. {
  374. let area_client = client_location.get_local_client(id).await?;
  375. let create = clients.with_mut(id, |client| {
  376. let mut entity_gateway = entity_gateway.clone();
  377. let mut item_state = item_state.clone();
  378. Box::pin(async move {
  379. let (item, remove): (&(dyn ShopItem + Send + Sync), bool) = match buy_item.shop_type {
  380. SHOP_OPTION_WEAPON => {
  381. (client.weapon_shop.get(buy_item.shop_index as usize).ok_or(ShipError::ShopError)?, false)
  382. },
  383. SHOP_OPTION_TOOL => {
  384. let item = client.tool_shop.get(buy_item.shop_index as usize).ok_or(ShipError::ShopError)?;
  385. let remove = matches!(item, ToolShopItem::Tech(_));
  386. (item, remove)
  387. },
  388. SHOP_OPTION_ARMOR => {
  389. let item = client.armor_shop.get(buy_item.shop_index as usize).ok_or(ShipError::ShopError)?;
  390. let remove = matches!(item, ArmorShopItem::Unit(_));
  391. (item, remove)
  392. },
  393. _ => {
  394. return Err(ShipError::ShopError.into())
  395. }
  396. };
  397. let inventory_item = buy_shop_item(&mut item_state, &mut entity_gateway, &client.character, item, ClientItemId(buy_item.item_id), buy_item.amount as u32).await?;
  398. if remove {
  399. match buy_item.shop_type {
  400. SHOP_OPTION_TOOL => {
  401. client.tool_shop.remove(buy_item.shop_index as usize);
  402. },
  403. SHOP_OPTION_ARMOR => {
  404. client.armor_shop.remove(buy_item.shop_index as usize);
  405. },
  406. _ => {}
  407. }
  408. }
  409. Ok::<_, anyhow::Error>(builder::message::create_withdrawn_inventory_item(area_client, &inventory_item)?)
  410. })}).await??;
  411. let other_clients_in_area = client_location.get_client_neighbors(id).await?;
  412. Ok(other_clients_in_area.into_iter()
  413. .map(move |c| {
  414. (c.client, SendShipPacket::Message(Message::new(GameMessage::CreateItem(create.clone()))))
  415. })
  416. .collect())
  417. }
  418. const TEK_SPECIAL_MODIFIER: [item::weapon::TekSpecialModifier; 3] = [item::weapon::TekSpecialModifier::Plus,
  419. item::weapon::TekSpecialModifier::Neutral,
  420. item::weapon::TekSpecialModifier::Minus];
  421. const TEK_PERCENT_MODIFIER: [item::weapon::TekPercentModifier; 5] = [item::weapon::TekPercentModifier::PlusPlus,
  422. item::weapon::TekPercentModifier::Plus,
  423. item::weapon::TekPercentModifier::Neutral,
  424. item::weapon::TekPercentModifier::Minus,
  425. item::weapon::TekPercentModifier::MinusMinus];
  426. pub async fn request_tek_item<EG>(id: ClientId,
  427. tek_request: TekRequest,
  428. entity_gateway: &mut EG,
  429. clients: &Clients,
  430. item_state: &mut ItemState)
  431. -> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
  432. where
  433. EG: EntityGateway + Clone + 'static,
  434. {
  435. //let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
  436. // TODO: secids have different mod rates
  437. let (grind_mod, special_mod, percent_mod) = {
  438. let mut rng = rand::thread_rng();
  439. let grind_mod = rng.gen_range(-4, 4);
  440. let special_mod = TEK_SPECIAL_MODIFIER.choose(&mut rng).cloned().unwrap();
  441. let percent_mod = TEK_PERCENT_MODIFIER.choose(&mut rng).cloned().unwrap();
  442. (grind_mod, special_mod, percent_mod)
  443. };
  444. let preview_pkt = clients.with_mut(id, |client| {
  445. let mut entity_gateway = entity_gateway.clone();
  446. let mut item_state = item_state.clone();
  447. Box::pin(async move {
  448. client.tek = Some((ClientItemId(tek_request.item_id), special_mod, percent_mod, grind_mod));
  449. let inventory = item_state.get_character_inventory(&client.character).await?;
  450. let item = inventory.get_by_client_id(&ClientItemId(tek_request.item_id))
  451. .ok_or_else(|| ItemStateError::WrongItemType(ClientItemId(tek_request.item_id)))?;
  452. let mut weapon = *item.item.as_individual()
  453. .ok_or_else(|| ItemStateError::WrongItemType(ClientItemId(tek_request.item_id)))?
  454. .as_weapon()
  455. .ok_or_else(|| ItemStateError::WrongItemType(ClientItemId(tek_request.item_id)))?;
  456. weapon.apply_modifier(&item::weapon::WeaponModifier::Tekked {
  457. special: special_mod,
  458. percent: percent_mod,
  459. grind: grind_mod,
  460. });
  461. take_meseta(&mut item_state, &mut entity_gateway, &client.character.id, item::Meseta(100)).await?;
  462. Ok::<_, anyhow::Error>(builder::message::tek_preview(ClientItemId(tek_request.item_id), &weapon)?)
  463. })}).await??;
  464. Ok(vec![(id, SendShipPacket::Message(Message::new(GameMessage::TekPreview(preview_pkt))))])
  465. }
  466. pub async fn accept_tek_item<EG>(id: ClientId,
  467. tek_accept: TekAccept,
  468. entity_gateway: &mut EG,
  469. client_location: &ClientLocation,
  470. clients: &Clients,
  471. item_state: &mut ItemState)
  472. -> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
  473. where
  474. EG: EntityGateway + Clone + 'static,
  475. {
  476. let area_client = client_location.get_local_client(id).await?;
  477. let neighbors = client_location.get_client_neighbors(id).await?;
  478. clients.with(id, |client| {
  479. let mut entity_gateway = entity_gateway.clone();
  480. let mut item_state = item_state.clone();
  481. Box::pin(async move {
  482. if let Some((item_id, special_mod, percent_mod, grind_mod)) = client.tek {
  483. if item_id.0 != tek_accept.item_id {
  484. return Err(MessageError::MismatchedTekIds(item_id, ClientItemId(tek_accept.item_id)).into());
  485. }
  486. let modifier = item::weapon::WeaponModifier::Tekked {
  487. special: special_mod,
  488. percent: percent_mod,
  489. grind: grind_mod,
  490. };
  491. let weapon = apply_modifier(&mut item_state, &mut entity_gateway, &client.character, item_id, item::ItemModifier::WeaponModifier(modifier)).await?;
  492. let create_item_pkt = builder::message::create_individual_item(area_client, item_id, &weapon)?;
  493. Ok(neighbors.into_iter()
  494. .map(move |c| {
  495. (c.client, SendShipPacket::Message(Message::new(GameMessage::CreateItem(create_item_pkt.clone()))))
  496. })
  497. .collect())
  498. }
  499. else {
  500. Err(MessageError::InvalidTek(ClientItemId(tek_accept.item_id)).into())
  501. }
  502. })}).await?
  503. }