tekking!
This commit is contained in:
parent
b45a0ba1d5
commit
f4fae162f0
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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()?,
|
||||
})
|
||||
}
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
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_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()))))
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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()
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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…
x
Reference in New Issue
Block a user