use log::warn; use libpso::packet::ship::*; use libpso::packet::messages::*; use crate::common::serverstate::ClientId; use crate::ship::ship::{SendShipPacket, ShipError, Clients, Rooms}; use crate::ship::location::{ClientLocation, ClientLocationError}; use crate::ship::drops::ItemDrop; use crate::ship::items::{ItemManager, FloorItemType, ClientItemId}; use crate::entity::gateway::EntityGateway; use libpso::utf8_to_utf16_array; use crate::ship::packet::builder; fn send_to_client(id: ClientId, target: u8, msg: DirectMessage, client_location: &ClientLocation) -> Box + Send> { Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter() .filter(move |client| client.local_client.id() == target) .map(move |client| { (client.client, SendShipPacket::DirectMessage(msg.clone())) })) } pub fn guildcard_send(id: ClientId, guildcard_send: &GuildcardSend, target: u32, client_location: &ClientLocation, clients: &Clients) -> Box + Send> { let client = clients.get(&id).unwrap(); let msg = DirectMessage{ flag: target, msg: GameMessage::GuildcardRecv(GuildcardRecv { client: guildcard_send.client, target: guildcard_send.target, guildcard: client.user.id.0, name: utf8_to_utf16_array!(client.character.name, 0x18), team: [0; 0x10], // TODO: teams not yet implemented desc: utf8_to_utf16_array!(client.character.guildcard.description, 0x58), one: 1, language: 0, // TODO: add language flag to character section_id: client.character.section_id.into(), class: client.character.char_class.into(), }), }; send_to_client(id, target as u8, msg, &client_location) } pub fn request_item(id: ClientId, request_item: &RequestItem, entity_gateway: &mut EG, client_location: &ClientLocation, clients: &mut Clients, rooms: &mut Rooms, item_manager: &mut ItemManager) -> Result + Send>, ShipError> where EG: EntityGateway { let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?; let room = rooms.get_mut(room_id.0) .ok_or_else(|| ShipError::InvalidRoom(room_id.0 as u32))? .as_mut() .ok_or_else(|| ShipError::InvalidRoom(room_id.0 as u32))?; let monster = room.maps.enemy_by_id(request_item.enemy_id as usize)?; if monster.dropped_item { return Err(ShipError::MonsterAlreadyDroppedItem(id, request_item.enemy_id)) } let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?; let item_drop_packets = clients_in_area.into_iter() .filter_map(|area_client| { room.drop_table.get_drop(&monster.map_area, &monster.monster).map(|item_drop_type| { warn!("drop is? {:?}", item_drop_type); (area_client, item_drop_type) }) }) .map(|(area_client, item_drop_type)| -> Result<_, ShipError> { let item_drop = ItemDrop { map_area: monster.map_area, x: request_item.x, y: request_item.y, z: request_item.z, item: item_drop_type, }; let client = clients.get_mut(&area_client.client).ok_or(ShipError::ClientNotFound(area_client.client))?; let floor_item = item_manager.enemy_drop_item_on_local_floor(entity_gateway, &client.character, item_drop).unwrap(); // TODO: unwrap let item_drop_msg = builder::message::item_drop(request_item.client, request_item.target, &floor_item)?; Ok((area_client.client, SendShipPacket::Message(Message::new(GameMessage::ItemDrop(item_drop_msg))))) }) .filter_map(|item_drop_pkt| { // TODO: log errors here item_drop_pkt.ok() }) .collect::>(); // TODO: can EntityGateway be Sync? Ok(Box::new(item_drop_packets.into_iter())) } pub fn pickup_item(id: ClientId, pickup_item: &PickupItem, entity_gateway: &mut EG, client_location: &ClientLocation, clients: &mut Clients, item_manager: &mut ItemManager) -> Result + Send>, ShipError> where EG: EntityGateway { let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?; let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?; let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?; let item = item_manager.get_floor_item_by_id(&client.character, ClientItemId(pickup_item.item_id))?; let remove_item = builder::message::remove_item_from_floor(area_client, &item)?; let create_item = match item.item { FloorItemType::Meseta(_) => None, _ => Some(builder::message::create_item(area_client, &item)?), }; match item_manager.character_picks_up_item(entity_gateway, &mut client.character, item) { Ok(_) => { Ok(Box::new(Vec::new().into_iter() .chain(clients_in_area.clone().into_iter() .map(move |c| { (c.client, SendShipPacket::Message(Message::new(GameMessage::RemoveItemFromFloor(remove_item.clone())))) })) .chain(clients_in_area.into_iter(). filter_map(move |c| { create_item.clone().map(|ci| (c.client, SendShipPacket::Message(Message::new(GameMessage::CreateItem(ci))))) } ))) ) }, Err(err) => { warn!("character {:?} could not pick up item: {:?}", client.character.id, err); Ok(Box::new(None.into_iter())) }, } } pub fn request_box_item(id: ClientId, box_drop_request: &BoxDropRequest, entity_gateway: &mut EG, client_location: &ClientLocation, clients: &mut Clients, rooms: &mut Rooms, item_manager: &mut ItemManager) -> Result + Send>, ShipError> where EG: EntityGateway { let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?; let room = rooms.get_mut(room_id.0) .ok_or_else(|| ShipError::InvalidRoom(room_id.0 as u32))? .as_mut() .ok_or_else(|| ShipError::InvalidRoom(room_id.0 as u32))?; let box_object = room.maps.object_by_id(box_drop_request.object_id as usize)?; if box_object.dropped_item { return Err(ShipError::BoxAlreadyDroppedItem(id, box_drop_request.object_id)) } let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?; let item_drop_packets = clients_in_area.into_iter() .filter_map(|area_client| { room.drop_table.get_box_drop(&box_object.map, &box_object).map(|item_drop_type| { warn!("drop is? {:?}", item_drop_type); (area_client, item_drop_type) }) }) .map(|(area_client, item_drop_type)| -> Result<_, ShipError> { let item_drop = ItemDrop { map_area: box_object.map, x: box_drop_request.x, y: 0.0, z: box_drop_request.z, item: item_drop_type, }; let client = clients.get_mut(&area_client.client).ok_or(ShipError::ClientNotFound(area_client.client))?; let floor_item = item_manager.enemy_drop_item_on_local_floor(entity_gateway, &client.character, item_drop).unwrap(); // TODO: unwrap let item_drop_msg = builder::message::item_drop(box_drop_request.client, box_drop_request.target, &floor_item)?; Ok((area_client.client, SendShipPacket::Message(Message::new(GameMessage::ItemDrop(item_drop_msg))))) }) .filter_map(|item_drop_pkt| { // TODO: log errors here item_drop_pkt.ok() }) .collect::>(); // TODO: can EntityGateway be Sync? Ok(Box::new(item_drop_packets.into_iter())) }