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::{Meseta, NewItemEntity};
|
||||||
use crate::entity::item::tool::Tool;
|
use crate::entity::item::tool::Tool;
|
||||||
use crate::ship::map::MapArea;
|
use crate::ship::map::MapArea;
|
||||||
|
use crate::ship::ship::ItemDropLocation;
|
||||||
use crate::ship::drops::{ItemDrop, ItemDropType};
|
use crate::ship::drops::{ItemDrop, ItemDropType};
|
||||||
use crate::ship::location::{AreaClient, RoomId};
|
use crate::ship::location::{AreaClient, RoomId};
|
||||||
|
|
||||||
@ -145,6 +146,8 @@ pub enum ItemManagerError {
|
|||||||
CouldNotAddToInventory(FloorItem),
|
CouldNotAddToInventory(FloorItem),
|
||||||
//ItemBelongsToOtherPlayer,
|
//ItemBelongsToOtherPlayer,
|
||||||
Idunnoman,
|
Idunnoman,
|
||||||
|
CouldNotSplitItem(InventoryItem),
|
||||||
|
CouldNotDropMeseta,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ItemManager {
|
pub struct ItemManager {
|
||||||
@ -152,11 +155,10 @@ pub struct ItemManager {
|
|||||||
|
|
||||||
character_inventory: HashMap<CharacterEntityId, Vec<InventoryItem>>,
|
character_inventory: HashMap<CharacterEntityId, Vec<InventoryItem>>,
|
||||||
character_floor: HashMap<CharacterEntityId, Vec<FloorItem>>,
|
character_floor: HashMap<CharacterEntityId, Vec<FloorItem>>,
|
||||||
character_item_id_counter: HashMap<CharacterEntityId, u32>,
|
|
||||||
|
|
||||||
character_room: HashMap<CharacterEntityId, RoomId>,
|
character_room: HashMap<CharacterEntityId, RoomId>,
|
||||||
room_floor: HashMap<RoomId, Vec<FloorItem>>,
|
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 {
|
impl ItemManager {
|
||||||
@ -165,7 +167,6 @@ impl ItemManager {
|
|||||||
id_counter: 0,
|
id_counter: 0,
|
||||||
character_inventory: HashMap::new(),
|
character_inventory: HashMap::new(),
|
||||||
character_floor: HashMap::new(),
|
character_floor: HashMap::new(),
|
||||||
character_item_id_counter: HashMap::new(),
|
|
||||||
character_room: HashMap::new(),
|
character_room: HashMap::new(),
|
||||||
room_floor: HashMap::new(),
|
room_floor: HashMap::new(),
|
||||||
room_item_id_counter: HashMap::new(),
|
room_item_id_counter: HashMap::new(),
|
||||||
@ -177,12 +178,6 @@ impl ItemManager {
|
|||||||
ClientItemId(self.id_counter)
|
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
|
// TODO: Result
|
||||||
pub fn load_character<EG: EntityGateway>(&mut self, entity_gateway: &mut EG, character: &CharacterEntity) {
|
pub fn load_character<EG: EntityGateway>(&mut self, entity_gateway: &mut EG, character: &CharacterEntity) {
|
||||||
let items = entity_gateway.get_items_by_character(&character);
|
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_room.insert(character.id, room_id);
|
||||||
self.character_floor.insert(character.id, Vec::new());
|
self.character_floor.insert(character.id, Vec::new());
|
||||||
self.room_floor.entry(room_id).or_insert(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> {
|
pub fn get_character_inventory(&self, character: &CharacterEntity) -> Result<CharacterInventory, ItemManagerError> {
|
||||||
@ -406,8 +406,8 @@ impl ItemManager {
|
|||||||
FloorItemType::Meseta(m) => ActiveItemEntityId::Meseta(m.clone()),
|
FloorItemType::Meseta(m) => ActiveItemEntityId::Meseta(m.clone()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let room_id = *self.character_room.get(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
|
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 item_id = self.room_item_id_counter.get_mut(room_id).ok_or(ItemManagerError::NoCharacter(character.id))?();
|
||||||
|
|
||||||
let floor_item = FloorItem {
|
let floor_item = FloorItem {
|
||||||
entity_id: entity_id,
|
entity_id: entity_id,
|
||||||
@ -488,4 +488,88 @@ impl ItemManager {
|
|||||||
shared_floor.push(room_floor_item);
|
shared_floor.push(room_floor_item);
|
||||||
Ok(())
|
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,
|
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 libpso::packet::messages::*;
|
||||||
use crate::entity::gateway::EntityGateway;
|
use crate::entity::gateway::EntityGateway;
|
||||||
use crate::common::serverstate::ClientId;
|
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::location::{ClientLocation, ClientLocationError, RoomLobby};
|
||||||
use crate::ship::map::{MapArea};
|
use crate::ship::map::{MapArea};
|
||||||
use crate::ship::items::{ItemManager, ClientItemId};
|
use crate::ship::items::{ItemManager, ClientItemId};
|
||||||
|
use crate::ship::packet::builder;
|
||||||
|
|
||||||
pub fn request_exp(id: ClientId,
|
pub fn request_exp(id: ClientId,
|
||||||
request_exp: &RequestExp,
|
request_exp: &RequestExp,
|
||||||
@ -51,3 +52,73 @@ where
|
|||||||
(c.client, SendShipPacket::Message(Message::new(GameMessage::PlayerDropItem(pdi.clone()))))
|
(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::items;
|
||||||
use crate::ship::room;
|
use crate::ship::room;
|
||||||
use crate::ship::map::{MapsError, MapAreaError};
|
use crate::ship::map::{MapsError, MapAreaError, MapArea};
|
||||||
use crate::ship::packet::handler;
|
use crate::ship::packet::handler;
|
||||||
|
|
||||||
pub const SHIP_PORT: u16 = 23423;
|
pub const SHIP_PORT: u16 = 23423;
|
||||||
@ -48,7 +48,8 @@ pub enum ShipError {
|
|||||||
ItemError, // TODO: refine this
|
ItemError, // TODO: refine this
|
||||||
PickUpInvalidItemId(u32),
|
PickUpInvalidItemId(u32),
|
||||||
DropInvalidItemId(u32),
|
DropInvalidItemId(u32),
|
||||||
ItemManagerError(#[from] items::ItemManagerError)
|
ItemManagerError(#[from] items::ItemManagerError),
|
||||||
|
ItemDropLocationNotSet,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[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 struct ClientState {
|
||||||
pub user: UserAccountEntity,
|
pub user: UserAccountEntity,
|
||||||
pub settings: UserSettingsEntity,
|
pub settings: UserSettingsEntity,
|
||||||
@ -162,6 +171,7 @@ pub struct ClientState {
|
|||||||
session: Session,
|
session: Session,
|
||||||
//guildcard: GuildCard,
|
//guildcard: GuildCard,
|
||||||
pub block: u32,
|
pub block: u32,
|
||||||
|
pub item_drop_location: Option<ItemDropLocation>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClientState {
|
impl ClientState {
|
||||||
@ -172,6 +182,7 @@ impl ClientState {
|
|||||||
character: character,
|
character: character,
|
||||||
session: session,
|
session: session,
|
||||||
block: 1,
|
block: 1,
|
||||||
|
item_drop_location: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -208,6 +219,12 @@ impl<EG: EntityGateway> ShipServerState<EG> {
|
|||||||
GameMessage::PlayerDropItem(player_drop_item) => {
|
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()
|
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();
|
let cmsg = msg.clone();
|
||||||
Box::new(self.client_location.get_client_neighbors(id).unwrap().into_iter()
|
Box::new(self.client_location.get_client_neighbors(id).unwrap().into_iter()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user