Merge pull request 'bankstuff' (#193) from bankstuff into master
This commit is contained in:
commit
744bf774ad
@ -2,6 +2,9 @@ use crate::ship::items::ClientItemId;
|
|||||||
use libpso::character::character;//::InventoryItem;
|
use libpso::character::character;//::InventoryItem;
|
||||||
use crate::entity::item::{ItemEntityId, ItemDetail};
|
use crate::entity::item::{ItemEntityId, ItemDetail};
|
||||||
use crate::entity::item::tool::Tool;
|
use crate::entity::item::tool::Tool;
|
||||||
|
use crate::ship::items::inventory::{InventoryItemHandle, InventoryItem};
|
||||||
|
|
||||||
|
const BANK_CAPACITY: usize = 200;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct IndividualBankItem {
|
pub struct IndividualBankItem {
|
||||||
@ -21,6 +24,15 @@ impl StackedBankItem {
|
|||||||
pub fn count(&self) -> usize {
|
pub fn count(&self) -> usize {
|
||||||
self.entity_ids.len()
|
self.entity_ids.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn take_entity_ids(&mut self, amount: usize) -> Option<Vec<ItemEntityId>> {
|
||||||
|
if amount <= self.count() {
|
||||||
|
Some(self.entity_ids.drain(..amount).collect())
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -123,22 +135,62 @@ impl BankItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CharacterBank(Vec<BankItem>);
|
|
||||||
|
pub struct BankItemHandle<'a> {
|
||||||
|
bank: &'a mut CharacterBank,
|
||||||
|
index: usize
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> BankItemHandle<'a> {
|
||||||
|
pub fn item(&'a self) -> Option<&'a BankItem> {
|
||||||
|
self.bank.items.get(self.index)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn item_mut(&mut self) -> Option<&mut BankItem> {
|
||||||
|
self.bank.items.get_mut(self.index)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_from_bank(self) {
|
||||||
|
self.bank.items.remove(self.index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CharacterBank {
|
||||||
|
item_id_counter: u32,
|
||||||
|
items: Vec<BankItem>
|
||||||
|
}
|
||||||
|
|
||||||
impl CharacterBank {
|
impl CharacterBank {
|
||||||
pub fn new(mut items: Vec<BankItem>) -> CharacterBank {
|
pub fn new(mut items: Vec<BankItem>) -> CharacterBank {
|
||||||
items.sort();
|
items.sort();
|
||||||
CharacterBank(items)
|
CharacterBank {
|
||||||
}
|
item_id_counter: 0,
|
||||||
|
items: items,
|
||||||
pub fn initialize_item_ids(&mut self, base_item_id: u32) {
|
|
||||||
for (i, item) in self.0.iter_mut().enumerate() {
|
|
||||||
item.set_item_id(ClientItemId(base_item_id + i as u32));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn initialize_item_ids(&mut self, base_item_id: u32) {
|
||||||
|
for (i, item) in self.items.iter_mut().enumerate() {
|
||||||
|
item.set_item_id(ClientItemId(base_item_id + i as u32));
|
||||||
|
}
|
||||||
|
self.item_id_counter = base_item_id + self.items.len() as u32 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_item_handle_by_id<'a>(&'a mut self, item_id: ClientItemId) -> Option<BankItemHandle<'a>> {
|
||||||
|
let (index, _) = self.items.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter(|(_, item)| {
|
||||||
|
item.item_id() == item_id
|
||||||
|
})
|
||||||
|
.nth(0)?;
|
||||||
|
Some(BankItemHandle {
|
||||||
|
bank: self,
|
||||||
|
index: index,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn as_client_bank_items(&self) -> character::Bank {
|
pub fn as_client_bank_items(&self) -> character::Bank {
|
||||||
self.0.iter()
|
self.items.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.fold(character::Bank::default(), |mut bank, (slot, item)| {
|
.fold(character::Bank::default(), |mut bank, (slot, item)| {
|
||||||
bank.item_count = (slot + 1) as u32;
|
bank.item_count = (slot + 1) as u32;
|
||||||
@ -152,7 +204,7 @@ impl CharacterBank {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_client_bank_request(&self) -> Vec<character::BankItem> {
|
pub fn as_client_bank_request(&self) -> Vec<character::BankItem> {
|
||||||
self.0.iter()
|
self.items.iter()
|
||||||
.map(|item| {
|
.map(|item| {
|
||||||
let bytes = item.as_client_bytes();
|
let bytes = item.as_client_bytes();
|
||||||
let mut data1 = [0; 12];
|
let mut data1 = [0; 12];
|
||||||
@ -179,7 +231,65 @@ impl CharacterBank {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn count(&self) -> usize {
|
pub fn count(&self) -> usize {
|
||||||
self.0.len()
|
self.items.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deposit_item(&mut self, mut inventory_item: InventoryItemHandle, amount: usize) -> Option<&BankItem> {
|
||||||
|
let remove = match inventory_item.item_mut()? {
|
||||||
|
InventoryItem::Individual(individual_inventory_item) => {
|
||||||
|
if self.items.len() >= BANK_CAPACITY {
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
self.items.push(BankItem::Individual(IndividualBankItem {
|
||||||
|
entity_id: individual_inventory_item.entity_id,
|
||||||
|
item_id: individual_inventory_item.item_id,
|
||||||
|
item: individual_inventory_item.item.clone(),
|
||||||
|
}));
|
||||||
|
true
|
||||||
|
},
|
||||||
|
InventoryItem::Stacked(stacked_inventory_item) => {
|
||||||
|
let existing_bank_item = self.items.iter_mut()
|
||||||
|
.find_map(|item| {
|
||||||
|
if let BankItem::Stacked(stacked_bank_item) = item {
|
||||||
|
if stacked_bank_item.tool == stacked_inventory_item.tool {
|
||||||
|
return Some(stacked_bank_item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
});
|
||||||
|
|
||||||
|
match existing_bank_item {
|
||||||
|
Some(stacked_bank_item) => {
|
||||||
|
if stacked_bank_item.count() + stacked_inventory_item.count() > stacked_inventory_item.tool.max_stack() {
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut deposited_entity_ids = stacked_inventory_item.take_entity_ids(amount)?;
|
||||||
|
stacked_bank_item.entity_ids.append(&mut deposited_entity_ids);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
if self.items.len() >= BANK_CAPACITY {
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
let deposited_entity_ids = stacked_inventory_item.take_entity_ids(amount)?;
|
||||||
|
|
||||||
|
self.item_id_counter += 1;
|
||||||
|
self.items.push(BankItem::Stacked(StackedBankItem {
|
||||||
|
entity_ids: deposited_entity_ids,
|
||||||
|
item_id: ClientItemId(self.item_id_counter),
|
||||||
|
tool: stacked_inventory_item.tool,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stacked_inventory_item.count() == 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if remove {
|
||||||
|
inventory_item.remove_from_inventory();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.items.last()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,9 +228,9 @@ impl RoomFloorItems {
|
|||||||
pub fn drop_partial_stacked_inventory_item(&mut self, inventory_item: InventoryItemHandle, amount: usize, new_item_id: ClientItemId, item_drop_location: (MapArea, f32, f32, f32)) -> Option<&StackedFloorItem> {
|
pub fn drop_partial_stacked_inventory_item(&mut self, inventory_item: InventoryItemHandle, amount: usize, new_item_id: ClientItemId, item_drop_location: (MapArea, f32, f32, f32)) -> Option<&StackedFloorItem> {
|
||||||
let consumed_item = inventory_item.consume(amount).ok()?;
|
let consumed_item = inventory_item.consume(amount).ok()?;
|
||||||
|
|
||||||
if let ItemDetail::Tool(tool) = consumed_item.item {
|
if let ItemDetail::Tool(tool) = consumed_item.item() {
|
||||||
self.0.push(FloorItem::Stacked(StackedFloorItem {
|
self.0.push(FloorItem::Stacked(StackedFloorItem {
|
||||||
entity_ids: consumed_item.entity_ids,
|
entity_ids: consumed_item.entity_ids(),
|
||||||
item_id: new_item_id,
|
item_id: new_item_id,
|
||||||
tool: tool,
|
tool: tool,
|
||||||
map_area: item_drop_location.0,
|
map_area: item_drop_location.0,
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use libpso::character::character;//::InventoryItem;
|
use libpso::character::character;//::InventoryItem;
|
||||||
use crate::entity::item::{ItemEntityId, ItemDetail};
|
use crate::entity::item::{ItemEntityId, ItemDetail, ItemType};
|
||||||
use crate::entity::item::tool::Tool;
|
use crate::entity::item::tool::Tool;
|
||||||
use crate::ship::items::ClientItemId;
|
use crate::ship::items::{ClientItemId, BankItem, BankItemHandle};
|
||||||
use crate::ship::items::floor::{IndividualFloorItem, StackedFloorItem};
|
use crate::ship::items::floor::{IndividualFloorItem, StackedFloorItem};
|
||||||
|
|
||||||
|
const INVENTORY_CAPACITY: usize = 30;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct InventorySlot(pub usize);
|
pub struct InventorySlot(pub usize);
|
||||||
@ -29,6 +31,15 @@ impl StackedInventoryItem {
|
|||||||
pub fn count(&self) -> usize {
|
pub fn count(&self) -> usize {
|
||||||
self.entity_ids.len()
|
self.entity_ids.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn take_entity_ids(&mut self, amount: usize) -> Option<Vec<ItemEntityId>> {
|
||||||
|
if amount <= self.count() {
|
||||||
|
Some(self.entity_ids.drain(..amount).collect())
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -68,6 +79,18 @@ impl InventoryItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn item_type(&self) -> ItemType {
|
||||||
|
match self {
|
||||||
|
InventoryItem::Individual(individual_inventory_item) => {
|
||||||
|
individual_inventory_item.item.item_type()
|
||||||
|
},
|
||||||
|
InventoryItem::Stacked(stacked_inventory_item) => {
|
||||||
|
ItemType::Tool(stacked_inventory_item.tool.tool)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TOOD: delete?
|
||||||
pub fn are_same_stackable_tool(&self, other_stacked_item: &StackedFloorItem) -> bool {
|
pub fn are_same_stackable_tool(&self, other_stacked_item: &StackedFloorItem) -> bool {
|
||||||
match self {
|
match self {
|
||||||
InventoryItem::Stacked(self_stacked_item) => {
|
InventoryItem::Stacked(self_stacked_item) => {
|
||||||
@ -78,6 +101,7 @@ impl InventoryItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TOOD: delete?
|
||||||
pub fn can_combine_stacks(&self, other_stacked_item: &StackedFloorItem) -> bool {
|
pub fn can_combine_stacks(&self, other_stacked_item: &StackedFloorItem) -> bool {
|
||||||
match self {
|
match self {
|
||||||
InventoryItem::Stacked(self_stacked_item) => {
|
InventoryItem::Stacked(self_stacked_item) => {
|
||||||
@ -90,6 +114,7 @@ impl InventoryItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: result
|
// TODO: result
|
||||||
|
// TOOD: delete?
|
||||||
pub fn combine_stacks(&mut self, other_stacked_item: &mut StackedFloorItem) {
|
pub fn combine_stacks(&mut self, other_stacked_item: &mut StackedFloorItem) {
|
||||||
match self {
|
match self {
|
||||||
InventoryItem::Stacked(self_stacked_item) => {
|
InventoryItem::Stacked(self_stacked_item) => {
|
||||||
@ -163,11 +188,46 @@ pub enum InventoryItemConsumeError {
|
|||||||
InvalidAmount,
|
InvalidAmount,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ConsumedItem {
|
pub struct IndividualConsumedItem {
|
||||||
pub entity_ids: Vec<ItemEntityId>,
|
pub entity_id: ItemEntityId,
|
||||||
pub item: ItemDetail
|
pub item: ItemDetail,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct StackedConsumedItem {
|
||||||
|
pub entity_ids: Vec<ItemEntityId>,
|
||||||
|
pub tool: Tool
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ConsumedItem {
|
||||||
|
Individual(IndividualConsumedItem),
|
||||||
|
Stacked(StackedConsumedItem),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConsumedItem {
|
||||||
|
pub fn entity_ids(&self) -> Vec<ItemEntityId> {
|
||||||
|
match self {
|
||||||
|
ConsumedItem::Individual(individual_consumed_item) => {
|
||||||
|
vec![individual_consumed_item.entity_id]
|
||||||
|
},
|
||||||
|
ConsumedItem::Stacked(stacked_consumed_item) => {
|
||||||
|
stacked_consumed_item.entity_ids.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn item(&self) -> ItemDetail {
|
||||||
|
match self {
|
||||||
|
ConsumedItem::Individual(individual_consumed_item) => {
|
||||||
|
individual_consumed_item.item.clone()
|
||||||
|
},
|
||||||
|
ConsumedItem::Stacked(stacked_consumed_item) => {
|
||||||
|
ItemDetail::Tool(stacked_consumed_item.tool)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pub struct InventoryItemHandle<'a> {
|
pub struct InventoryItemHandle<'a> {
|
||||||
inventory: &'a mut CharacterInventory,
|
inventory: &'a mut CharacterInventory,
|
||||||
slot: usize,
|
slot: usize,
|
||||||
@ -175,37 +235,41 @@ pub struct InventoryItemHandle<'a> {
|
|||||||
|
|
||||||
impl<'a> InventoryItemHandle<'a> {
|
impl<'a> InventoryItemHandle<'a> {
|
||||||
pub fn item(&'a self) -> Option<&'a InventoryItem> {
|
pub fn item(&'a self) -> Option<&'a InventoryItem> {
|
||||||
self.inventory.0.get(self.slot)
|
self.inventory.items.get(self.slot)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn item_mut(&mut self) -> Option<&mut InventoryItem> {
|
||||||
|
self.inventory.items.get_mut(self.slot)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_from_inventory(self) {
|
pub fn remove_from_inventory(self) {
|
||||||
self.inventory.0.remove(self.slot);
|
self.inventory.items.remove(self.slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn consume(self, amount: usize) -> Result<ConsumedItem, InventoryItemConsumeError> {
|
pub fn consume(self, amount: usize) -> Result<ConsumedItem, InventoryItemConsumeError> {
|
||||||
enum RemoveMethod {
|
enum RemoveMethod {
|
||||||
EntireThing(ConsumedItem),
|
EntireThing(ConsumedItem),
|
||||||
Partial(ItemDetail),
|
Partial(Tool),
|
||||||
}
|
}
|
||||||
|
|
||||||
let inventory_item = self.inventory.0.get(self.slot).ok_or(InventoryItemConsumeError::InconsistentState)?;
|
let inventory_item = self.inventory.items.get(self.slot).ok_or(InventoryItemConsumeError::InconsistentState)?;
|
||||||
let remove_method = match inventory_item {
|
let remove_method = match inventory_item {
|
||||||
InventoryItem::Individual(individual_inventory_item) => {
|
InventoryItem::Individual(individual_inventory_item) => {
|
||||||
RemoveMethod::EntireThing(ConsumedItem {
|
RemoveMethod::EntireThing(ConsumedItem::Individual(IndividualConsumedItem {
|
||||||
entity_ids: vec![individual_inventory_item.entity_id],
|
entity_id: individual_inventory_item.entity_id,
|
||||||
item: individual_inventory_item.item.clone()
|
item: individual_inventory_item.item.clone()
|
||||||
})
|
}))
|
||||||
},
|
},
|
||||||
InventoryItem::Stacked(stacked_inventory_item) => {
|
InventoryItem::Stacked(stacked_inventory_item) => {
|
||||||
match stacked_inventory_item.count().cmp(&amount) {
|
match stacked_inventory_item.count().cmp(&amount) {
|
||||||
Ordering::Equal => {
|
Ordering::Equal => {
|
||||||
RemoveMethod::EntireThing(ConsumedItem {
|
RemoveMethod::EntireThing(ConsumedItem::Stacked(StackedConsumedItem {
|
||||||
entity_ids: stacked_inventory_item.entity_ids.clone(),
|
entity_ids: stacked_inventory_item.entity_ids.clone(),
|
||||||
item: ItemDetail::Tool(stacked_inventory_item.tool),
|
tool: stacked_inventory_item.tool,
|
||||||
})
|
}))
|
||||||
},
|
},
|
||||||
Ordering::Greater => {
|
Ordering::Greater => {
|
||||||
RemoveMethod::Partial(ItemDetail::Tool(stacked_inventory_item.tool))
|
RemoveMethod::Partial(stacked_inventory_item.tool)
|
||||||
},
|
},
|
||||||
Ordering::Less => {
|
Ordering::Less => {
|
||||||
return Err(InventoryItemConsumeError::InvalidAmount)
|
return Err(InventoryItemConsumeError::InvalidAmount)
|
||||||
@ -216,11 +280,11 @@ impl<'a> InventoryItemHandle<'a> {
|
|||||||
|
|
||||||
match remove_method {
|
match remove_method {
|
||||||
RemoveMethod::EntireThing(consumed_item) => {
|
RemoveMethod::EntireThing(consumed_item) => {
|
||||||
self.inventory.0.remove(self.slot);
|
self.inventory.items.remove(self.slot);
|
||||||
Ok(consumed_item)
|
Ok(consumed_item)
|
||||||
},
|
},
|
||||||
RemoveMethod::Partial(item_detail) => {
|
RemoveMethod::Partial(tool) => {
|
||||||
let entity_ids = self.inventory.0.get_mut(self.slot)
|
let entity_ids = self.inventory.items.get_mut(self.slot)
|
||||||
.and_then(|item| {
|
.and_then(|item| {
|
||||||
if let InventoryItem::Stacked(stacked_inventory_item) = item {
|
if let InventoryItem::Stacked(stacked_inventory_item) = item {
|
||||||
Some(stacked_inventory_item.entity_ids.drain(..amount).collect::<Vec<_>>())
|
Some(stacked_inventory_item.entity_ids.drain(..amount).collect::<Vec<_>>())
|
||||||
@ -230,10 +294,10 @@ impl<'a> InventoryItemHandle<'a> {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.ok_or(InventoryItemConsumeError::InvalidAmount)?;
|
.ok_or(InventoryItemConsumeError::InvalidAmount)?;
|
||||||
Ok(ConsumedItem {
|
Ok(ConsumedItem::Stacked(StackedConsumedItem {
|
||||||
entity_ids: entity_ids,
|
entity_ids: entity_ids,
|
||||||
item: item_detail,
|
tool: tool,
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -243,21 +307,28 @@ impl<'a> InventoryItemHandle<'a> {
|
|||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CharacterInventory(Vec<InventoryItem>);
|
pub struct CharacterInventory {
|
||||||
|
item_id_counter: u32,
|
||||||
|
items: Vec<InventoryItem>,
|
||||||
|
}
|
||||||
|
|
||||||
impl CharacterInventory {
|
impl CharacterInventory {
|
||||||
pub fn new(items: Vec<InventoryItem>) -> CharacterInventory {
|
pub fn new(items: Vec<InventoryItem>) -> CharacterInventory {
|
||||||
CharacterInventory(items)
|
CharacterInventory{
|
||||||
}
|
item_id_counter: 0,
|
||||||
|
items: items,
|
||||||
pub fn initialize_item_ids(&mut self, base_item_id: u32) {
|
|
||||||
for (i, item) in self.0.iter_mut().enumerate() {
|
|
||||||
item.set_item_id(ClientItemId(base_item_id + i as u32));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn initialize_item_ids(&mut self, base_item_id: u32) {
|
||||||
|
for (i, item) in self.items.iter_mut().enumerate() {
|
||||||
|
item.set_item_id(ClientItemId(base_item_id + i as u32));
|
||||||
|
}
|
||||||
|
self.item_id_counter = base_item_id + self.items.len() as u32 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn as_client_inventory_items(&self) -> [character::InventoryItem; 30] {
|
pub fn as_client_inventory_items(&self) -> [character::InventoryItem; 30] {
|
||||||
self.0.iter()
|
self.items.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.fold([character::InventoryItem::default(); 30], |mut inventory, (slot, item)| {
|
.fold([character::InventoryItem::default(); 30], |mut inventory, (slot, item)| {
|
||||||
let bytes = item.as_client_bytes();
|
let bytes = item.as_client_bytes();
|
||||||
@ -273,15 +344,15 @@ impl CharacterInventory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn slot(&self, slot: usize) -> Option<&InventoryItem> {
|
pub fn slot(&self, slot: usize) -> Option<&InventoryItem> {
|
||||||
self.0.get(slot)
|
self.items.get(slot)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn count(&self) -> usize {
|
pub fn count(&self) -> usize {
|
||||||
self.0.len()
|
self.items.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_item_handle_by_id<'a>(&'a mut self, item_id: ClientItemId) -> Option<InventoryItemHandle<'a>> {
|
pub fn get_item_handle_by_id<'a>(&'a mut self, item_id: ClientItemId) -> Option<InventoryItemHandle<'a>> {
|
||||||
let (slot, _) = self.0.iter()
|
let (slot, _) = self.items.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter(|(_, item)| {
|
.filter(|(_, item)| {
|
||||||
item.item_id() == item_id
|
item.item_id() == item_id
|
||||||
@ -294,7 +365,7 @@ impl CharacterInventory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_item_by_id(&self, item_id: ClientItemId) -> Option<&InventoryItem> {
|
pub fn get_item_by_id(&self, item_id: ClientItemId) -> Option<&InventoryItem> {
|
||||||
self.0.iter()
|
self.items.iter()
|
||||||
.filter(|item| {
|
.filter(|item| {
|
||||||
item.item_id() == item_id
|
item.item_id() == item_id
|
||||||
})
|
})
|
||||||
@ -302,14 +373,14 @@ impl CharacterInventory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn take_item_by_id(&mut self, item_id: ClientItemId) -> Option<InventoryItem> {
|
pub fn take_item_by_id(&mut self, item_id: ClientItemId) -> Option<InventoryItem> {
|
||||||
self.0
|
self.items
|
||||||
.drain_filter(|i| i.item_id() == item_id)
|
.drain_filter(|i| i.item_id() == item_id)
|
||||||
.nth(0)
|
.nth(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_item(&mut self, item: InventoryItem) -> Result<(), ()> { // TODO: errors
|
pub fn add_item(&mut self, item: InventoryItem) -> Result<(), ()> { // TODO: errors
|
||||||
// TODO: check slot conflict?
|
// TODO: check slot conflict?
|
||||||
self.0.push(item);
|
self.items.push(item);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,14 +390,14 @@ impl CharacterInventory {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.0.push(InventoryItem::Individual(IndividualInventoryItem {
|
self.items.push(InventoryItem::Individual(IndividualInventoryItem {
|
||||||
entity_id: floor_item.entity_id,
|
entity_id: floor_item.entity_id,
|
||||||
item_id: floor_item.item_id,
|
item_id: floor_item.item_id,
|
||||||
item: floor_item.item.clone(),
|
item: floor_item.item.clone(),
|
||||||
equipped: false,
|
equipped: false,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if let Some(InventoryItem::Individual(new_item)) = self.0.last() {
|
if let Some(InventoryItem::Individual(new_item)) = self.items.last() {
|
||||||
Some((new_item, InventorySlot(self.count())))
|
Some((new_item, InventorySlot(self.count())))
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -336,7 +407,7 @@ impl CharacterInventory {
|
|||||||
|
|
||||||
// TODO: can be simplified using find instead of position
|
// TODO: can be simplified using find instead of position
|
||||||
pub fn pick_up_stacked_floor_item(&mut self, floor_item: &StackedFloorItem) -> Option<(&StackedInventoryItem, InventorySlot)> {
|
pub fn pick_up_stacked_floor_item(&mut self, floor_item: &StackedFloorItem) -> Option<(&StackedInventoryItem, InventorySlot)> {
|
||||||
let existing_stack_position = self.0.iter()
|
let existing_stack_position = self.items.iter()
|
||||||
.position(|inventory_item| {
|
.position(|inventory_item| {
|
||||||
if let InventoryItem::Stacked(stacked_inventory_item) = inventory_item {
|
if let InventoryItem::Stacked(stacked_inventory_item) = inventory_item {
|
||||||
if stacked_inventory_item.tool == floor_item.tool {
|
if stacked_inventory_item.tool == floor_item.tool {
|
||||||
@ -347,7 +418,7 @@ impl CharacterInventory {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if let Some(existing_stack_position) = existing_stack_position {
|
if let Some(existing_stack_position) = existing_stack_position {
|
||||||
if let Some(InventoryItem::Stacked(stacked_item)) = self.0.get_mut(existing_stack_position) {
|
if let Some(InventoryItem::Stacked(stacked_item)) = self.items.get_mut(existing_stack_position) {
|
||||||
if stacked_item.count() + floor_item.count() <= stacked_item.tool.max_stack() {
|
if stacked_item.count() + floor_item.count() <= stacked_item.tool.max_stack() {
|
||||||
stacked_item.entity_ids.append(&mut floor_item.entity_ids.clone());
|
stacked_item.entity_ids.append(&mut floor_item.entity_ids.clone());
|
||||||
Some((stacked_item, InventorySlot(existing_stack_position)))
|
Some((stacked_item, InventorySlot(existing_stack_position)))
|
||||||
@ -367,8 +438,8 @@ impl CharacterInventory {
|
|||||||
tool: floor_item.tool,
|
tool: floor_item.tool,
|
||||||
});
|
});
|
||||||
|
|
||||||
self.0.push(new_stacked_item);
|
self.items.push(new_stacked_item);
|
||||||
if let Some(InventoryItem::Stacked(new_item)) = self.0.last() {
|
if let Some(InventoryItem::Stacked(new_item)) = self.items.last() {
|
||||||
Some((new_item, InventorySlot(self.count())))
|
Some((new_item, InventorySlot(self.count())))
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -376,5 +447,69 @@ impl CharacterInventory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn withdraw_item(&mut self, mut bank_item: BankItemHandle, amount: usize) -> Option<(&InventoryItem, usize)> {
|
||||||
|
let (remove, slot) = match bank_item.item_mut()? {
|
||||||
|
BankItem::Individual(individual_bank_item) => {
|
||||||
|
if self.items.len() >= INVENTORY_CAPACITY {
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
self.items.push(InventoryItem::Individual(IndividualInventoryItem {
|
||||||
|
entity_id: individual_bank_item.entity_id,
|
||||||
|
item_id: individual_bank_item.item_id,
|
||||||
|
item: individual_bank_item.item.clone(),
|
||||||
|
equipped: false,
|
||||||
|
}));
|
||||||
|
(true, self.count())
|
||||||
|
},
|
||||||
|
BankItem::Stacked(stacked_bank_item) => {
|
||||||
|
let existing_inventory_item = self.items.iter_mut()
|
||||||
|
.enumerate()
|
||||||
|
.find_map(|(index, item)| {
|
||||||
|
if let InventoryItem::Stacked(stacked_inventory_item) = item {
|
||||||
|
if stacked_inventory_item.tool == stacked_inventory_item.tool {
|
||||||
|
return Some((index, stacked_inventory_item))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
});
|
||||||
|
|
||||||
|
let slot = match existing_inventory_item {
|
||||||
|
Some((slot, stacked_inventory_item)) => {
|
||||||
|
if stacked_inventory_item.count() + stacked_bank_item.count() > stacked_bank_item.tool.max_stack() {
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut withdrawn_entity_ids = stacked_bank_item.take_entity_ids(amount)?;
|
||||||
|
stacked_inventory_item.entity_ids.append(&mut withdrawn_entity_ids);
|
||||||
|
slot
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
if self.items.len() >= INVENTORY_CAPACITY {
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
let withdrawn_entity_ids = stacked_bank_item.take_entity_ids(amount)?;
|
||||||
|
|
||||||
|
self.item_id_counter += 1; // oh no
|
||||||
|
self.items.push(InventoryItem::Stacked(StackedInventoryItem {
|
||||||
|
entity_ids: withdrawn_entity_ids,
|
||||||
|
item_id: ClientItemId(self.item_id_counter),
|
||||||
|
tool: stacked_bank_item.tool,
|
||||||
|
}));
|
||||||
|
self.count()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
(stacked_bank_item.count() == 0, slot)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if remove {
|
||||||
|
bank_item.remove_from_bank();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.items.last().map(|item| {
|
||||||
|
(item, slot)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,15 +32,18 @@ pub enum ItemManagerError {
|
|||||||
Idunnoman,
|
Idunnoman,
|
||||||
CouldNotSplitItem(ClientItemId),
|
CouldNotSplitItem(ClientItemId),
|
||||||
CouldNotDropMeseta,
|
CouldNotDropMeseta,
|
||||||
|
InvalidBankName(BankName),
|
||||||
NotEnoughTools(Tool, usize, usize), // have, expected
|
NotEnoughTools(Tool, usize, usize), // have, expected
|
||||||
InventoryItemConsumeError(#[from] InventoryItemConsumeError),
|
InventoryItemConsumeError(#[from] InventoryItemConsumeError),
|
||||||
|
BankFull,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ItemManager {
|
pub struct ItemManager {
|
||||||
id_counter: u32,
|
id_counter: u32,
|
||||||
|
|
||||||
character_inventory: HashMap<CharacterEntityId, CharacterInventory>,
|
character_inventory: HashMap<CharacterEntityId, CharacterInventory>,
|
||||||
character_bank: HashMap<CharacterEntityId, BTreeMap<BankName, CharacterBank>>,
|
//character_bank: HashMap<CharacterEntityId, BTreeMap<BankName, CharacterBank>>,
|
||||||
|
character_bank: HashMap<CharacterEntityId, CharacterBank>,
|
||||||
character_floor: HashMap<CharacterEntityId, RoomFloorItems>,
|
character_floor: HashMap<CharacterEntityId, RoomFloorItems>,
|
||||||
|
|
||||||
character_room: HashMap<CharacterEntityId, RoomId>,
|
character_room: HashMap<CharacterEntityId, RoomId>,
|
||||||
@ -101,7 +104,8 @@ impl ItemManager {
|
|||||||
acc
|
acc
|
||||||
});
|
});
|
||||||
|
|
||||||
let bank_items = items.into_iter()
|
// TODO: not using BankName anymore, clean this up
|
||||||
|
let mut bank_items = items.into_iter()
|
||||||
.filter_map(|item| {
|
.filter_map(|item| {
|
||||||
match item.location {
|
match item.location {
|
||||||
ItemLocation::Bank{name, ..} => Some((item.id, item.item, name)),
|
ItemLocation::Bank{name, ..} => Some((item.id, item.item, name)),
|
||||||
@ -158,7 +162,7 @@ impl ItemManager {
|
|||||||
.collect::<BTreeMap<_, _>>();
|
.collect::<BTreeMap<_, _>>();
|
||||||
let inventory = CharacterInventory::new(inventory_items.into_iter().map(|(_k, v)| v).take(30).collect());
|
let inventory = CharacterInventory::new(inventory_items.into_iter().map(|(_k, v)| v).take(30).collect());
|
||||||
self.character_inventory.insert(character.id, inventory);
|
self.character_inventory.insert(character.id, inventory);
|
||||||
self.character_bank.insert(character.id, bank_items);
|
self.character_bank.insert(character.id, bank_items.remove(&BankName("".to_string())).unwrap_or(CharacterBank::new(Vec::new())));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_character_to_room(&mut self, room_id: RoomId, character: &CharacterEntity, area_client: AreaClient) {
|
pub fn add_character_to_room(&mut self, room_id: RoomId, character: &CharacterEntity, area_client: AreaClient) {
|
||||||
@ -166,7 +170,7 @@ impl ItemManager {
|
|||||||
let inventory = self.character_inventory.get_mut(&character.id).unwrap();
|
let inventory = self.character_inventory.get_mut(&character.id).unwrap();
|
||||||
inventory.initialize_item_ids(base_inventory_id);
|
inventory.initialize_item_ids(base_inventory_id);
|
||||||
let base_bank_id = ((area_client.local_client.id() as u32) << 21) | 0x20000;
|
let base_bank_id = ((area_client.local_client.id() as u32) << 21) | 0x20000;
|
||||||
let default_bank = self.character_bank.get_mut(&character.id).unwrap().get_mut(&BankName("".to_string()));
|
let default_bank = self.character_bank.get_mut(&character.id);//.unwrap().get_mut(&BankName("".to_string()));
|
||||||
match default_bank {
|
match default_bank {
|
||||||
Some(default_bank) => {
|
Some(default_bank) => {
|
||||||
default_bank.initialize_item_ids(base_bank_id);
|
default_bank.initialize_item_ids(base_bank_id);
|
||||||
@ -192,11 +196,20 @@ impl ItemManager {
|
|||||||
pub fn get_character_bank(&self, character: &CharacterEntity) -> Result<&CharacterBank, ItemManagerError> {
|
pub fn get_character_bank(&self, character: &CharacterEntity) -> Result<&CharacterBank, ItemManagerError> {
|
||||||
Ok(self.character_bank
|
Ok(self.character_bank
|
||||||
.get(&character.id)
|
.get(&character.id)
|
||||||
.ok_or(ItemManagerError::NoCharacter(character.id))?
|
.ok_or(ItemManagerError::NoCharacter(character.id))?)
|
||||||
.get(&BankName("".to_string()))
|
//.get(&BankName("".to_string()))
|
||||||
.unwrap()) // TODO: make an error
|
//.ok_or(ItemManagerError::InvalidBankName(BankName("".to_string())))?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*pub fn get_character_bank_mut(&mut self, character: &CharacterEntity) -> Result<&CharacterBank, ItemManagerError> {
|
||||||
|
Ok(self.character_bank
|
||||||
|
.get_mut(&character.id)
|
||||||
|
.ok_or(ItemManagerError::NoCharacter(character.id))?
|
||||||
|
.entry(BankName("".to_string()))
|
||||||
|
.or_insert(CharacterBank::new(Vec::new())))
|
||||||
|
//.ok_or(ItemManagerError::InvalidBankName(BankName("".to_string())))?)
|
||||||
|
}*/
|
||||||
|
|
||||||
pub fn remove_character_from_room(&mut self, character: &CharacterEntity) {
|
pub fn remove_character_from_room(&mut self, character: &CharacterEntity) {
|
||||||
self.character_inventory.remove(&character.id);
|
self.character_inventory.remove(&character.id);
|
||||||
self.character_floor.remove(&character.id);
|
self.character_floor.remove(&character.id);
|
||||||
@ -256,7 +269,6 @@ impl ItemManager {
|
|||||||
},
|
},
|
||||||
Some(FloorItem::Stacked(stacked_floor_item)) => {
|
Some(FloorItem::Stacked(stacked_floor_item)) => {
|
||||||
let new_inventory_item = inventory.pick_up_stacked_floor_item(&stacked_floor_item);
|
let new_inventory_item = inventory.pick_up_stacked_floor_item(&stacked_floor_item);
|
||||||
println!("new inv item! {:?}", new_inventory_item);
|
|
||||||
|
|
||||||
match new_inventory_item {
|
match new_inventory_item {
|
||||||
Some((new_inventory_item, slot)) => {
|
Some((new_inventory_item, slot)) => {
|
||||||
@ -501,29 +513,86 @@ impl ItemManager {
|
|||||||
let used_item = inventory.get_item_handle_by_id(item_id).ok_or(ItemManagerError::NoSuchItemId(item_id))?;
|
let used_item = inventory.get_item_handle_by_id(item_id).ok_or(ItemManagerError::NoSuchItemId(item_id))?;
|
||||||
let consumed_item = used_item.consume(amount)?;
|
let consumed_item = used_item.consume(amount)?;
|
||||||
|
|
||||||
for entity_id in consumed_item.entity_ids {
|
for entity_id in consumed_item.entity_ids() {
|
||||||
entity_gateway.change_item_location(&entity_id,
|
entity_gateway.change_item_location(&entity_id,
|
||||||
ItemLocation::Consumed).await;
|
ItemLocation::Consumed).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(consumed_item.item)
|
Ok(consumed_item.item())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn player_deposits_item<EG: EntityGateway>(&mut self,
|
pub async fn player_deposits_item<EG: EntityGateway>(&mut self,
|
||||||
_entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
_character: &CharacterEntity,
|
character: &CharacterEntity,
|
||||||
_item_id: ClientItemId,
|
item_id: ClientItemId,
|
||||||
_amount: usize)
|
amount: usize)
|
||||||
-> Result<(), ItemManagerError> {
|
-> Result<(), ItemManagerError> {
|
||||||
|
let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
|
||||||
|
let bank = self.character_bank
|
||||||
|
.get_mut(&character.id)
|
||||||
|
.ok_or(ItemManagerError::NoCharacter(character.id))?;
|
||||||
|
|
||||||
|
let item_to_deposit = inventory.get_item_handle_by_id(item_id).ok_or(ItemManagerError::NoSuchItemId(item_id))?;
|
||||||
|
let bank_item = bank.deposit_item(item_to_deposit, amount).ok_or(ItemManagerError::Idunnoman)?;
|
||||||
|
|
||||||
|
match bank_item {
|
||||||
|
BankItem::Individual(individual_bank_item) => {
|
||||||
|
entity_gateway.change_item_location(&individual_bank_item.entity_id,
|
||||||
|
ItemLocation::Bank {
|
||||||
|
character_id: character.id,
|
||||||
|
name: BankName("".to_string())
|
||||||
|
}).await;
|
||||||
|
},
|
||||||
|
BankItem::Stacked(stacked_bank_item) => {
|
||||||
|
for entity_id in &stacked_bank_item.entity_ids {
|
||||||
|
entity_gateway.change_item_location(entity_id,
|
||||||
|
ItemLocation::Bank {
|
||||||
|
character_id: character.id,
|
||||||
|
name: BankName("".to_string())
|
||||||
|
}).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn player_withdraws_item<EG: EntityGateway>(&mut self,
|
pub async fn player_withdraws_item<EG: EntityGateway>(&mut self,
|
||||||
_entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
_character: &CharacterEntity,
|
character: &CharacterEntity,
|
||||||
_item_id: ClientItemId,
|
item_id: ClientItemId,
|
||||||
_amount: usize)
|
amount: usize)
|
||||||
-> Result<(), ItemManagerError> {
|
-> Result<&InventoryItem, ItemManagerError> {
|
||||||
Ok(())
|
|
||||||
|
let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
|
||||||
|
let bank = self.character_bank
|
||||||
|
.get_mut(&character.id)
|
||||||
|
.ok_or(ItemManagerError::NoCharacter(character.id))?;
|
||||||
|
|
||||||
|
let item_to_withdraw = bank.get_item_handle_by_id(item_id).ok_or(ItemManagerError::NoSuchItemId(item_id))?;
|
||||||
|
let inventory_item = inventory.withdraw_item(item_to_withdraw, amount).ok_or(ItemManagerError::Idunnoman)?;
|
||||||
|
|
||||||
|
match inventory_item {
|
||||||
|
(InventoryItem::Individual(individual_inventory_item), slot) => {
|
||||||
|
entity_gateway.change_item_location(&individual_inventory_item.entity_id,
|
||||||
|
ItemLocation::Inventory {
|
||||||
|
character_id: character.id,
|
||||||
|
slot: slot,
|
||||||
|
equipped: false,
|
||||||
|
}).await;
|
||||||
|
},
|
||||||
|
(InventoryItem::Stacked(stacked_inventory_item), slot) => {
|
||||||
|
for entity_id in &stacked_inventory_item.entity_ids {
|
||||||
|
entity_gateway.change_item_location(entity_id,
|
||||||
|
ItemLocation::Inventory {
|
||||||
|
character_id: character.id,
|
||||||
|
slot: slot,
|
||||||
|
equipped: false,
|
||||||
|
}).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(inventory_item.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ use libpso::packet::messages::*;
|
|||||||
use libpso::packet::ship::*;
|
use libpso::packet::ship::*;
|
||||||
use crate::common::leveltable::CharacterStats;
|
use crate::common::leveltable::CharacterStats;
|
||||||
use crate::ship::ship::{ShipError};
|
use crate::ship::ship::{ShipError};
|
||||||
use crate::ship::items::{StackedFloorItem, FloorItem, CharacterBank};
|
use crate::ship::items::{ClientItemId, InventoryItem, StackedFloorItem, FloorItem, CharacterBank};
|
||||||
use crate::ship::location::AreaClient;
|
use crate::ship::location::AreaClient;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
|
|
||||||
@ -37,6 +37,18 @@ pub fn create_item(area_client: AreaClient, item: &FloorItem) -> Result<CreateIt
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn create_withdrawn_inventory_item(area_client: AreaClient, item: &InventoryItem) -> Result<CreateItem, ShipError> {
|
||||||
|
let bytes = item.as_client_bytes();
|
||||||
|
Ok(CreateItem {
|
||||||
|
client: area_client.local_client.id(),
|
||||||
|
target: 0,
|
||||||
|
item_data: bytes[0..12].try_into()?,
|
||||||
|
item_id: item.item_id().0,
|
||||||
|
item_data2: bytes[12..16].try_into()?,
|
||||||
|
unknown: 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn remove_item_from_floor(area_client: AreaClient, item: &FloorItem) -> Result<RemoveItemFromFloor, ShipError> {
|
pub fn remove_item_from_floor(area_client: AreaClient, item: &FloorItem) -> Result<RemoveItemFromFloor, ShipError> {
|
||||||
Ok(RemoveItemFromFloor {
|
Ok(RemoveItemFromFloor {
|
||||||
client: area_client.local_client.id(),
|
client: area_client.local_client.id(),
|
||||||
@ -118,3 +130,12 @@ pub fn bank_item_list(bank: &CharacterBank) -> BankItemList {
|
|||||||
items: bank.as_client_bank_request()
|
items: bank.as_client_bank_request()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn player_no_longer_has_item(area_client: AreaClient, item_id: ClientItemId, amount: u32) -> PlayerNoLongerHasItem {
|
||||||
|
PlayerNoLongerHasItem {
|
||||||
|
client: area_client.local_client.id(),
|
||||||
|
target: 0,
|
||||||
|
item_id: item_id.0,
|
||||||
|
amount: amount,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -72,45 +72,6 @@ where
|
|||||||
|
|
||||||
let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
|
let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||||
|
|
||||||
/*
|
|
||||||
let item_drop_packets = clients_in_area.into_iter()
|
|
||||||
.filter_map(|area_client| {
|
|
||||||
room.drop_table.get_drop(&monster.map_area, &monster.monster).map(|item_drop_type| {
|
|
||||||
warn!("drop is? {:?}", item_drop_type);
|
|
||||||
(area_client, item_drop_type)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.map(|(area_client, item_drop_type)| async {
|
|
||||||
let item_drop = ItemDrop {
|
|
||||||
map_area: monster.map_area,
|
|
||||||
x: request_item.x,
|
|
||||||
y: request_item.y,
|
|
||||||
z: request_item.z,
|
|
||||||
item: item_drop_type,
|
|
||||||
};
|
|
||||||
let client = clients.get_mut(&area_client.client).ok_or(ShipError::ClientNotFound(area_client.client))?;
|
|
||||||
let floor_item = item_manager.enemy_drop_item_on_local_floor(entity_gateway, &client.character, item_drop).await.unwrap(); // TODO: unwrap
|
|
||||||
let item_drop_msg = builder::message::item_drop(request_item.client, request_item.target, &floor_item)?;
|
|
||||||
|
|
||||||
// I am not able to manually specify a closure return type when also using the async keyword
|
|
||||||
let result: Result<(ClientId, SendShipPacket), ShipError> = Ok((area_client.client, SendShipPacket::Message(Message::new(GameMessage::ItemDrop(item_drop_msg)))));
|
|
||||||
result
|
|
||||||
})
|
|
||||||
.map(|item_drop_pkt| async {
|
|
||||||
item_drop_pkt.await
|
|
||||||
});
|
|
||||||
|
|
||||||
let item_drop_packets = join_all(item_drop_packets).await.into_iter()
|
|
||||||
.filter_map(|item_drop_pkt| {
|
|
||||||
// TODO: log errors here
|
|
||||||
item_drop_pkt.ok()
|
|
||||||
});
|
|
||||||
|
|
||||||
//.collect::<Vec<_>>(); // TODO: can EntityGateway be Sync?
|
|
||||||
|
|
||||||
Ok(Box::new(item_drop_packets))
|
|
||||||
*/
|
|
||||||
|
|
||||||
let client_and_drop = clients_in_area.into_iter()
|
let client_and_drop = clients_in_area.into_iter()
|
||||||
.filter_map(|area_client| {
|
.filter_map(|area_client| {
|
||||||
room.drop_table.get_drop(&monster.map_area, &monster.monster).map(|item_drop_type| {
|
room.drop_table.get_drop(&monster.map_area, &monster.monster).map(|item_drop_type| {
|
||||||
@ -209,41 +170,6 @@ EG: EntityGateway
|
|||||||
|
|
||||||
let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
|
let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||||
|
|
||||||
/*let item_drop_packets = clients_in_area.into_iter()
|
|
||||||
.filter_map(|area_client| {
|
|
||||||
room.drop_table.get_box_drop(&box_object.map, &box_object).map(|item_drop_type| {
|
|
||||||
warn!("drop is? {:?}", item_drop_type);
|
|
||||||
(area_client, item_drop_type)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.map(async move |(area_client, item_drop_type)| -> Result<_, ShipError> {
|
|
||||||
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_type,
|
|
||||||
};
|
|
||||||
let client = clients.get_mut(&area_client.client).ok_or(ShipError::ClientNotFound(area_client.client))?;
|
|
||||||
let floor_item = item_manager.enemy_drop_item_on_local_floor(entity_gateway, &client.character, item_drop).await.unwrap(); // TODO: unwrap
|
|
||||||
let item_drop_msg = builder::message::item_drop(box_drop_request.client, box_drop_request.target, &floor_item)?;
|
|
||||||
Ok((area_client.client, SendShipPacket::Message(Message::new(GameMessage::ItemDrop(item_drop_msg)))))
|
|
||||||
})
|
|
||||||
/*.filter_map(|item_drop_pkt| {
|
|
||||||
// TODO: log errors here
|
|
||||||
item_drop_pkt.ok()
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>(); // TODO: can EntityGateway be Sync?*/
|
|
||||||
;
|
|
||||||
let item_drop_packets = join_all(item_drop_packets).await.into_iter()
|
|
||||||
.filter_map(|item_drop_pkt| {
|
|
||||||
// TODO: log errors here
|
|
||||||
item_drop_pkt.ok()
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(Box::new(item_drop_packets))
|
|
||||||
*/
|
|
||||||
|
|
||||||
let client_and_drop = clients_in_area.into_iter()
|
let client_and_drop = clients_in_area.into_iter()
|
||||||
.filter_map(|area_client| {
|
.filter_map(|area_client| {
|
||||||
room.drop_table.get_box_drop(&box_object.map, &box_object).map(|item_drop_type| {
|
room.drop_table.get_box_drop(&box_object.map, &box_object).map(|item_drop_type| {
|
||||||
@ -287,6 +213,7 @@ pub async fn send_bank_list(id: ClientId,
|
|||||||
pub async fn bank_interaction<EG>(id: ClientId,
|
pub async fn bank_interaction<EG>(id: ClientId,
|
||||||
bank_interaction: &BankInteraction,
|
bank_interaction: &BankInteraction,
|
||||||
entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
|
client_location: &ClientLocation,
|
||||||
clients: &mut Clients,
|
clients: &mut Clients,
|
||||||
item_manager: &mut ItemManager)
|
item_manager: &mut ItemManager)
|
||||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError>
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError>
|
||||||
@ -294,32 +221,51 @@ where
|
|||||||
EG: EntityGateway
|
EG: EntityGateway
|
||||||
{
|
{
|
||||||
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||||
match bank_interaction.action {
|
let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||||
|
let other_clients_in_area = client_location.get_client_neighbors(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||||
|
let bank_action_pkts = match bank_interaction.action {
|
||||||
BANK_ACTION_DEPOSIT => {
|
BANK_ACTION_DEPOSIT => {
|
||||||
if bank_interaction.item_id == 0xFFFFFFFF {
|
if bank_interaction.item_id == 0xFFFFFFFF {
|
||||||
if client.character.meseta < bank_interaction.meseta_amount && client.character.bank_meseta <= 999999 {
|
if client.character.meseta > bank_interaction.meseta_amount && (bank_interaction.meseta_amount + client.character.bank_meseta) <= 999999 {
|
||||||
client.character.meseta += bank_interaction.meseta_amount;
|
client.character.meseta -= bank_interaction.meseta_amount;
|
||||||
|
client.character.bank_meseta += bank_interaction.meseta_amount;
|
||||||
entity_gateway.save_character(&client.character).await;
|
entity_gateway.save_character(&client.character).await;
|
||||||
}
|
}
|
||||||
|
Vec::new()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
//let inventory_item = item_manager.get_inventory_item_by_id(&client.character, ClientItemId(bank_interaction.item_id))?;
|
|
||||||
item_manager.player_deposits_item(entity_gateway, &client.character, ClientItemId(bank_interaction.item_id), bank_interaction.item_amount as usize).await?;
|
item_manager.player_deposits_item(entity_gateway, &client.character, ClientItemId(bank_interaction.item_id), bank_interaction.item_amount as usize).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 => {
|
BANK_ACTION_WITHDRAW => {
|
||||||
if bank_interaction.item_id == 0xFFFFFFFF {
|
if bank_interaction.item_id == 0xFFFFFFFF {
|
||||||
if client.character.meseta + bank_interaction.meseta_amount <= 999999 {
|
if client.character.meseta + bank_interaction.meseta_amount <= 999999 {
|
||||||
client.character.meseta += bank_interaction.meseta_amount;
|
client.character.meseta += bank_interaction.meseta_amount;
|
||||||
|
client.character.bank_meseta -= bank_interaction.meseta_amount;
|
||||||
entity_gateway.save_character(&client.character).await;
|
entity_gateway.save_character(&client.character).await;
|
||||||
}
|
}
|
||||||
|
Vec::new()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
//let bank_item = item_manager.get_bank_item_by_id(&client.character, ClientItemId(bank_interaction.item_id))?;
|
let item_added_to_inventory = item_manager.player_withdraws_item(entity_gateway, &client.character, ClientItemId(bank_interaction.item_id), bank_interaction.item_amount as usize).await?;
|
||||||
item_manager.player_withdraws_item(entity_gateway, &client.character, ClientItemId(bank_interaction.item_id), bank_interaction.item_amount as usize).await?;
|
let item_created = builder::message::create_withdrawn_inventory_item(area_client, &item_added_to_inventory)?;
|
||||||
|
vec![SendShipPacket::Message(Message::new(GameMessage::CreateItem(item_created)))]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {}
|
_ => {
|
||||||
}
|
Vec::new()
|
||||||
Ok(Box::new(None.into_iter()))
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Box::new(other_clients_in_area.into_iter()
|
||||||
|
.map(move |c| {
|
||||||
|
bank_action_pkts.clone().into_iter()
|
||||||
|
.map(move |pkt| {
|
||||||
|
(c.client, pkt)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
@ -117,7 +117,7 @@ pub fn drop_coordinates(id: ClientId,
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn split_item_stack<EG>(id: ClientId,
|
pub async fn split_item_stack<EG>(id: ClientId,
|
||||||
split_item_stack: &PlayerSplitItemStack,
|
no_longer_has_item: &PlayerNoLongerHasItem,
|
||||||
entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
clients: &mut Clients,
|
clients: &mut Clients,
|
||||||
@ -131,12 +131,12 @@ where
|
|||||||
let room_id = client_location.get_room(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)?;
|
let drop_location = client.item_drop_location.ok_or(ShipError::ItemDropLocationNotSet)?;
|
||||||
|
|
||||||
if drop_location.item_id.0 != split_item_stack.item_id {
|
if drop_location.item_id.0 != no_longer_has_item.item_id {
|
||||||
return Err(ShipError::DropInvalidItemId(split_item_stack.item_id));
|
return Err(ShipError::DropInvalidItemId(no_longer_has_item.item_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
if split_item_stack.item_id == 0xFFFFFFFF {
|
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, split_item_stack.amount as u32).await?;
|
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)?;
|
let dropped_meseta_pkt = builder::message::drop_split_meseta_stack(area_client, &dropped_meseta)?;
|
||||||
client.item_drop_location = None;
|
client.item_drop_location = None;
|
||||||
@ -148,7 +148,7 @@ where
|
|||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let dropped_item = item_manager.player_drops_partial_stack_on_shared_floor(entity_gateway, &client.character, drop_location.item_id, drop_location, split_item_stack.amount as usize).await?;
|
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)?;
|
let dropped_item_pkt = builder::message::drop_split_stack(area_client, dropped_item)?;
|
||||||
client.item_drop_location = None;
|
client.item_drop_location = None;
|
||||||
|
@ -283,8 +283,8 @@ impl<EG: EntityGateway> ShipServerState<EG> {
|
|||||||
GameMessage::DropCoordinates(drop_coordinates) => {
|
GameMessage::DropCoordinates(drop_coordinates) => {
|
||||||
handler::message::drop_coordinates(id, drop_coordinates, &self.client_location, &mut self.clients, &self.rooms)
|
handler::message::drop_coordinates(id, drop_coordinates, &self.client_location, &mut self.clients, &self.rooms)
|
||||||
},
|
},
|
||||||
GameMessage::PlayerSplitItemStack(split_item_stack) => {
|
GameMessage::PlayerNoLongerHasItem(no_longer_has_item) => {
|
||||||
handler::message::split_item_stack(id, split_item_stack, &mut self.entity_gateway, &mut self.client_location, &mut self.clients, &mut self.item_manager).await
|
handler::message::split_item_stack(id, no_longer_has_item, &mut self.entity_gateway, &mut self.client_location, &mut self.clients, &mut self.item_manager).await
|
||||||
},
|
},
|
||||||
GameMessage::PlayerChangedMap(_) | GameMessage::PlayerChangedMap2(_) | GameMessage::TellOtherPlayerMyLocation(_) |
|
GameMessage::PlayerChangedMap(_) | GameMessage::PlayerChangedMap2(_) | GameMessage::TellOtherPlayerMyLocation(_) |
|
||||||
GameMessage::PlayerWarpingToFloor(_) | GameMessage::PlayerTeleported(_) | GameMessage::PlayerStopped(_) |
|
GameMessage::PlayerWarpingToFloor(_) | GameMessage::PlayerTeleported(_) | GameMessage::PlayerStopped(_) |
|
||||||
@ -327,7 +327,7 @@ impl<EG: EntityGateway> ShipServerState<EG> {
|
|||||||
handler::direct_message::send_bank_list(id, &self.clients, &mut self.item_manager).await
|
handler::direct_message::send_bank_list(id, &self.clients, &mut self.item_manager).await
|
||||||
},
|
},
|
||||||
GameMessage::BankInteraction(bank_interaction) => {
|
GameMessage::BankInteraction(bank_interaction) => {
|
||||||
handler::direct_message::bank_interaction(id, bank_interaction, &mut self.entity_gateway, &mut self.clients, &mut self.item_manager).await
|
handler::direct_message::bank_interaction(id, bank_interaction, &mut self.entity_gateway, &self.client_location, &mut self.clients, &mut self.item_manager).await
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
let cmsg = msg.clone();
|
let cmsg = msg.clone();
|
||||||
|
1569
tests/test_bank.rs
1569
tests/test_bank.rs
File diff suppressed because it is too large
Load Diff
@ -205,7 +205,7 @@ async fn test_pick_up_meseta_when_inventory_full() {
|
|||||||
z: 0.0,
|
z: 0.0,
|
||||||
})))).await.unwrap().for_each(drop);
|
})))).await.unwrap().for_each(drop);
|
||||||
|
|
||||||
ship.handle(ClientId(2), &RecvShipPacket::Message(Message::new(GameMessage::PlayerSplitItemStack(PlayerSplitItemStack {
|
ship.handle(ClientId(2), &RecvShipPacket::Message(Message::new(GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
|
||||||
client: 0,
|
client: 0,
|
||||||
target: 0,
|
target: 0,
|
||||||
item_id: 0xFFFFFFFF,
|
item_id: 0xFFFFFFFF,
|
||||||
@ -445,7 +445,7 @@ async fn test_can_not_drop_more_meseta_than_is_held() {
|
|||||||
z: 0.0,
|
z: 0.0,
|
||||||
})))).await.unwrap().for_each(drop);
|
})))).await.unwrap().for_each(drop);
|
||||||
|
|
||||||
let split_attempt = ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerSplitItemStack(PlayerSplitItemStack {
|
let split_attempt = ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
|
||||||
client: 0,
|
client: 0,
|
||||||
target: 0,
|
target: 0,
|
||||||
item_id: 0xFFFFFFFF,
|
item_id: 0xFFFFFFFF,
|
||||||
@ -565,7 +565,7 @@ async fn test_can_not_pick_up_meseta_when_full() {
|
|||||||
z: 0.0,
|
z: 0.0,
|
||||||
})))).await.unwrap().for_each(drop);
|
})))).await.unwrap().for_each(drop);
|
||||||
|
|
||||||
ship.handle(ClientId(2), &RecvShipPacket::Message(Message::new(GameMessage::PlayerSplitItemStack(PlayerSplitItemStack {
|
ship.handle(ClientId(2), &RecvShipPacket::Message(Message::new(GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
|
||||||
client: 0,
|
client: 0,
|
||||||
target: 0,
|
target: 0,
|
||||||
item_id: 0xFFFFFFFF,
|
item_id: 0xFFFFFFFF,
|
||||||
@ -579,7 +579,6 @@ async fn test_can_not_pick_up_meseta_when_full() {
|
|||||||
map_area: 0,
|
map_area: 0,
|
||||||
unknown: [0; 3]
|
unknown: [0; 3]
|
||||||
})))).await.unwrap().collect::<Vec<_>>();
|
})))).await.unwrap().collect::<Vec<_>>();
|
||||||
println!("pkts {:?}", packets);
|
|
||||||
assert!(packets.len() == 0);
|
assert!(packets.len() == 0);
|
||||||
|
|
||||||
let characters1 = entity_gateway.get_characters_by_user(&user1).await;
|
let characters1 = entity_gateway.get_characters_by_user(&user1).await;
|
||||||
@ -622,7 +621,7 @@ async fn test_meseta_caps_at_999999_when_trying_to_pick_up_more() {
|
|||||||
z: 0.0,
|
z: 0.0,
|
||||||
})))).await.unwrap().for_each(drop);
|
})))).await.unwrap().for_each(drop);
|
||||||
|
|
||||||
ship.handle(ClientId(2), &RecvShipPacket::Message(Message::new(GameMessage::PlayerSplitItemStack(PlayerSplitItemStack {
|
ship.handle(ClientId(2), &RecvShipPacket::Message(Message::new(GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
|
||||||
client: 0,
|
client: 0,
|
||||||
target: 0,
|
target: 0,
|
||||||
item_id: 0xFFFFFFFF,
|
item_id: 0xFFFFFFFF,
|
||||||
@ -688,7 +687,7 @@ async fn test_player_drops_partial_stack_and_other_player_picks_it_up() {
|
|||||||
z: 0.0,
|
z: 0.0,
|
||||||
})))).await.unwrap().for_each(drop);
|
})))).await.unwrap().for_each(drop);
|
||||||
|
|
||||||
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerSplitItemStack(PlayerSplitItemStack {
|
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
|
||||||
client: 0,
|
client: 0,
|
||||||
target: 0,
|
target: 0,
|
||||||
item_id: 0x10000,
|
item_id: 0x10000,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user