Browse Source

tekking!

pbs
jake 4 years ago
parent
commit
f4fae162f0
  1. 35
      src/ship/items/manager.rs
  2. 11
      src/ship/packet/builder/message.rs
  3. 105
      src/ship/packet/handler/direct_message.rs
  4. 67
      src/ship/packet/handler/message.rs
  5. 12
      src/ship/ship.rs

35
src/ship/items/manager.rs

@ -7,6 +7,7 @@ use crate::entity::item::{ItemDetail, ItemLocation, BankName};
use crate::entity::item::{Meseta, NewItemEntity, ItemEntity, InventoryItemEntity, EquippedEntity, InventoryEntity, BankItemEntity, BankEntity};
use crate::entity::item::tool::{Tool, ToolType};
use crate::entity::item::unit;
use crate::entity::item::weapon;
use crate::ship::map::MapArea;
use crate::ship::ship::ItemDropLocation;
use crate::ship::drops::{ItemDrop, ItemDropType};
@ -888,4 +889,38 @@ impl ItemManager {
entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
Ok(())
}
pub async fn replace_item_with_tekked<EG: EntityGateway>(&mut self,
entity_gateway: &mut EG,
character: &CharacterEntity,
item_id: ClientItemId,
tek: weapon::WeaponModifier)
-> Result<weapon::Weapon, anyhow::Error> {
let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
let item = inventory.remove_by_id(item_id)
.ok_or(ItemManagerError::NoSuchItemId(item_id))?;
let individual = item
.individual()
.ok_or(ItemManagerError::WrongItemType(item_id))?;
let entity_id = individual.entity_id;
let mut weapon = individual
.weapon()
.ok_or(ItemManagerError::WrongItemType(item_id))?
.clone();
weapon.apply_modifier(&tek);
entity_gateway.add_weapon_modifier(&entity_id, tek).await?;
inventory.add_item(InventoryItem::Individual(IndividualInventoryItem {
entity_id: entity_id,
item_id: item_id,
item: ItemDetail::Weapon(weapon.clone()),
}));
entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
Ok(weapon)
}
}

11
src/ship/packet/builder/message.rs

@ -163,3 +163,14 @@ pub fn shop_list<I: ShopItem>(shop_type: u8, items: &Vec<I>) -> ShopList {
items: items,
}
}
pub fn tek_preview(id: ClientItemId, weapon: &item::weapon::Weapon) -> Result<TekPreview, ShipError> {
let bytes = weapon.as_bytes();
Ok(TekPreview {
client: 0x79,
target: 0,
item_bytes: bytes[0..12].try_into()?,
item_id: id.0,
item_bytes2: bytes[12..16].try_into()?,
})
}

105
src/ship/packet/handler/direct_message.rs

