2020-04-22 07:14:59 -06:00
|
|
|
use log::warn;
|
2020-12-03 15:04:48 -07:00
|
|
|
use rand::Rng;
|
|
|
|
use rand::seq::SliceRandom;
|
2020-04-22 07:14:59 -06:00
|
|
|
use libpso::packet::ship::*;
|
|
|
|
use libpso::packet::messages::*;
|
2022-08-02 00:08:55 -06:00
|
|
|
use crate::common::leveltable::LEVEL_TABLE;
|
2020-04-22 07:14:59 -06:00
|
|
|
use crate::common::serverstate::ClientId;
|
2022-10-18 04:46:21 -06:00
|
|
|
use crate::ship::ship::{SendShipPacket, ShipError, Clients, ItemShops};
|
2020-05-02 22:08:37 -03:00
|
|
|
use crate::ship::location::{ClientLocation, ClientLocationError};
|
2020-05-05 21:53:04 -06:00
|
|
|
use crate::ship::drops::ItemDrop;
|
2022-10-18 04:46:21 -06:00
|
|
|
use crate::ship::room::Rooms;
|
2022-07-19 10:18:54 -06:00
|
|
|
use crate::ship::items::ClientItemId;
|
2020-04-22 07:14:59 -06:00
|
|
|
use crate::entity::gateway::EntityGateway;
|
2020-12-03 15:04:48 -07:00
|
|
|
use crate::entity::item;
|
2020-05-05 21:53:04 -06:00
|
|
|
use libpso::utf8_to_utf16_array;
|
2020-04-26 22:01:05 -06:00
|
|
|
use crate::ship::packet::builder;
|
2020-09-27 20:30:37 -06:00
|
|
|
use crate::ship::shops::{ShopItem, ToolShopItem, ArmorShopItem};
|
2022-07-19 20:06:43 -06:00
|
|
|
use crate::ship::items::state::{ItemState, ItemStateError};
|
|
|
|
use crate::ship::items::floor::{FloorType, FloorItemDetail};
|
2022-07-19 21:26:00 -06:00
|
|
|
use crate::ship::items::actions::TriggerCreateItem;
|
|
|
|
use crate::ship::items::tasks::{pick_up_item, withdraw_meseta, deposit_meseta, withdraw_item, deposit_item, buy_shop_item, enemy_drops_item, take_meseta, apply_modifier};
|
2020-04-22 07:14:59 -06:00
|
|
|
|
2020-07-19 14:14:07 -06:00
|
|
|
const BANK_ACTION_DEPOSIT: u8 = 0;
|
|
|
|
const BANK_ACTION_WITHDRAW: u8 = 1;
|
2020-09-27 18:16:27 -06:00
|
|
|
|
|
|
|
const SHOP_OPTION_TOOL: u8 = 0;
|
|
|
|
const SHOP_OPTION_WEAPON: u8 = 1;
|
|
|
|
const SHOP_OPTION_ARMOR: u8 = 2;
|
|
|
|
|
2020-12-03 15:04:48 -07:00
|
|
|
#[derive(thiserror::Error, Debug)]
|
|
|
|
pub enum MessageError {
|
2022-07-19 12:34:07 -06:00
|
|
|
#[error("invalid tek {0}")]
|
2020-12-03 15:04:48 -07:00
|
|
|
InvalidTek(ClientItemId),
|
2022-07-19 12:34:07 -06:00
|
|
|
#[error("mismatched tek {0} {1}")]
|
2020-12-03 15:04:48 -07:00
|
|
|
MismatchedTekIds(ClientItemId, ClientItemId),
|
|
|
|
}
|
|
|
|
|
2022-09-18 21:01:32 -06:00
|
|
|
async fn send_to_client(id: ClientId,
|
|
|
|
target: u8,
|
|
|
|
msg: DirectMessage,
|
|
|
|
client_location: &ClientLocation)
|
2022-10-18 04:46:21 -06:00
|
|
|
-> Vec<(ClientId, SendShipPacket)> {
|
|
|
|
client_location.get_all_clients_by_client(id).await.unwrap().into_iter()
|
|
|
|
.filter(move |client| client.local_client.id() == target)
|
|
|
|
.map(move |client| {
|
|
|
|
(client.client, SendShipPacket::DirectMessage(msg.clone()))
|
|
|
|
})
|
|
|
|
.collect()
|
2020-04-22 07:14:59 -06:00
|
|
|
}
|
|
|
|
|
2022-09-18 21:01:32 -06:00
|
|
|
pub async fn guildcard_send(id: ClientId,
|
2022-10-18 04:46:21 -06:00
|
|
|
guildcard_send: GuildcardSend,
|
2022-09-18 21:01:32 -06:00
|
|
|
target: u32,
|
|
|
|
client_location: &ClientLocation,
|
|
|
|
clients: &Clients)
|
2022-10-18 04:46:21 -06:00
|
|
|
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
|
|
|
let msg = clients.with(id, |client| Box::pin(async move {
|
|
|
|
DirectMessage{
|
|
|
|
flag: target,
|
|
|
|
msg: GameMessage::GuildcardRecv(GuildcardRecv {
|
|
|
|
client: guildcard_send.client,
|
|
|
|
target: guildcard_send.target,
|
|
|
|
guildcard: client.user.id.0,
|
|
|
|
name: utf8_to_utf16_array!(client.character.name, 0x18),
|
|
|
|
team: [0; 0x10], // TODO: teams not yet implemented
|
|
|
|
desc: utf8_to_utf16_array!(client.character.guildcard.description, 0x58),
|
|
|
|
one: 1,
|
|
|
|
language: 0, // TODO: add language flag to character
|
|
|
|
section_id: client.character.section_id.into(),
|
|
|
|
class: client.character.char_class.into(),
|
|
|
|
}),
|
|
|
|
}
|
|
|
|
})).await?;
|
|
|
|
|
|
|
|
Ok(send_to_client(id, target as u8, msg, client_location).await)
|
2020-04-22 07:14:59 -06:00
|
|
|
}
|
2020-04-26 22:01:05 -06:00
|
|
|
|
2020-06-02 18:51:18 -06:00
|
|
|
pub async fn request_item<EG>(id: ClientId,
|
2022-10-18 04:46:21 -06:00
|
|
|
request_item: RequestItem,
|
|
|
|
entity_gateway: &mut EG,
|
2020-06-02 18:51:18 -06:00
|
|
|
client_location: &ClientLocation,
|
2022-10-18 04:46:21 -06:00
|
|
|
clients: &Clients,
|
|
|
|
rooms: &Rooms,
|
2022-07-18 20:51:35 -06:00
|
|
|
item_state: &mut ItemState)
|
2022-10-18 04:46:21 -06:00
|
|
|
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
2020-05-05 21:53:04 -06:00
|
|
|
where
|
|
|
|
EG: EntityGateway
|
|
|
|
{
|
2022-09-18 21:01:32 -06:00
|
|
|
let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
2022-10-18 04:46:21 -06:00
|
|
|
let monster = rooms.with(room_id, |room| Box::pin(async move {
|
|
|
|
room.maps.enemy_by_id(request_item.enemy_id as usize)
|
|
|
|
})).await??;
|
2020-04-26 22:01:05 -06:00
|
|
|
|
|
|
|
if monster.dropped_item {
|
2020-11-16 21:27:03 -07:00
|
|
|
return Err(ShipError::MonsterAlreadyDroppedItem(id, request_item.enemy_id).into())
|
2020-04-26 22:01:05 -06:00
|
|
|
}
|
|
|
|
|
2022-09-18 21:01:32 -06:00
|
|
|
let clients_in_area = client_location.get_clients_in_room(room_id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
2022-10-18 04:46:21 -06:00
|
|
|
let client_and_drop = rooms.with_mut(room_id, |room| Box::pin(async move {
|
|
|
|
clients_in_area.into_iter()
|
|
|
|
.filter_map(move |area_client| {
|
|
|
|
room.drop_table.get_drop(&monster.map_area, &monster.monster).map(|item_drop_type| {
|
|
|
|
(area_client, item_drop_type)
|
|
|
|
})
|
2020-06-02 18:51:18 -06:00
|
|
|
})
|
2022-10-18 04:46:21 -06:00
|
|
|
.collect::<Vec<_>>()
|
|
|
|
})).await?;
|
|
|
|
|
2020-06-02 18:51:18 -06:00
|
|
|
let mut item_drop_packets = Vec::new();
|
|
|
|
for (area_client, item_drop) in client_and_drop {
|
|
|
|
let item_drop = ItemDrop {
|
|
|
|
map_area: monster.map_area,
|
|
|
|
x: request_item.x,
|
|
|
|
y: request_item.y,
|
|
|
|
z: request_item.z,
|
|
|
|
item: item_drop,
|
|
|
|
};
|
2022-10-18 04:46:21 -06:00
|
|
|
let character_id = clients.with(id, |client| Box::pin(async move {
|
|
|
|
client.character.id
|
|
|
|
})).await?;
|
|
|
|
|
|
|
|
let floor_item = enemy_drops_item(item_state, entity_gateway, character_id, item_drop).await?;
|
2022-07-18 20:51:35 -06:00
|
|
|
let item_drop_msg = builder::message::item_drop(request_item.client, request_item.target, &floor_item)?;
|
2020-06-02 18:51:18 -06:00
|
|
|
|
|
|
|
item_drop_packets.push((area_client.client, SendShipPacket::Message(Message::new(GameMessage::ItemDrop(item_drop_msg)))));
|
|
|
|
}
|
2020-04-26 22:01:05 -06:00
|
|
|
|
2022-10-18 04:46:21 -06:00
|
|
|
Ok(item_drop_packets)
|
2020-04-26 22:01:05 -06:00
|
|
|
}
|
2020-05-05 21:53:04 -06:00
|
|
|
|
2020-06-02 18:51:18 -06:00
|
|
|
pub async fn pickup_item<EG>(id: ClientId,
|
2022-10-18 04:46:21 -06:00
|
|
|
pickup_item: PickupItem,
|
|
|
|
entity_gateway: &mut EG,
|
|
|
|
client_location: &ClientLocation,
|
|
|
|
clients: &Clients,
|
|
|
|
item_state: &mut ItemState)
|
|
|
|
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
2020-05-05 21:53:04 -06:00
|
|
|
where
|
2022-10-18 04:46:21 -06:00
|
|
|
EG: EntityGateway + Clone + 'static,
|
2020-05-05 21:53:04 -06:00
|
|
|
{
|
2022-09-18 21:01:32 -06:00
|
|
|
let area_client = client_location.get_local_client(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
|
|
|
let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
|
|
|
let clients_in_area = client_location.get_clients_in_room(room_id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
2020-07-19 14:14:07 -06:00
|
|
|
|
2022-10-18 04:46:21 -06:00
|
|
|
clients.with(id, |client| {
|
|
|
|
let mut entity_gateway = entity_gateway.clone();
|
|
|
|
let mut item_state = item_state.clone();
|
|
|
|
Box::pin(async move {
|
|
|
|
let (item, floor_type) = item_state.get_floor_item(&client.character.id, &ClientItemId(pickup_item.item_id)).await?;
|
|
|
|
let remove_item = builder::message::remove_item_from_floor(area_client, &item)?;
|
|
|
|
let create_item = match &item.item {
|
|
|
|
FloorItemDetail::Individual(individual_floor_item) => Some(builder::message::create_individual_item(area_client, item.item_id, individual_floor_item)?),
|
|
|
|
FloorItemDetail::Stacked(stacked_floor_item) => Some(builder::message::create_stacked_item(area_client, item.item_id, &stacked_floor_item.tool, stacked_floor_item.count())?),
|
|
|
|
FloorItemDetail::Meseta(_) => None,
|
|
|
|
};
|
2022-05-01 21:59:34 -06:00
|
|
|
|
2022-10-18 04:46:21 -06:00
|
|
|
match pick_up_item(&mut item_state, &mut entity_gateway, &client.character, &ClientItemId(pickup_item.item_id)).await {
|
|
|
|
Ok(trigger_create_item) => {
|
|
|
|
let remove_packets: Box<dyn Iterator<Item=(ClientId, SendShipPacket)> + Send> = match floor_type {
|
|
|
|
FloorType::Local => {
|
|
|
|
Box::new(vec![(id, SendShipPacket::Message(Message::new(GameMessage::RemoveItemFromFloor(remove_item.clone()))))].into_iter())
|
|
|
|
},
|
|
|
|
FloorType::Shared => {
|
|
|
|
Box::new(clients_in_area.clone().into_iter()
|
|
|
|
.map(move |c| {
|
|
|
|
(c.client, SendShipPacket::Message(Message::new(GameMessage::RemoveItemFromFloor(remove_item.clone()))))
|
|
|
|
}))
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(remove_packets
|
|
|
|
.chain(clients_in_area.into_iter()
|
|
|
|
.filter_map(move |c| {
|
|
|
|
match trigger_create_item {
|
|
|
|
TriggerCreateItem::Yes => create_item.clone().map(|ci| (c.client, SendShipPacket::Message(Message::new(GameMessage::CreateItem(ci))))),
|
|
|
|
_ => None
|
|
|
|
}
|
|
|
|
}))
|
|
|
|
.collect())
|
2020-11-13 19:04:09 -07:00
|
|
|
},
|
2022-10-18 04:46:21 -06:00
|
|
|
Err(err) => {
|
|
|
|
warn!("character {:?} could not pick up item: {:?}", client.character.id, err);
|
|
|
|
Ok(Vec::new())
|
2020-11-13 19:04:09 -07:00
|
|
|
},
|
2022-10-18 04:46:21 -06:00
|
|
|
}
|
|
|
|
})}).await?
|
2020-05-05 21:53:04 -06:00
|
|
|
}
|
|
|
|
|
2020-06-02 18:51:18 -06:00
|
|
|
pub async fn request_box_item<EG>(id: ClientId,
|
2022-10-18 04:46:21 -06:00
|
|
|
box_drop_request: BoxDropRequest,
|
|
|
|
entity_gateway: &mut EG,
|
2022-09-18 21:01:32 -06:00
|
|
|
client_location: &ClientLocation,
|
2022-10-18 04:46:21 -06:00
|
|
|
clients: &Clients,
|
|
|
|
rooms: &Rooms,
|
2022-09-18 21:01:32 -06:00
|
|
|
item_state: &mut ItemState)
|
2022-10-18 04:46:21 -06:00
|
|
|
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
2020-05-15 13:37:44 -03:00
|
|
|
where
|
2022-10-18 04:46:21 -06:00
|
|
|
EG: EntityGateway + Clone + 'static
|
2020-05-15 13:37:44 -03:00
|
|
|
{
|
2022-09-18 21:01:32 -06:00
|
|
|
let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
2022-10-18 04:46:21 -06:00
|
|
|
let box_object = rooms.with(room_id, |room| Box::pin(async move {
|
|
|
|
room.maps.object_by_id(box_drop_request.object_id as usize)
|
|
|
|
})).await??;
|
2020-05-15 13:37:44 -03:00
|
|
|
|
|
|
|
if box_object.dropped_item {
|
2020-11-16 21:27:03 -07:00
|
|
|
return Err(ShipError::BoxAlreadyDroppedItem(id, box_drop_request.object_id).into())
|
2020-05-15 13:37:44 -03:00
|
|
|
}
|
|
|
|
|
2022-09-18 21:01:32 -06:00
|
|
|
let clients_in_area = client_location.get_clients_in_room(room_id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
2022-10-18 04:46:21 -06:00
|
|
|
|
|
|
|
let client_and_drop = rooms.with_mut(room_id, |room| Box::pin(async move {
|
|
|
|
clients_in_area.into_iter()
|
|
|
|
.filter_map(move |area_client| {
|
|
|
|
room.drop_table.get_box_drop(&box_object.map, &box_object).map(|item_drop_type| {
|
|
|
|
(area_client, item_drop_type)
|
|
|
|
})
|
2020-06-02 18:51:18 -06:00
|
|
|
})
|
2022-10-18 04:46:21 -06:00
|
|
|
.collect::<Vec<_>>()
|
|
|
|
})).await?;
|
2020-06-02 18:51:18 -06:00
|
|
|
|
|
|
|
let mut item_drop_packets = Vec::new();
|
|
|
|
for (area_client, item_drop) in client_and_drop {
|
|
|
|
let item_drop = ItemDrop {
|
|
|
|
map_area: box_object.map,
|
|
|
|
x: box_drop_request.x,
|
|
|
|
y: 0.0,
|
|
|
|
z: box_drop_request.z,
|
|
|
|
item: item_drop,
|
|
|
|
};
|
2022-10-18 04:46:21 -06:00
|
|
|
//let client = clients.get_mut(&area_client.client).ok_or(ShipError::ClientNotFound(area_client.client))?;
|
|
|
|
let character_id = clients.with(area_client.client, |client| Box::pin(async move {
|
|
|
|
client.character.id
|
|
|
|
})).await?;
|
|
|
|
let floor_item = enemy_drops_item(item_state, entity_gateway, character_id, item_drop).await?;
|
|
|
|
//let floor_item = enemy_drops_item(item_state, &mut entity_gateway, client.character.id, item_drop).await?;
|
2022-07-18 20:51:35 -06:00
|
|
|
let item_drop_msg = builder::message::item_drop(box_drop_request.client, box_drop_request.target, &floor_item)?;
|
2020-06-02 18:51:18 -06:00
|
|
|
item_drop_packets.push((area_client.client, SendShipPacket::Message(Message::new(GameMessage::ItemDrop(item_drop_msg)))))
|
|
|
|
}
|
2020-05-15 13:37:44 -03:00
|
|
|
|
2022-10-18 04:46:21 -06:00
|
|
|
Ok(item_drop_packets)
|
2020-05-30 10:30:39 -06:00
|
|
|
}
|
2020-07-19 14:14:07 -06:00
|
|
|
|
|
|
|
|
|
|
|
pub async fn send_bank_list(id: ClientId,
|
|
|
|
clients: &Clients,
|
2022-05-14 13:06:40 -06:00
|
|
|
item_state: &mut ItemState)
|
2022-10-18 04:46:21 -06:00
|
|
|
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
2020-07-19 14:14:07 -06:00
|
|
|
{
|
2022-10-18 04:46:21 -06:00
|
|
|
let bank = clients.with(id, |client| {
|
|
|
|
let item_state = item_state.clone();
|
|
|
|
Box::pin(async move {
|
|
|
|
item_state.get_character_bank(&client.character).await
|
|
|
|
})
|
|
|
|
}).await??;
|
|
|
|
let bank_items_pkt = builder::message::bank_item_list(&bank);
|
|
|
|
Ok(vec![(id, SendShipPacket::BankItemList(bank_items_pkt))])
|
2020-07-19 14:14:07 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn bank_interaction<EG>(id: ClientId,
|
2022-10-18 04:46:21 -06:00
|
|
|
bank_interaction: BankInteraction,
|
|
|
|
entity_gateway: &mut EG,
|
2020-07-23 17:18:20 -06:00
|
|
|
client_location: &ClientLocation,
|
2022-10-18 04:46:21 -06:00
|
|
|
clients: &Clients,
|
2022-05-14 13:06:40 -06:00
|
|
|
item_state: &mut ItemState)
|
2022-10-18 04:46:21 -06:00
|
|
|
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
2020-07-19 14:14:07 -06:00
|
|
|
where
|
2022-10-18 04:46:21 -06:00
|
|
|
EG: EntityGateway + Clone + 'static,
|
2020-07-19 14:14:07 -06:00
|
|
|
{
|
2022-09-18 21:01:32 -06:00
|
|
|
let area_client = client_location.get_local_client(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
|
|
|
let other_clients_in_area = client_location.get_all_clients_by_client(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
2022-10-18 04:46:21 -06:00
|
|
|
|
|
|
|
let bank_action_pkts = clients.with(id, |client| {
|
|
|
|
let mut entity_gateway = entity_gateway.clone();
|
|
|
|
let mut item_state = item_state.clone();
|
|
|
|
Box::pin(async move {
|
|
|
|
Ok::<_, ShipError>(match bank_interaction.action {
|
|
|
|
BANK_ACTION_DEPOSIT => {
|
|
|
|
if bank_interaction.item_id == 0xFFFFFFFF {
|
|
|
|
deposit_meseta(&mut item_state, &mut entity_gateway, &client.character, bank_interaction.meseta_amount).await?;
|
|
|
|
Vec::new()
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
deposit_item(&mut item_state, &mut entity_gateway, &client.character, &ClientItemId(bank_interaction.item_id), bank_interaction.item_amount as u32).await?;
|
|
|
|
let player_no_longer_has_item = builder::message::player_no_longer_has_item(area_client, ClientItemId(bank_interaction.item_id), bank_interaction.item_amount as u32);
|
|
|
|
vec![SendShipPacket::Message(Message::new(GameMessage::PlayerNoLongerHasItem(player_no_longer_has_item)))]
|
|
|
|
}
|
|
|
|
},
|
|
|
|
BANK_ACTION_WITHDRAW => {
|
|
|
|
if bank_interaction.item_id == 0xFFFFFFFF {
|
|
|
|
withdraw_meseta(&mut item_state, &mut entity_gateway, &client.character, bank_interaction.meseta_amount).await?;
|
|
|
|
Vec::new()
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
let item_added_to_inventory = withdraw_item(&mut item_state, &mut entity_gateway, &client.character, &ClientItemId(bank_interaction.item_id), bank_interaction.item_amount as u32).await?;
|
|
|
|
let item_created = builder::message::create_withdrawn_inventory_item2(area_client, &item_added_to_inventory)?;
|
|
|
|
vec![SendShipPacket::Message(Message::new(GameMessage::CreateItem(item_created)))]
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => { // TODO: error?
|
|
|
|
Vec::new()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}).await??;
|
|
|
|
|
|
|
|
Ok(other_clients_in_area.into_iter()
|
|
|
|
.flat_map(move |c| {
|
|
|
|
bank_action_pkts.clone().into_iter()
|
|
|
|
.map(move |pkt| {
|
|
|
|
(c.client, pkt)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
)
|
2020-07-19 14:14:07 -06:00
|
|
|
}
|
2020-09-27 18:16:27 -06:00
|
|
|
|
|
|
|
pub async fn shop_request(id: ClientId,
|
2022-10-18 04:46:21 -06:00
|
|
|
shop_request: ShopRequest,
|
2020-09-27 18:16:27 -06:00
|
|
|
client_location: &ClientLocation,
|
2022-10-18 04:46:21 -06:00
|
|
|
clients: &Clients,
|
2020-09-27 18:16:27 -06:00
|
|
|
rooms: &Rooms,
|
2022-10-18 04:46:21 -06:00
|
|
|
shops: &ItemShops)
|
|
|
|
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
2020-09-27 18:16:27 -06:00
|
|
|
{
|
2022-10-18 04:46:21 -06:00
|
|
|
//let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
2022-09-18 21:01:32 -06:00
|
|
|
let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
2022-10-18 04:46:21 -06:00
|
|
|
/*
|
2020-09-27 18:16:27 -06:00
|
|
|
let room = rooms.get(room_id.0)
|
2021-12-29 15:46:22 -07:00
|
|
|
.ok_or(ShipError::InvalidRoom(room_id.0 as u32))?
|
2020-09-27 18:16:27 -06:00
|
|
|
.as_ref()
|
2021-12-29 15:46:22 -07:00
|
|
|
.ok_or(ShipError::InvalidRoom(room_id.0 as u32))?;
|
2022-10-18 04:46:21 -06:00
|
|
|
*/
|
|
|
|
let difficulty = rooms.with(room_id, |room| Box::pin(async move {
|
|
|
|
room.mode.difficulty()
|
|
|
|
})).await?;
|
|
|
|
let shop_list = clients.with_mut(id, |client| {
|
|
|
|
let mut shops = shops.clone();
|
|
|
|
Box::pin(async move {
|
|
|
|
let level = LEVEL_TABLE.get_level_from_exp(client.character.char_class, client.character.exp) as usize;
|
|
|
|
match shop_request.shop_type {
|
|
|
|
SHOP_OPTION_WEAPON => {
|
|
|
|
client.weapon_shop = shops.weapon_shop.get_mut(&(difficulty, client.character.section_id))
|
|
|
|
.ok_or(ShipError::ShopError)?
|
|
|
|
.lock()
|
|
|
|
.await
|
|
|
|
.generate_weapon_list(level);
|
|
|
|
Ok(builder::message::shop_list(shop_request.shop_type, &client.weapon_shop))
|
|
|
|
},
|
|
|
|
SHOP_OPTION_TOOL => {
|
|
|
|
client.tool_shop = shops.tool_shop
|
|
|
|
.lock()
|
|
|
|
.await
|
|
|
|
.generate_tool_list(level);
|
|
|
|
Ok(builder::message::shop_list(shop_request.shop_type, &client.tool_shop))
|
|
|
|
},
|
|
|
|
SHOP_OPTION_ARMOR => {
|
|
|
|
client.armor_shop = shops.armor_shop
|
|
|
|
.lock()
|
|
|
|
.await
|
|
|
|
.generate_armor_list(level);
|
|
|
|
Ok(builder::message::shop_list(shop_request.shop_type, &client.armor_shop))
|
|
|
|
},
|
|
|
|
_ => {
|
|
|
|
Err(ShipError::ShopError)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})}).await??;
|
|
|
|
/*
|
2020-09-27 18:16:27 -06:00
|
|
|
let shop_list = match shop_request.shop_type {
|
|
|
|
SHOP_OPTION_WEAPON => {
|
|
|
|
client.weapon_shop = shops.weapon_shop.get_mut(&(room.mode.difficulty(), client.character.section_id))
|
|
|
|
.ok_or(ShipError::ShopError)?
|
|
|
|
.generate_weapon_list(level);
|
|
|
|
builder::message::shop_list(shop_request.shop_type, &client.weapon_shop)
|
|
|
|
},
|
|
|
|
SHOP_OPTION_TOOL => {
|
|
|
|
client.tool_shop = shops.tool_shop.generate_tool_list(level);
|
|
|
|
builder::message::shop_list(shop_request.shop_type, &client.tool_shop)
|
|
|
|
},
|
|
|
|
SHOP_OPTION_ARMOR => {
|
|
|
|
client.armor_shop = shops.armor_shop.generate_armor_list(level);
|
|
|
|
builder::message::shop_list(shop_request.shop_type, &client.armor_shop)
|
|
|
|
},
|
|
|
|
_ => {
|
2020-11-16 21:27:03 -07:00
|
|
|
return Err(ShipError::ShopError.into())
|
2020-09-27 18:16:27 -06:00
|
|
|
}
|
|
|
|
};
|
2022-10-18 04:46:21 -06:00
|
|
|
*/
|
2020-09-27 18:16:27 -06:00
|
|
|
|
2022-10-18 04:46:21 -06:00
|
|
|
Ok(vec![(id, SendShipPacket::Message(Message::new(GameMessage::ShopList(shop_list))))])
|
2020-09-27 18:16:27 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pub async fn buy_item<EG>(id: ClientId,
|
2022-10-18 04:46:21 -06:00
|
|
|
buy_item: BuyItem,
|
|
|
|
entity_gateway: &mut EG,
|
2020-09-27 18:16:27 -06:00
|
|
|
client_location: &ClientLocation,
|
2022-10-18 04:46:21 -06:00
|
|
|
clients: &Clients,
|
2022-06-21 00:09:52 -06:00
|
|
|
item_state: &mut ItemState)
|
2022-10-18 04:46:21 -06:00
|
|
|
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
2020-09-27 18:16:27 -06:00
|
|
|
where
|
2022-10-18 04:46:21 -06:00
|
|
|
EG: EntityGateway + Clone + 'static,
|
2020-09-27 18:16:27 -06:00
|
|
|
{
|
2022-09-18 21:01:32 -06:00
|
|
|
let area_client = client_location.get_local_client(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
2020-09-27 18:16:27 -06:00
|
|
|
|
2022-10-18 04:46:21 -06:00
|
|
|
let create = clients.with_mut(id, |client| {
|
|
|
|
let mut entity_gateway = entity_gateway.clone();
|
|
|
|
let mut item_state = item_state.clone();
|
|
|
|
Box::pin(async move {
|
|
|
|
let (item, remove): (&(dyn ShopItem + Send + Sync), bool) = match buy_item.shop_type {
|
|
|
|
SHOP_OPTION_WEAPON => {
|
|
|
|
(client.weapon_shop.get(buy_item.shop_index as usize).ok_or(ShipError::ShopError)?, false)
|
|
|
|
},
|
|
|
|
SHOP_OPTION_TOOL => {
|
|
|
|
let item = client.tool_shop.get(buy_item.shop_index as usize).ok_or(ShipError::ShopError)?;
|
|
|
|
let remove = matches!(item, ToolShopItem::Tech(_));
|
|
|
|
(item, remove)
|
|
|
|
},
|
|
|
|
SHOP_OPTION_ARMOR => {
|
|
|
|
let item = client.armor_shop.get(buy_item.shop_index as usize).ok_or(ShipError::ShopError)?;
|
|
|
|
let remove = matches!(item, ArmorShopItem::Unit(_));
|
|
|
|
(item, remove)
|
|
|
|
},
|
|
|
|
_ => {
|
|
|
|
return Err(ShipError::ShopError.into())
|
|
|
|
}
|
|
|
|
};
|
2020-09-27 18:16:27 -06:00
|
|
|
|
2022-10-18 04:46:21 -06:00
|
|
|
let inventory_item = buy_shop_item(&mut item_state, &mut entity_gateway, &client.character, item, ClientItemId(buy_item.item_id), buy_item.amount as u32).await?;
|
|
|
|
|
|
|
|
if remove {
|
|
|
|
match buy_item.shop_type {
|
|
|
|
SHOP_OPTION_TOOL => {
|
|
|
|
client.tool_shop.remove(buy_item.shop_index as usize);
|
|
|
|
},
|
|
|
|
SHOP_OPTION_ARMOR => {
|
|
|
|
client.armor_shop.remove(buy_item.shop_index as usize);
|
|
|
|
},
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
builder::message::create_withdrawn_inventory_item(area_client, &inventory_item)
|
|
|
|
})}).await??;
|
2020-09-27 20:30:37 -06:00
|
|
|
|
2022-09-18 21:01:32 -06:00
|
|
|
let other_clients_in_area = client_location.get_client_neighbors(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
2022-10-18 04:46:21 -06:00
|
|
|
Ok(other_clients_in_area.into_iter()
|
|
|
|
.map(move |c| {
|
|
|
|
(c.client, SendShipPacket::Message(Message::new(GameMessage::CreateItem(create.clone()))))
|
|
|
|
})
|
|
|
|
.collect())
|
2020-09-27 18:16:27 -06:00
|
|
|
}
|
2020-12-03 15:04:48 -07:00
|
|
|
|
|
|
|
|
|
|
|
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,
|
2022-10-18 04:46:21 -06:00
|
|
|
tek_request: TekRequest,
|
|
|
|
entity_gateway: &mut EG,
|
|
|
|
clients: &Clients,
|
2022-07-18 23:57:54 -06:00
|
|
|
item_state: &mut ItemState)
|
2022-10-18 04:46:21 -06:00
|
|
|
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
2020-12-03 15:04:48 -07:00
|
|
|
where
|
2022-10-18 04:46:21 -06:00
|
|
|
EG: EntityGateway + Clone + 'static,
|
2020-12-03 15:04:48 -07:00
|
|
|
{
|
2022-10-18 04:46:21 -06:00
|
|
|
//let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
2020-12-03 15:04:48 -07:00
|
|
|
|
2022-09-18 21:01:32 -06:00
|
|
|
// TODO: secids have different mod rates
|
2020-12-03 15:04:48 -07:00
|
|
|
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)
|
|
|
|
};
|
|
|
|
|
2022-10-18 04:46:21 -06:00
|
|
|
let preview_pkt = clients.with_mut(id, |client| {
|
|
|
|
let mut entity_gateway = entity_gateway.clone();
|
|
|
|
let mut item_state = item_state.clone();
|
|
|
|
Box::pin(async move {
|
|
|
|
client.tek = Some((ClientItemId(tek_request.item_id), special_mod, percent_mod, grind_mod));
|
2020-12-03 15:04:48 -07:00
|
|
|
|
2022-10-18 04:46:21 -06:00
|
|
|
let inventory = item_state.get_character_inventory(&client.character).await?;
|
|
|
|
let item = inventory.get_by_client_id(&ClientItemId(tek_request.item_id))
|
|
|
|
.ok_or(ItemStateError::WrongItemType(ClientItemId(tek_request.item_id)))?;
|
|
|
|
let mut weapon = *item.item.as_individual()
|
|
|
|
.ok_or(ItemStateError::WrongItemType(ClientItemId(tek_request.item_id)))?
|
|
|
|
.as_weapon()
|
|
|
|
.ok_or(ItemStateError::WrongItemType(ClientItemId(tek_request.item_id)))?;
|
2020-12-03 15:04:48 -07:00
|
|
|
|
2022-10-18 04:46:21 -06:00
|
|
|
weapon.apply_modifier(&item::weapon::WeaponModifier::Tekked {
|
|
|
|
special: special_mod,
|
|
|
|
percent: percent_mod,
|
|
|
|
grind: grind_mod,
|
|
|
|
});
|
2020-12-03 15:04:48 -07:00
|
|
|
|
2022-10-18 04:46:21 -06:00
|
|
|
take_meseta(&mut item_state, &mut entity_gateway, &client.character.id, item::Meseta(100)).await?;
|
|
|
|
builder::message::tek_preview(ClientItemId(tek_request.item_id), &weapon)
|
|
|
|
})}).await??;
|
2020-12-03 15:04:48 -07:00
|
|
|
|
|
|
|
|
2022-10-18 04:46:21 -06:00
|
|
|
Ok(vec![(id, SendShipPacket::Message(Message::new(GameMessage::TekPreview(preview_pkt))))])
|
2020-12-03 15:04:48 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn accept_tek_item<EG>(id: ClientId,
|
2022-10-18 04:46:21 -06:00
|
|
|
tek_accept: TekAccept,
|
|
|
|
entity_gateway: &mut EG,
|
2020-12-03 15:04:48 -07:00
|
|
|
client_location: &ClientLocation,
|
2022-10-18 04:46:21 -06:00
|
|
|
clients: &Clients,
|
2022-07-18 23:57:54 -06:00
|
|
|
item_state: &mut ItemState)
|
2022-10-18 04:46:21 -06:00
|
|
|
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
2020-12-03 15:04:48 -07:00
|
|
|
where
|
2022-10-18 04:46:21 -06:00
|
|
|
EG: EntityGateway + Clone + 'static,
|
2020-12-03 15:04:48 -07:00
|
|
|
{
|
2022-09-18 21:01:32 -06:00
|
|
|
let area_client = client_location.get_local_client(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
2022-10-18 04:46:21 -06:00
|
|
|
let neighbors = client_location.get_client_neighbors(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
|
|
|
|
|
|
|
clients.with(id, |client| {
|
|
|
|
let mut entity_gateway = entity_gateway.clone();
|
|
|
|
let mut item_state = item_state.clone();
|
|
|
|
Box::pin(async move {
|
|
|
|
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());
|
|
|
|
}
|
2020-12-03 15:04:48 -07:00
|
|
|
|
2022-10-18 04:46:21 -06:00
|
|
|
let modifier = item::weapon::WeaponModifier::Tekked {
|
|
|
|
special: special_mod,
|
|
|
|
percent: percent_mod,
|
|
|
|
grind: grind_mod,
|
|
|
|
};
|
|
|
|
let weapon = apply_modifier(&mut item_state, &mut entity_gateway, &client.character, item_id, item::ItemModifier::WeaponModifier(modifier)).await?;
|
2020-12-03 15:04:48 -07:00
|
|
|
|
2022-10-18 04:46:21 -06:00
|
|
|
let create_item_pkt = builder::message::create_individual_item(area_client, item_id, &weapon)?;
|
2020-12-03 15:04:48 -07:00
|
|
|
|
2022-10-18 04:46:21 -06:00
|
|
|
Ok(neighbors.into_iter()
|
|
|
|
.map(move |c| {
|
|
|
|
(c.client, SendShipPacket::Message(Message::new(GameMessage::CreateItem(create_item_pkt.clone()))))
|
|
|
|
})
|
|
|
|
.collect())
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
Err(MessageError::InvalidTek(ClientItemId(tek_accept.item_id)).into())
|
|
|
|
}
|
|
|
|
})}).await?
|
2020-12-03 15:04:48 -07:00
|
|
|
}
|