diff --git a/src/ship/packet/builder/message.rs b/src/ship/packet/builder/message.rs index 30202a7..14ca50f 100644 --- a/src/ship/packet/builder/message.rs +++ b/src/ship/packet/builder/message.rs @@ -1,4 +1,5 @@ use libpso::packet::messages::*; +use crate::common::leveltable::CharacterStats; use crate::ship::ship::{ShipError}; use crate::ship::items::{FloorItem}; use crate::ship::location::AreaClient; @@ -63,3 +64,25 @@ pub fn drop_split_stack(area_client: AreaClient, item: &FloorItem) -> Result GiveCharacterExp { + GiveCharacterExp { + client: area_client.local_client.id(), + target: 0, + exp: exp, + } +} + +pub fn character_leveled_up(area_client: AreaClient, level: u32, before_stats: CharacterStats, after_stats: CharacterStats) -> PlayerLevelUp { + PlayerLevelUp { + client: area_client.local_client.id(), + target: 0, + atp: after_stats.atp - before_stats.atp, + mst: after_stats.mst - before_stats.mst, + evp: after_stats.evp - before_stats.evp, + hp: after_stats. hp - before_stats. hp, + dfp: after_stats.dfp - before_stats.dfp, + ata: after_stats.ata - before_stats.ata, + lvl: level, + } +} diff --git a/src/ship/packet/handler/message.rs b/src/ship/packet/handler/message.rs index 52f22e2..5551fb4 100644 --- a/src/ship/packet/handler/message.rs +++ b/src/ship/packet/handler/message.rs @@ -3,26 +3,58 @@ use libpso::packet::ship::*; use libpso::packet::messages::*; use crate::entity::gateway::EntityGateway; use crate::common::serverstate::ClientId; +use crate::common::leveltable::CharacterLevelTable; use crate::ship::ship::{SendShipPacket, ShipError, Rooms, Clients, ItemDropLocation}; use crate::ship::location::{ClientLocation, ClientLocationError, RoomLobby}; use crate::ship::map::{MapArea}; use crate::ship::items::{ItemManager, ClientItemId}; use crate::ship::packet::builder; -pub fn request_exp(id: ClientId, - request_exp: &RequestExp, - client_location: &ClientLocation, - rooms: &Rooms) - -> Box + Send> { - - match client_location.get_area(id).unwrap() { - RoomLobby::Room(room) => { - let r = rooms[room.0].as_ref().unwrap(); - warn!("killed a {:?}", r.maps.enemy_by_id(request_exp.enemy_id as usize).unwrap().monster); - }, - _ => {} - }; - Box::new(None.into_iter()) +pub async fn request_exp(id: ClientId, + request_exp: &RequestExp, + entity_gateway: &mut EG, + client_location: &ClientLocation, + clients: &mut Clients, + rooms: &mut Rooms, + level_table: &CharacterLevelTable) + -> Result + Send>, ShipError> { + 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 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_exp.enemy_id as usize)?; + let monster_stats = room.monster_stats.get(&monster.monster).unwrap(); + + let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?; + let gain_exp_pkt = builder::message::character_gained_exp(area_client, monster_stats.exp); + let mut exp_pkts: Box + Send> = Box::new(clients_in_area.clone().into_iter() + .map(move |c| { + (c.client, SendShipPacket::Message(Message::new(GameMessage::GiveCharacterExp(gain_exp_pkt.clone())))) + })); + + let before_level = level_table.get_level_from_exp(client.character.char_class, client.character.exp); + let after_level = level_table.get_level_from_exp(client.character.char_class, client.character.exp + monster_stats.exp); + let level_up = before_level != after_level; + + if level_up { + let (_, before_stats) = level_table.get_stats_from_exp(client.character.char_class, client.character.exp); + let (after_level, after_stats) = level_table.get_stats_from_exp(client.character.char_class, client.character.exp + monster_stats.exp); + + let level_up_pkt = builder::message::character_leveled_up(area_client, after_level, before_stats, after_stats); + exp_pkts = Box::new(exp_pkts.chain(clients_in_area.into_iter() + .map(move |c| { + (c.client, SendShipPacket::Message(Message::new(GameMessage::PlayerLevelUp(level_up_pkt.clone())))) + }))) + } + + client.character.exp += monster_stats.exp; + entity_gateway.save_character(&client.character).await; + + Ok(exp_pkts) } pub async fn player_drop_item(id: ClientId, diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 97c4aa4..ba97417 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -242,7 +242,7 @@ pub struct ShipServerState { client_location: ClientLocation, level_table: CharacterLevelTable, name: String, - rooms: Rooms, + pub rooms: Rooms, pub item_manager: items::ItemManager, quests: quests::QuestList, } @@ -264,7 +264,7 @@ impl ShipServerState { async fn message(&mut self, id: ClientId, msg: &Message) -> Result + Send>, ShipError> { match &msg.msg { GameMessage::RequestExp(request_exp) => { - Ok(handler::message::request_exp(id, request_exp, &self.client_location, &self.rooms)) + handler::message::request_exp(id, request_exp, &mut self.entity_gateway, &self.client_location, &mut self.clients, &mut self.rooms, &self.level_table).await }, GameMessage::PlayerDropItem(player_drop_item) => { handler::message::player_drop_item(id, player_drop_item, &mut self.entity_gateway, &mut self.client_location, &mut self.clients, &mut self.rooms, &mut self.item_manager).await