|
@ -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())
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|