Merge pull request 'drop partial stacks of items/meseta' (#148) from drop_split_item into master
This commit is contained in:
commit
a8892467f6
@ -7,6 +7,7 @@ use crate::entity::item::{ItemEntityId, ItemEntity, ItemDetail, ItemLocation};
|
||||
use crate::entity::item::{Meseta, NewItemEntity};
|
||||
use crate::entity::item::tool::Tool;
|
||||
use crate::ship::map::MapArea;
|
||||
use crate::ship::ship::ItemDropLocation;
|
||||
use crate::ship::drops::{ItemDrop, ItemDropType};
|
||||
use crate::ship::location::{AreaClient, RoomId};
|
||||
|
||||
@ -145,6 +146,8 @@ pub enum ItemManagerError {
|
||||
CouldNotAddToInventory(FloorItem),
|
||||
//ItemBelongsToOtherPlayer,
|
||||
Idunnoman,
|
||||
CouldNotSplitItem(InventoryItem),
|
||||
CouldNotDropMeseta,
|
||||
}
|
||||
|
||||
pub struct ItemManager {
|
||||
@ -152,11 +155,10 @@ pub struct ItemManager {
|
||||
|
||||
character_inventory: HashMap<CharacterEntityId, Vec<InventoryItem>>,
|
||||
character_floor: HashMap<CharacterEntityId, Vec<FloorItem>>,
|
||||
character_item_id_counter: HashMap<CharacterEntityId, u32>,
|
||||
|
||||
character_room: HashMap<CharacterEntityId, RoomId>,
|
||||
room_floor: HashMap<RoomId, Vec<FloorItem>>,
|
||||
room_item_id_counter: HashMap<RoomId, u32>,
|
||||
room_item_id_counter: HashMap<RoomId, Box<dyn FnMut() -> ClientItemId + Send>>,
|
||||
}
|
||||
|
||||
impl ItemManager {
|
||||
@ -165,7 +167,6 @@ impl ItemManager {
|
||||
id_counter: 0,
|
||||
character_inventory: HashMap::new(),
|
||||
character_floor: HashMap::new(),
|
||||
character_item_id_counter: HashMap::new(),
|
||||
character_room: HashMap::new(),
|
||||
room_floor: HashMap::new(),
|
||||
room_item_id_counter: HashMap::new(),
|
||||
@ -177,12 +178,6 @@ impl ItemManager {
|
||||
ClientItemId(self.id_counter)
|
||||
}
|
||||
|
||||
pub fn next_drop_item_id(&mut self, room_id: RoomId) -> ClientItemId {
|
||||
let next_id = self.room_item_id_counter.entry(room_id).or_insert(0xF0000000);
|
||||
*next_id += 1;
|
||||
ClientItemId(*next_id)
|
||||
}
|
||||
|
||||
// TODO: Result
|
||||
pub fn load_character<EG: EntityGateway>(&mut self, entity_gateway: &mut EG, character: &CharacterEntity) {
|
||||
let items = entity_gateway.get_items_by_character(&character);
|
||||
@ -234,7 +229,12 @@ impl ItemManager {
|
||||
self.character_room.insert(character.id, room_id);
|
||||
self.character_floor.insert(character.id, Vec::new());
|
||||
self.room_floor.entry(room_id).or_insert(Vec::new());
|
||||
self.character_item_id_counter.insert(character.id, base_id + inventory.len() as u32);
|
||||
|
||||
let mut inc = 0xF0000000;
|
||||
self.room_item_id_counter.entry(room_id).or_insert(Box::new(move || {
|
||||
inc += 1;
|
||||
ClientItemId(inc)
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn get_character_inventory(&self, character: &CharacterEntity) -> Result<CharacterInventory, ItemManagerError> {
|
||||
@ -406,8 +406,8 @@ impl ItemManager {
|
||||
FloorItemType::Meseta(m) => ActiveItemEntityId::Meseta(m.clone()),
|
||||
};
|
||||
|
||||
let room_id = *self.character_room.get(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
|
||||
let item_id = self.next_drop_item_id(room_id);
|
||||
let room_id = self.character_room.get(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
|
||||
let item_id = self.room_item_id_counter.get_mut(room_id).ok_or(ItemManagerError::NoCharacter(character.id))?();
|
||||
|
||||
let floor_item = FloorItem {
|
||||
entity_id: entity_id,
|
||||
@ -488,4 +488,88 @@ impl ItemManager {
|
||||
shared_floor.push(room_floor_item);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn player_drops_meseta_on_shared_floor<EG: EntityGateway>(&mut self,
|
||||
entity_gateway: &mut EG,
|
||||
character: &mut CharacterEntity,
|
||||
drop_location: ItemDropLocation,
|
||||
amount: u32)
|
||||
-> Result<FloorItem, ItemManagerError> {
|
||||
let room_id = self.character_room.get(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
|
||||
let shared_floor = self.room_floor.get_mut(&room_id).ok_or(ItemManagerError::NoCharacter(character.id))?;
|
||||
if character.meseta <= amount {
|
||||
return Err(ItemManagerError::CouldNotDropMeseta)
|
||||
}
|
||||
character.meseta -= amount;
|
||||
entity_gateway.save_character(&character);
|
||||
|
||||
let item_id = self.room_item_id_counter.get_mut(room_id).ok_or(ItemManagerError::NoCharacter(character.id))?();
|
||||
let floor_item = FloorItem {
|
||||
entity_id: ActiveItemEntityId::Meseta(Meseta(amount)),
|
||||
item_id: item_id,
|
||||
item: FloorItemType::Meseta(Meseta(amount)),
|
||||
map_area: drop_location.map_area,
|
||||
x: drop_location.x,
|
||||
y: 0.0,
|
||||
z: drop_location.z,
|
||||
};
|
||||
|
||||
shared_floor.push(floor_item.clone());
|
||||
Ok(floor_item)
|
||||
}
|
||||
|
||||
pub fn player_drops_partial_stack_on_shared_floor<EG: EntityGateway>(&mut self,
|
||||
entity_gateway: &mut EG,
|
||||
character: &CharacterEntity,
|
||||
inventory_item: InventoryItem,
|
||||
drop_location: ItemDropLocation,
|
||||
amount: usize)
|
||||
-> Result<FloorItem, ItemManagerError> {
|
||||
let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
|
||||
let room_id = self.character_room.get(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
|
||||
let shared_floor = self.room_floor.get_mut(&room_id).ok_or(ItemManagerError::NoCharacter(character.id))?;
|
||||
|
||||
let item_to_split = inventory.iter_mut()
|
||||
.find(|i| i.item_id == inventory_item.item_id)
|
||||
.ok_or(ItemManagerError::NoSuchItemId(inventory_item.item_id))?;
|
||||
|
||||
if let (ActiveItemEntityId::Stacked(ref mut entity_ids), HeldItemType::Stacked(tool, ref mut tool_amount)) = (&mut item_to_split.entity_id, &mut item_to_split.item) {
|
||||
if entity_ids.len() <= amount || *tool_amount <= amount {
|
||||
return Err(ItemManagerError::CouldNotSplitItem(inventory_item));
|
||||
}
|
||||
|
||||
*tool_amount -= amount;
|
||||
|
||||
let dropped_entities = entity_ids.drain(..amount).collect::<Vec<_>>();
|
||||
|
||||
dropped_entities.iter().for_each(|entity_id| {
|
||||
entity_gateway.save_item(&ItemEntity {
|
||||
id: *entity_id,
|
||||
item: ItemDetail::Tool(*tool),
|
||||
location: ItemLocation::SharedFloor {
|
||||
map_area: drop_location.map_area,
|
||||
x: drop_location.x,
|
||||
y: 0.0,
|
||||
z: drop_location.z,
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
let item_id = self.room_item_id_counter.get_mut(room_id).ok_or(ItemManagerError::NoCharacter(character.id))?();
|
||||
let floor_item = FloorItem {
|
||||
entity_id: ActiveItemEntityId::Stacked(dropped_entities),
|
||||
item_id: item_id,
|
||||
item: FloorItemType::Stacked(*tool, amount),
|
||||
map_area: drop_location.map_area,
|
||||
x: drop_location.x,
|
||||
y: 0.0,
|
||||
z: drop_location.z,
|
||||
};
|
||||
shared_floor.push(floor_item.clone());
|
||||
Ok(floor_item)
|
||||
}
|
||||
else {
|
||||
Err(ItemManagerError::CouldNotSplitItem(inventory_item))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -46,3 +46,20 @@ pub fn remove_item_from_floor(area_client: AreaClient, item: &FloorItem) -> Resu
|
||||
item_id: item.item_id.0,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn drop_split_stack(area_client: AreaClient, item: &FloorItem) -> Result<DropSplitStack, ShipError> {
|
||||
let item_bytes = item.item.as_client_bytes();
|
||||
Ok(DropSplitStack {
|
||||
client: area_client.local_client.id(),
|
||||
target: 0,
|
||||
variety: 0,
|
||||
unknown1: 0,
|
||||
map_area: item.map_area.area_value(),
|
||||
x: item.x,
|
||||
z: item.z,
|
||||
item_bytes: item_bytes[0..12].try_into()?,
|
||||
item_id: item.item_id.0,
|
||||
item_bytes2: item_bytes[12..16].try_into()?,
|
||||
unknown2: 0,
|
||||
})
|
||||
}
|
||||
|
@ -3,10 +3,11 @@ use libpso::packet::ship::*;
|
||||
use libpso::packet::messages::*;
|
||||
use crate::entity::gateway::EntityGateway;
|
||||
use crate::common::serverstate::ClientId;
|
||||
use crate::ship::ship::{SendShipPacket, ShipError, Rooms, Clients};
|
||||
use crate::ship::ship::{SendShipPacket, ShipError, Rooms, Clients, ItemDropLocation};
|
||||
use crate::ship::location::{ClientLocation, ClientLocationError, RoomLobby};
|
||||
use crate::ship::map::{MapArea};
|
||||
use crate::ship::items::{ItemManager, ClientItemId};
|
||||
use crate::ship::packet::builder;
|
||||
|
||||
pub fn request_exp(id: ClientId,
|
||||
request_exp: &RequestExp,
|
||||
@ -51,3 +52,73 @@ where
|
||||
(c.client, SendShipPacket::Message(Message::new(GameMessage::PlayerDropItem(pdi.clone()))))
|
||||
})))
|
||||
}
|
||||
|
||||
pub fn drop_coordinates(id: ClientId,
|
||||
drop_coordinates: &DropCoordinates,
|
||||
client_location: &ClientLocation,
|
||||
clients: &mut Clients,
|
||||
rooms: &Rooms)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError>
|
||||
{
|
||||
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||
let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let room = rooms.get(room_id.0)
|
||||
.ok_or_else(|| ShipError::InvalidRoom(room_id.0 as u32))?
|
||||
.as_ref()
|
||||
.ok_or_else(|| ShipError::InvalidRoom(room_id.0 as u32))?;
|
||||
|
||||
client.item_drop_location = Some(ItemDropLocation {
|
||||
map_area: MapArea::from_value(&room.mode.episode(), drop_coordinates.map_area)?,
|
||||
x: drop_coordinates.x,
|
||||
z: drop_coordinates.z,
|
||||
item_id: ClientItemId(drop_coordinates.item_id),
|
||||
});
|
||||
|
||||
Ok(Box::new(None.into_iter()))
|
||||
}
|
||||
|
||||
pub fn split_item_stack<EG>(id: ClientId,
|
||||
split_item_stack: &PlayerSplitItemStack,
|
||||
entity_gateway: &mut EG,
|
||||
client_location: &ClientLocation,
|
||||
clients: &mut Clients,
|
||||
item_manager: &mut ItemManager)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError>
|
||||
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 drop_location.item_id.0 != split_item_stack.item_id {
|
||||
return Err(ShipError::DropInvalidItemId(split_item_stack.item_id));
|
||||
}
|
||||
|
||||
if split_item_stack.item_id == 0xFFFFFFFF {
|
||||
let dropped_meseta = item_manager.player_drops_meseta_on_shared_floor(entity_gateway, &mut client.character, drop_location, split_item_stack.amount as u32)?;
|
||||
|
||||
let dropped_meseta_pkt = builder::message::drop_split_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 item_to_split = item_manager.get_inventory_item_by_id(&client.character, drop_location.item_id)?;
|
||||
let dropped_item = item_manager.player_drops_partial_stack_on_shared_floor(entity_gateway, &client.character, item_to_split, drop_location, split_item_stack.amount as usize)?;
|
||||
|
||||
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()))))
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ use crate::ship::location::{ClientLocation, RoomLobby, MAX_ROOMS, ClientLocation
|
||||
|
||||
use crate::ship::items;
|
||||
use crate::ship::room;
|
||||
use crate::ship::map::{MapsError, MapAreaError};
|
||||
use crate::ship::map::{MapsError, MapAreaError, MapArea};
|
||||
use crate::ship::packet::handler;
|
||||
|
||||
pub const SHIP_PORT: u16 = 23423;
|
||||
@ -48,7 +48,8 @@ pub enum ShipError {
|
||||
ItemError, // TODO: refine this
|
||||
PickUpInvalidItemId(u32),
|
||||
DropInvalidItemId(u32),
|
||||
ItemManagerError(#[from] items::ItemManagerError)
|
||||
ItemManagerError(#[from] items::ItemManagerError),
|
||||
ItemDropLocationNotSet,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -155,6 +156,14 @@ impl SendServerPacket for SendShipPacket {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct ItemDropLocation {
|
||||
pub map_area: MapArea,
|
||||
pub x: f32,
|
||||
pub z: f32,
|
||||
pub item_id: items::ClientItemId,
|
||||
}
|
||||
|
||||
pub struct ClientState {
|
||||
pub user: UserAccountEntity,
|
||||
pub settings: UserSettingsEntity,
|
||||
@ -162,6 +171,7 @@ pub struct ClientState {
|
||||
session: Session,
|
||||
//guildcard: GuildCard,
|
||||
pub block: u32,
|
||||
pub item_drop_location: Option<ItemDropLocation>,
|
||||
}
|
||||
|
||||
impl ClientState {
|
||||
@ -172,6 +182,7 @@ impl ClientState {
|
||||
character: character,
|
||||
session: session,
|
||||
block: 1,
|
||||
item_drop_location: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -208,6 +219,12 @@ impl<EG: EntityGateway> ShipServerState<EG> {
|
||||
GameMessage::PlayerDropItem(player_drop_item) => {
|
||||
handler::message::player_drop_item(id, player_drop_item, &mut self.entity_gateway, &mut self.client_location, &mut self.clients, &mut self.rooms, &mut self.item_manager).unwrap()
|
||||
},
|
||||
GameMessage::DropCoordinates(drop_coordinates) => {
|
||||
handler::message::drop_coordinates(id, drop_coordinates, &self.client_location, &mut self.clients, &self.rooms).unwrap()
|
||||
},
|
||||
GameMessage::PlayerSplitItemStack(split_item_stack) => {
|
||||
handler::message::split_item_stack(id, split_item_stack, &mut self.entity_gateway, &mut self.client_location, &mut self.clients, &mut self.item_manager).unwrap()
|
||||
},
|
||||
_ => {
|
||||
let cmsg = msg.clone();
|
||||
Box::new(self.client_location.get_client_neighbors(id).unwrap().into_iter()
|
||||
|
Loading…
x
Reference in New Issue
Block a user