@ -1,4 +1,6 @@
use log::warn;
use rand::Rng;
use rand::seq::SliceRandom;
use libpso::packet::ship::*;
use libpso::packet::messages::*;
use crate::common::leveltable::CharacterLevelTable;
@ -6,8 +8,9 @@ use crate::common::serverstate::ClientId;
use crate::ship::ship::{SendShipPacket, ShipError, Clients, Rooms, ItemShops};
use crate::ship::location::{ClientLocation, ClientLocationError};
use crate::ship::drops::ItemDrop;
use crate::ship::items::{ItemManager, ClientItemId, TriggerCreateItem, FloorItem, FloorType};
use crate::ship::items::{ItemManager, ItemManagerError, ClientItemId, TriggerCreateItem, FloorItem, FloorType};
use crate::entity::gateway::EntityGateway;
use crate::entity::item;
use libpso::utf8_to_utf16_array;
use crate::ship::packet::builder;
use crate::ship::shops::{ShopItem, ToolShopItem, ArmorShopItem};
@ -24,6 +27,13 @@ const BANK_MESETA_CAPACITY: u32 = 999999;
//const BANK_ACTION_: u8 = 1;
#[derive(thiserror::Error, Debug)]
#[error("")]
pub enum MessageError {
InvalidTek(ClientItemId),
MismatchedTekIds(ClientItemId, ClientItemId),
}
fn send_to_client(id: ClientId, target: u8, msg: DirectMessage, client_location: &ClientLocation)
-> Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> {
Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter()
@ -395,3 +405,96 @@ where
})))
}
const TEK_SPECIAL_MODIFIER: [item::weapon::TekSpecialModifier; 3] = [item::weapon::TekSpecialModifier::Plus,
item::weapon::TekSpecialModifier::Neutral,
item::weapon::TekSpecialModifier::Minus];
const TEK_PERCENT_MODIFIER: [item::weapon::TekPercentModifier; 5] = [item::weapon::TekPercentModifier::PlusPlus,
item::weapon::TekPercentModifier::Plus,
item::weapon::TekPercentModifier::Neutral,
item::weapon::TekPercentModifier::Minus,
item::weapon::TekPercentModifier::MinusMinus];
pub async fn request_tek_item<EG>(id: ClientId,
tek_request: &TekRequest,
entity_gateway: &mut EG,
clients: &mut Clients,
item_manager: &mut ItemManager)
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
where
EG: EntityGateway
{
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
let (grind_mod, special_mod, percent_mod) = {
let mut rng = rand::thread_rng();
let grind_mod = rng.gen_range(-4, 4);
let special_mod = TEK_SPECIAL_MODIFIER.choose(&mut rng).cloned().unwrap();
let percent_mod = TEK_PERCENT_MODIFIER.choose(&mut rng).cloned().unwrap();
(grind_mod, special_mod, percent_mod)
};
client.tek = Some((ClientItemId(tek_request.item_id), special_mod, percent_mod, grind_mod));
let inventory = item_manager.get_character_inventory(&client.character)?;
let item = inventory.get_item_by_id(ClientItemId(tek_request.item_id))
.ok_or(ItemManagerError::WrongItemType(ClientItemId(tek_request.item_id)))?;
let mut weapon = item.individual()
.ok_or(ItemManagerError::WrongItemType(ClientItemId(tek_request.item_id)))?
.weapon()
.ok_or(ItemManagerError::WrongItemType(ClientItemId(tek_request.item_id)))?
.clone();
weapon.apply_modifier(&item::weapon::WeaponModifier::Tekked {
special: special_mod,
percent: percent_mod,
grind: grind_mod,
});
client.character.meseta -= 100;
entity_gateway.save_character(&client.character).await?;
let preview_pkt = builder::message::tek_preview(ClientItemId(tek_request.item_id), &weapon)?;
Ok(Box::new(vec![(id, SendShipPacket::Message(Message::new(GameMessage::TekPreview(preview_pkt))))].into_iter()))
}
pub async fn accept_tek_item<EG>(id: ClientId,
tek_accept: &TekAccept,
entity_gateway: &mut EG,
client_location: &ClientLocation,
clients: &mut Clients,
item_manager: &mut ItemManager)
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
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() })?;
if let Some((item_id, special_mod, percent_mod, grind_mod)) = client.tek {
if item_id.0 != tek_accept.item_id {
return Err(MessageError::MismatchedTekIds(item_id, ClientItemId(tek_accept.item_id)).into());
}
let modifier = item::weapon::WeaponModifier::Tekked {
special: special_mod,
percent: percent_mod,
grind: grind_mod,
};
let weapon = item_manager.replace_item_with_tekked(entity_gateway, &client.character, item_id, modifier).await?;
let create_item_pkt = builder::message::create_item(area_client, item_id, &item::ItemDetail::Weapon(weapon))?;
let neighbors = client_location.get_client_neighbors(id).map_err(|err| -> ClientLocationError { err.into() })?;
Ok(Box::new(neighbors.into_iter()
.map(move |c| {
(c.client, SendShipPacket::Message(Message::new(GameMessage::CreateItem(create_item_pkt.clone()))))
})))
}
else {
Err(MessageError::InvalidTek(ClientItemId(tek_accept.item_id)).into())
}
}

67
src/ship/packet/handler/message.rs

