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. 17
      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::{Meseta, NewItemEntity, ItemEntity, InventoryItemEntity, EquippedEntity, InventoryEntity, BankItemEntity, BankEntity};
use crate::entity::item::tool::{Tool, ToolType}; use crate::entity::item::tool::{Tool, ToolType};
use crate::entity::item::unit; use crate::entity::item::unit;
use crate::entity::item::weapon;
use crate::ship::map::MapArea; use crate::ship::map::MapArea;
use crate::ship::ship::ItemDropLocation; use crate::ship::ship::ItemDropLocation;
use crate::ship::drops::{ItemDrop, ItemDropType}; 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?; entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
Ok(()) 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, 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 log::warn;
use rand::Rng;
use rand::seq::SliceRandom;
use libpso::packet::ship::*; use libpso::packet::ship::*;
use libpso::packet::messages::*; use libpso::packet::messages::*;
use crate::common::leveltable::CharacterLevelTable; 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::ship::{SendShipPacket, ShipError, Clients, Rooms, ItemShops};
use crate::ship::location::{ClientLocation, ClientLocationError}; use crate::ship::location::{ClientLocation, ClientLocationError};
use crate::ship::drops::ItemDrop; 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::gateway::EntityGateway;
use crate::entity::item;
use libpso::utf8_to_utf16_array; use libpso::utf8_to_utf16_array;
use crate::ship::packet::builder; use crate::ship::packet::builder;
use crate::ship::shops::{ShopItem, ToolShopItem, ArmorShopItem}; use crate::ship::shops::{ShopItem, ToolShopItem, ArmorShopItem};
@ -24,6 +27,13 @@ const BANK_MESETA_CAPACITY: u32 = 999999;
//const BANK_ACTION_: u8 = 1; //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) fn send_to_client(id: ClientId, target: u8, msg: DirectMessage, client_location: &ClientLocation)
-> Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> { -> Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> {
Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter() 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())
}
}

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

@ -114,7 +114,7 @@ pub fn drop_coordinates(id: ClientId,
Ok(Box::new(None.into_iter())) Ok(Box::new(None.into_iter()))
} }
pub async fn split_item_stack<EG>(id: ClientId,
pub async fn no_longer_has_item<EG>(id: ClientId,
no_longer_has_item: &PlayerNoLongerHasItem, no_longer_has_item: &PlayerNoLongerHasItem,
entity_gateway: &mut EG, entity_gateway: &mut EG,
client_location: &ClientLocation, client_location: &ClientLocation,
@ -127,8 +127,7 @@ where
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; 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 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_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 { if drop_location.item_id.0 != no_longer_has_item.item_id {
return Err(ShipError::DropInvalidItemId(no_longer_has_item.item_id).into()); return Err(ShipError::DropInvalidItemId(no_longer_has_item.item_id).into());
} }
@ -158,6 +157,18 @@ where
}))) })))
} }
} }
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::PlayerNoLongerHasItem(no_longer_has_item.clone()))))
})))
}
else {
Err(ShipError::InvalidItem(ClientItemId(no_longer_has_item.item_id)).into())
}
}
pub fn update_player_position(id: ClientId, pub fn update_player_position(id: ClientId,
message: &Message, message: &Message,

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

Loading…
Cancel
Save