@ -114,48 +114,59 @@ pub fn drop_coordinates(id: ClientId,
Ok(Box::new(None.into_iter()))
}
pub async fn split_item_stack<EG>(id: ClientId,
no_longer_has_item: &PlayerNoLongerHasItem,
entity_gateway: &mut EG,
client_location: &ClientLocation,
clients: &mut Clients,
item_manager: &mut ItemManager)
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
pub async fn no_longer_has_item<EG>(id: ClientId,
no_longer_has_item: &PlayerNoLongerHasItem,
entity_gateway: &mut EG,
client_location: &ClientLocation,
clients: &mut Clients,
item_manager: &mut ItemManager)
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
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 drop_location = client.item_drop_location.ok_or(ShipError::ItemDropLocationNotSet)?;
if let Some(drop_location) = client.item_drop_location {
if drop_location.item_id.0 != no_longer_has_item.item_id {
return Err(ShipError::DropInvalidItemId(no_longer_has_item.item_id).into());
}
if drop_location.item_id.0 != no_longer_has_item.item_id {
return Err(ShipError::DropInvalidItemId(no_longer_has_item.item_id).into());
}
if no_longer_has_item.item_id == 0xFFFFFFFF {
let dropped_meseta = item_manager.player_drops_meseta_on_shared_floor(entity_gateway, &mut client.character, drop_location, no_longer_has_item.amount as u32).await?;
let dropped_meseta_pkt = builder::message::drop_split_meseta_stack(area_client, &dropped_meseta)?;
client.item_drop_location = None;
if no_longer_has_item.item_id == 0xFFFFFFFF {
let dropped_meseta = item_manager.player_drops_meseta_on_shared_floor(entity_gateway, &mut client.character, drop_location, no_longer_has_item.amount as u32).await?;
let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
Ok(Box::new(clients_in_area.into_iter()
.map(move |c| {
(c.client, SendShipPacket::Message(Message::new(GameMessage::DropSplitStack(dropped_meseta_pkt.clone()))))
})))
}
else {
let dropped_item = item_manager.player_drops_partial_stack_on_shared_floor(entity_gateway, &client.character, drop_location.item_id, drop_location, no_longer_has_item.amount as usize).await?;
let dropped_meseta_pkt = builder::message::drop_split_meseta_stack(area_client, &dropped_meseta)?;
client.item_drop_location = None;
let dropped_item_pkt = builder::message::drop_split_stack(area_client, dropped_item)?;
client.item_drop_location = None;
let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
Ok(Box::new(clients_in_area.into_iter()
let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
Ok(Box::new(clients_in_area.into_iter()
.map(move |c| {
(c.client, SendShipPacket::Message(Message::new(GameMessage::DropSplitStack(dropped_item_pkt.clone()))))
})))
}
}
else if let Some(_tek) = client.tek {
let neighbors = client_location.get_client_neighbors(id).map_err(|err| -> ClientLocationError { err.into() })?;
let no_longer_has_item = no_longer_has_item.clone();
Ok(Box::new(neighbors.into_iter()
.map(move |c| {
(c.client, SendShipPacket::Message(Message::new(GameMessage::DropSplitStack(dropped_meseta_pkt.clone()))))
(c.client, SendShipPacket::Message(Message::new(GameMessage::PlayerNoLongerHasItem(no_longer_has_item.clone()))))
})))
}
else {
let dropped_item = item_manager.player_drops_partial_stack_on_shared_floor(entity_gateway, &client.character, drop_location.item_id, drop_location, no_longer_has_item.amount as usize).await?;
let dropped_item_pkt = builder::message::drop_split_stack(area_client, dropped_item)?;
client.item_drop_location = None;
let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
Ok(Box::new(clients_in_area.into_iter()
.map(move |c| {
(c.client, SendShipPacket::Message(Message::new(GameMessage::DropSplitStack(dropped_item_pkt.clone()))))
})))
Err(ShipError::InvalidItem(ClientItemId(no_longer_has_item.item_id)).into())
}
}

12
src/ship/ship.rs

@ -23,6 +23,7 @@ use crate::login::character::SHIP_MENU_ID;
use crate::entity::gateway::{EntityGateway, GatewayError};
use crate::entity::account::{UserAccountEntity, UserSettingsEntity};
use crate::entity::character::{CharacterEntity, SectionID};
use crate::entity::item;
use crate::ship::location::{ClientLocation, RoomLobby, MAX_ROOMS, ClientLocationError, GetNeighborError, GetClientsError, GetAreaError};
@ -71,6 +72,7 @@ pub enum ShipError {
UnknownMonster(crate::ship::monster::MonsterType),
InvalidShip(usize),
InvalidBlock(usize),
InvalidItem(items::ClientItemId),
}
#[derive(Debug)]
@ -254,6 +256,7 @@ pub struct ClientState {
pub weapon_shop: Vec<WeaponShopItem>,
pub tool_shop: Vec<ToolShopItem>,
pub armor_shop: Vec<ArmorShopItem>,
pub tek: Option<(items::ClientItemId, item::weapon::TekSpecialModifier, item::weapon::TekPercentModifier, i32)>,
}
impl ClientState {
@ -273,6 +276,7 @@ impl ClientState {
weapon_shop: Vec::new(),
tool_shop: Vec::new(),
armor_shop: Vec::new(),
tek: None,
}
}
}
@ -445,7 +449,7 @@ impl<EG: EntityGateway> ShipServerState<EG> {
},
GameMessage::PlayerNoLongerHasItem(no_longer_has_item) => {
let block = self.blocks.with_client(id, &self.clients)?;
handler::message::split_item_stack(id, no_longer_has_item, &mut self.entity_gateway, &mut block.client_location, &mut self.clients, &mut self.item_manager).await?
handler::message::no_longer_has_item(id, no_longer_has_item, &mut self.entity_gateway, &mut block.client_location, &mut self.clients, &mut self.item_manager).await?
},
GameMessage::PlayerChangedMap(_) | GameMessage::PlayerChangedMap2(_) | GameMessage::TellOtherPlayerMyLocation(_) |
GameMessage::PlayerWarpingToFloor(_) | GameMessage::PlayerTeleported(_) | GameMessage::PlayerStopped(_) |
@ -516,6 +520,12 @@ impl<EG: EntityGateway> ShipServerState<EG> {
GameMessage::BuyItem(buy_item) => {
handler::direct_message::buy_item(id, buy_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await?
},
GameMessage::TekRequest(tek_request) => {
handler::direct_message::request_tek_item(id, tek_request, &mut self.entity_gateway, &mut self.clients, &mut self.item_manager).await?
},
GameMessage::TekAccept(tek_accept) => {
handler::direct_message::accept_tek_item(id, tek_accept, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await?
},
_ => {
let cmsg = msg.clone();
Box::new(block.client_location.get_all_clients_by_client(id).unwrap().into_iter()

Loading…
Cancel
Save