move bank code out of state
This commit is contained in:
		
							parent
							
								
									317f236f7c
								
							
						
					
					
						commit
						7d5c4e0165
					
				@ -2,7 +2,7 @@ use libpso::character::character;
 | 
			
		||||
use crate::common::leveltable::CharacterStats;
 | 
			
		||||
use crate::entity::character::CharacterEntity;
 | 
			
		||||
//use crate::ship::items::{CharacterInventory, CharacterBank};
 | 
			
		||||
use crate::ship::items::state::{BankState};
 | 
			
		||||
use crate::ship::items::bank::BankState;
 | 
			
		||||
use crate::ship::items::inventory::InventoryState;
 | 
			
		||||
use crate::entity::item::Meseta;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,8 @@ use std::pin::Pin;
 | 
			
		||||
use crate::ship::map::MapArea;
 | 
			
		||||
use crate::entity::character::{CharacterEntity, CharacterEntityId};
 | 
			
		||||
use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction};
 | 
			
		||||
use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateError, AddItemResult, StackedItemDetail, BankItem, BankItemDetail, IndividualItemDetail};
 | 
			
		||||
use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateError, AddItemResult, StackedItemDetail, IndividualItemDetail};
 | 
			
		||||
use crate::ship::items::bank::{BankItem, BankItemDetail};
 | 
			
		||||
use crate::ship::items::itemstateaction::{ItemStateAction, ItemAction};
 | 
			
		||||
use crate::ship::items::inventory::{InventoryItem, InventoryItemDetail};
 | 
			
		||||
use crate::ship::items::floor::{FloorItem, FloorItemDetail};
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										340
									
								
								src/ship/items/bank.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										340
									
								
								src/ship/items/bank.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,340 @@
 | 
			
		||||
use std::cmp::Ordering;
 | 
			
		||||
use libpso::character::character;
 | 
			
		||||
use crate::ship::items::ClientItemId;
 | 
			
		||||
use crate::entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, BankEntity, BankItemEntity, BankName};
 | 
			
		||||
use std::future::Future;
 | 
			
		||||
 | 
			
		||||
use crate::entity::character::CharacterEntityId;
 | 
			
		||||
use crate::ship::items::state::ItemStateError;
 | 
			
		||||
use crate::ship::items::state::{IndividualItemDetail, StackedItemDetail, AddItemResult};
 | 
			
		||||
use crate::ship::items::inventory::{InventoryItem, InventoryItemDetail};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#[derive(thiserror::Error, Debug)]
 | 
			
		||||
pub enum BankError {
 | 
			
		||||
    #[error("bank full")]
 | 
			
		||||
    BankFull,
 | 
			
		||||
    #[error("stack full")]
 | 
			
		||||
    StackFull,
 | 
			
		||||
    #[error("meseta full")]
 | 
			
		||||
    MesetaFull,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
pub enum BankItemDetail {
 | 
			
		||||
    Individual(IndividualItemDetail),
 | 
			
		||||
    Stacked(StackedItemDetail),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl BankItemDetail {
 | 
			
		||||
    fn stacked_mut(&mut self) -> Option<&mut StackedItemDetail> {
 | 
			
		||||
        match self {
 | 
			
		||||
            BankItemDetail::Stacked(sitem) => Some(sitem),
 | 
			
		||||
            _ => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn as_client_bytes(&self) -> [u8; 16] {
 | 
			
		||||
        match self {
 | 
			
		||||
            BankItemDetail::Individual(item) => {
 | 
			
		||||
                match &item.item {
 | 
			
		||||
                    ItemDetail::Weapon(w) => w.as_bytes(),
 | 
			
		||||
                    ItemDetail::Armor(a) => a.as_bytes(),
 | 
			
		||||
                    ItemDetail::Shield(s) => s.as_bytes(),
 | 
			
		||||
                    ItemDetail::Unit(u) => u.as_bytes(),
 | 
			
		||||
                    ItemDetail::Tool(t) => t.as_individual_bytes(),
 | 
			
		||||
                    ItemDetail::TechniqueDisk(d) => d.as_bytes(),
 | 
			
		||||
                    ItemDetail::Mag(m) => m.as_bytes(),
 | 
			
		||||
                    ItemDetail::ESWeapon(e) => e.as_bytes(),
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            BankItemDetail::Stacked(item) => {
 | 
			
		||||
                item.tool.as_stacked_bytes(item.entity_ids.len())
 | 
			
		||||
            },
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
pub struct BankItem {
 | 
			
		||||
    pub item_id: ClientItemId,
 | 
			
		||||
    pub item: BankItemDetail,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl BankItem {
 | 
			
		||||
    pub async fn with_entity_id<F, Fut, T>(&self, mut param: T, mut func: F) -> Result<T, ItemStateError>
 | 
			
		||||
    where
 | 
			
		||||
        F: FnMut(T, ItemEntityId) -> Fut,
 | 
			
		||||
        Fut: Future<Output=Result<T, ItemStateError>>,
 | 
			
		||||
    {
 | 
			
		||||
        match &self.item {
 | 
			
		||||
            BankItemDetail::Individual(individual_item) => {
 | 
			
		||||
                param = func(param, individual_item.entity_id).await?;
 | 
			
		||||
            },
 | 
			
		||||
            BankItemDetail::Stacked(stacked_item) => {
 | 
			
		||||
                for entity_id in &stacked_item.entity_ids {
 | 
			
		||||
                    param = func(param, *entity_id).await?;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        Ok(param)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
pub struct Bank(Vec<BankItem>);
 | 
			
		||||
 | 
			
		||||
impl Bank {
 | 
			
		||||
    pub fn new(items: Vec<BankItem>) -> Bank {
 | 
			
		||||
        Bank(items)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
pub struct BankState {
 | 
			
		||||
    pub character_id: CharacterEntityId,
 | 
			
		||||
    pub item_id_counter: u32,
 | 
			
		||||
    pub name: BankName,
 | 
			
		||||
    pub bank: Bank,
 | 
			
		||||
    pub meseta: Meseta,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl BankState {
 | 
			
		||||
    pub fn new(character_id: CharacterEntityId, name: BankName, mut bank: Bank, meseta: Meseta) -> BankState {
 | 
			
		||||
        bank.0.sort();
 | 
			
		||||
        BankState {
 | 
			
		||||
            character_id,
 | 
			
		||||
            item_id_counter: 0,
 | 
			
		||||
            name,
 | 
			
		||||
            bank,
 | 
			
		||||
            meseta,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn count(&self) -> usize {
 | 
			
		||||
        self.bank.0.len()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn initialize_item_ids(&mut self, base_item_id: u32) {
 | 
			
		||||
        for (i, item) in self.bank.0.iter_mut().enumerate() {
 | 
			
		||||
            item.item_id = ClientItemId(base_item_id + i as u32);
 | 
			
		||||
        }
 | 
			
		||||
        self.item_id_counter = base_item_id + self.bank.0.len() as u32;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn add_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> {
 | 
			
		||||
        if self.meseta.0 + amount > 999999 {
 | 
			
		||||
            return Err(ItemStateError::FullOfMeseta)
 | 
			
		||||
        }
 | 
			
		||||
        self.meseta.0 += amount;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn remove_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> {
 | 
			
		||||
        if amount > self.meseta.0 {
 | 
			
		||||
            return Err(ItemStateError::InvalidMesetaRemoval(amount))
 | 
			
		||||
        }
 | 
			
		||||
        self.meseta.0 -= amount;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn add_inventory_item(&mut self, item: InventoryItem) -> Result<AddItemResult, BankError> {
 | 
			
		||||
        match item.item {
 | 
			
		||||
            InventoryItemDetail::Individual(iitem) => {
 | 
			
		||||
                if self.bank.0.len() >= 30 {
 | 
			
		||||
                    Err(BankError::BankFull)
 | 
			
		||||
                }
 | 
			
		||||
                else {
 | 
			
		||||
                    self.bank.0.push(BankItem {
 | 
			
		||||
                        item_id: item.item_id,
 | 
			
		||||
                        item: BankItemDetail::Individual(iitem)
 | 
			
		||||
                    });
 | 
			
		||||
                    self.bank.0.sort();
 | 
			
		||||
                    Ok(AddItemResult::NewItem)
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            InventoryItemDetail::Stacked(sitem) => {
 | 
			
		||||
                let existing_stack = self.bank.0
 | 
			
		||||
                    .iter_mut()
 | 
			
		||||
                    .filter_map(|item| item.item.stacked_mut())
 | 
			
		||||
                    .find(|item| {
 | 
			
		||||
                        item.tool == sitem.tool
 | 
			
		||||
                    });
 | 
			
		||||
                match existing_stack {
 | 
			
		||||
                    Some(existing_stack) => {
 | 
			
		||||
                        if existing_stack.entity_ids.len() + sitem.entity_ids.len() > sitem.tool.max_stack() {
 | 
			
		||||
                            Err(BankError::StackFull)
 | 
			
		||||
                        }
 | 
			
		||||
                        else {
 | 
			
		||||
                            existing_stack.entity_ids.append(&mut sitem.entity_ids.clone());
 | 
			
		||||
                            Ok(AddItemResult::AddToStack)
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    None => {
 | 
			
		||||
                        if self.bank.0.len() >= 30 {
 | 
			
		||||
                            Err(BankError::BankFull)
 | 
			
		||||
                        }
 | 
			
		||||
                        else {
 | 
			
		||||
                            self.bank.0.push(BankItem {
 | 
			
		||||
                                item_id: item.item_id,
 | 
			
		||||
                                item: BankItemDetail::Stacked(sitem)
 | 
			
		||||
                            });
 | 
			
		||||
                            self.bank.0.sort();
 | 
			
		||||
                            Ok(AddItemResult::NewItem)
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn take_item(&mut self, item_id: &ClientItemId, amount: u32) -> Option<BankItem> {
 | 
			
		||||
        let idx = self.bank.0
 | 
			
		||||
            .iter()
 | 
			
		||||
            .position(|i| i.item_id == *item_id)?;
 | 
			
		||||
        match &mut self.bank.0[idx].item {
 | 
			
		||||
            BankItemDetail::Individual(_individual_item) => {
 | 
			
		||||
                Some(self.bank.0.remove(idx))
 | 
			
		||||
            },
 | 
			
		||||
            BankItemDetail::Stacked(stacked_item) => {
 | 
			
		||||
                let remove_all = match stacked_item.entity_ids.len().cmp(&(amount as usize)) {
 | 
			
		||||
                    Ordering::Equal => true,
 | 
			
		||||
                    Ordering::Greater => false,
 | 
			
		||||
                    Ordering::Less => return None,
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                if remove_all {
 | 
			
		||||
                    Some(self.bank.0.remove(idx))
 | 
			
		||||
                }
 | 
			
		||||
                else {
 | 
			
		||||
                    let entity_ids = stacked_item.entity_ids.drain(..(amount as usize)).collect();
 | 
			
		||||
                    self.item_id_counter += 1;
 | 
			
		||||
                    Some(BankItem {
 | 
			
		||||
                        item_id: ClientItemId(self.item_id_counter),
 | 
			
		||||
                        item: BankItemDetail::Stacked(StackedItemDetail {
 | 
			
		||||
                            entity_ids,
 | 
			
		||||
                            tool: stacked_item.tool,
 | 
			
		||||
                        })})
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn as_client_bank_items(&self) -> character::Bank {
 | 
			
		||||
        self.bank.0.iter()
 | 
			
		||||
            .enumerate()
 | 
			
		||||
            .fold(character::Bank::default(), |mut bank, (slot, item)| {
 | 
			
		||||
                bank.item_count = (slot + 1) as u32;
 | 
			
		||||
                let bytes = item.item.as_client_bytes();
 | 
			
		||||
                bank.items[slot].data1.copy_from_slice(&bytes[0..12]);
 | 
			
		||||
                bank.items[slot].data2.copy_from_slice(&bytes[12..16]);
 | 
			
		||||
                bank.items[slot].item_id = item.item_id.0;
 | 
			
		||||
 | 
			
		||||
                bank
 | 
			
		||||
            })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn as_client_bank_request(&self) -> Vec<character::BankItem> {
 | 
			
		||||
        self.bank.0.iter()
 | 
			
		||||
            .map(|item| {
 | 
			
		||||
                let bytes = item.item.as_client_bytes();
 | 
			
		||||
                let mut data1 = [0; 12];
 | 
			
		||||
                let mut data2 = [0; 4];
 | 
			
		||||
                data1.copy_from_slice(&bytes[0..12]);
 | 
			
		||||
                data2.copy_from_slice(&bytes[12..16]);
 | 
			
		||||
                let amount = match &item.item {
 | 
			
		||||
                    BankItemDetail::Individual(_individual_bank_item) => {
 | 
			
		||||
                        1
 | 
			
		||||
                    },
 | 
			
		||||
                    BankItemDetail::Stacked(stacked_bank_item) => {
 | 
			
		||||
                        stacked_bank_item.count()
 | 
			
		||||
                    },
 | 
			
		||||
                };
 | 
			
		||||
                character::BankItem {
 | 
			
		||||
                    data1,
 | 
			
		||||
                    data2,
 | 
			
		||||
                    item_id: item.item_id.0,
 | 
			
		||||
                    amount: amount as u16,
 | 
			
		||||
                    flags: 1,
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
            .collect()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn as_bank_entity(&self) -> BankEntity {
 | 
			
		||||
        BankEntity {
 | 
			
		||||
            items: self.bank.0.iter()
 | 
			
		||||
                .map(|item| {
 | 
			
		||||
                    match &item.item {
 | 
			
		||||
                        BankItemDetail::Individual(item) => {
 | 
			
		||||
                            BankItemEntity::Individual(ItemEntity {
 | 
			
		||||
                                id: item.entity_id,
 | 
			
		||||
                                item: item.item.clone(),
 | 
			
		||||
                            })
 | 
			
		||||
                        },
 | 
			
		||||
                        BankItemDetail::Stacked(items) => {
 | 
			
		||||
                            BankItemEntity::Stacked(items.entity_ids.iter()
 | 
			
		||||
                                                    .map(|id| {
 | 
			
		||||
                                                        ItemEntity {
 | 
			
		||||
                                                            id: *id,
 | 
			
		||||
                                                            item: ItemDetail::Tool(items.tool)
 | 
			
		||||
                                                        }
 | 
			
		||||
                                                    })
 | 
			
		||||
                                                    .collect())
 | 
			
		||||
                        },
 | 
			
		||||
                    }
 | 
			
		||||
                })
 | 
			
		||||
                .collect()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl std::cmp::PartialEq for BankItem {
 | 
			
		||||
    fn eq(&self, other: &BankItem) -> bool {
 | 
			
		||||
        let mut self_bytes = [0u8; 4];
 | 
			
		||||
        let mut other_bytes = [0u8; 4];
 | 
			
		||||
        self_bytes.copy_from_slice(&self.item.as_client_bytes()[0..4]);
 | 
			
		||||
        other_bytes.copy_from_slice(&other.item.as_client_bytes()[0..4]);
 | 
			
		||||
 | 
			
		||||
        let self_value = u32::from_be_bytes(self_bytes);
 | 
			
		||||
        let other_value = u32::from_be_bytes(other_bytes);
 | 
			
		||||
 | 
			
		||||
        self_value.eq(&other_value)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl std::cmp::Eq for BankItem {}
 | 
			
		||||
 | 
			
		||||
impl std::cmp::PartialOrd for BankItem {
 | 
			
		||||
    fn partial_cmp(&self, other: &BankItem) -> Option<std::cmp::Ordering> {
 | 
			
		||||
        let mut self_bytes = [0u8; 4];
 | 
			
		||||
        let mut other_bytes = [0u8; 4];
 | 
			
		||||
        self_bytes.copy_from_slice(&self.item.as_client_bytes()[0..4]);
 | 
			
		||||
        other_bytes.copy_from_slice(&other.item.as_client_bytes()[0..4]);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        let self_value = u32::from_be_bytes(self_bytes);
 | 
			
		||||
        let other_value = u32::from_be_bytes(other_bytes);
 | 
			
		||||
 | 
			
		||||
        self_value.partial_cmp(&other_value)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl std::cmp::Ord for BankItem {
 | 
			
		||||
    fn cmp(&self, other: &BankItem) -> std::cmp::Ordering {
 | 
			
		||||
        let mut self_bytes = [0u8; 4];
 | 
			
		||||
        let mut other_bytes = [0u8; 4];
 | 
			
		||||
        self_bytes.copy_from_slice(&self.item.as_client_bytes()[0..4]);
 | 
			
		||||
        other_bytes.copy_from_slice(&other.item.as_client_bytes()[0..4]);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        let self_value = u32::from_le_bytes(self_bytes);
 | 
			
		||||
        let other_value = u32::from_le_bytes(other_bytes);
 | 
			
		||||
 | 
			
		||||
        self_value.cmp(&other_value)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -4,6 +4,7 @@ pub mod apply_item;
 | 
			
		||||
pub mod itemstateaction;
 | 
			
		||||
pub mod inventory;
 | 
			
		||||
pub mod floor;
 | 
			
		||||
pub mod bank;
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize, derive_more::Display)]
 | 
			
		||||
pub struct ClientItemId(pub u32);
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,6 @@
 | 
			
		||||
use std::cmp::Ordering;
 | 
			
		||||
use std::collections::HashMap;
 | 
			
		||||
use libpso::character::character;
 | 
			
		||||
use crate::ship::items::ClientItemId;
 | 
			
		||||
use crate::entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, InventoryItemEntity, BankEntity, BankItemEntity, BankName};
 | 
			
		||||
use std::future::Future;
 | 
			
		||||
use crate::entity::item::{ItemEntityId, ItemDetail, ItemEntity, InventoryItemEntity, BankItemEntity, BankName};
 | 
			
		||||
 | 
			
		||||
use crate::ship::location::{AreaClient, RoomId};
 | 
			
		||||
use crate::entity::character::{CharacterEntity, CharacterEntityId};
 | 
			
		||||
@ -14,8 +11,8 @@ use crate::entity::item::mag::Mag;
 | 
			
		||||
use crate::ship::drops::ItemDrop;
 | 
			
		||||
 | 
			
		||||
use crate::ship::items::inventory::{Inventory, InventoryItem, InventoryItemDetail, InventoryError, InventoryState};
 | 
			
		||||
 | 
			
		||||
use crate::ship::items::floor::{FloorState, FloorItem, LocalFloor, SharedFloor, FloorType};
 | 
			
		||||
use crate::ship::items::bank::{Bank, BankState, BankItem, BankItemDetail, BankError};
 | 
			
		||||
 | 
			
		||||
// TODO: Commit trait that ItemStateProxy and EntityTransaction implement that .commit requires and acts on upon everything succeeding (like 3 less lines of code!)
 | 
			
		||||
 | 
			
		||||
@ -136,78 +133,7 @@ impl StackedItemDetail {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
pub enum BankItemDetail {
 | 
			
		||||
    Individual(IndividualItemDetail),
 | 
			
		||||
    Stacked(StackedItemDetail),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl BankItemDetail {
 | 
			
		||||
    fn stacked_mut(&mut self) -> Option<&mut StackedItemDetail> {
 | 
			
		||||
        match self {
 | 
			
		||||
            BankItemDetail::Stacked(sitem) => Some(sitem),
 | 
			
		||||
            _ => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn as_client_bytes(&self) -> [u8; 16] {
 | 
			
		||||
        match self {
 | 
			
		||||
            BankItemDetail::Individual(item) => {
 | 
			
		||||
                match &item.item {
 | 
			
		||||
                    ItemDetail::Weapon(w) => w.as_bytes(),
 | 
			
		||||
                    ItemDetail::Armor(a) => a.as_bytes(),
 | 
			
		||||
                    ItemDetail::Shield(s) => s.as_bytes(),
 | 
			
		||||
                    ItemDetail::Unit(u) => u.as_bytes(),
 | 
			
		||||
                    ItemDetail::Tool(t) => t.as_individual_bytes(),
 | 
			
		||||
                    ItemDetail::TechniqueDisk(d) => d.as_bytes(),
 | 
			
		||||
                    ItemDetail::Mag(m) => m.as_bytes(),
 | 
			
		||||
                    ItemDetail::ESWeapon(e) => e.as_bytes(),
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            BankItemDetail::Stacked(item) => {
 | 
			
		||||
                item.tool.as_stacked_bytes(item.entity_ids.len())
 | 
			
		||||
            },
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
pub struct BankItem {
 | 
			
		||||
    pub item_id: ClientItemId,
 | 
			
		||||
    pub item: BankItemDetail,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl BankItem {
 | 
			
		||||
    pub async fn with_entity_id<F, Fut, T>(&self, mut param: T, mut func: F) -> Result<T, ItemStateError>
 | 
			
		||||
    where
 | 
			
		||||
        F: FnMut(T, ItemEntityId) -> Fut,
 | 
			
		||||
        Fut: Future<Output=Result<T, ItemStateError>>,
 | 
			
		||||
    {
 | 
			
		||||
        match &self.item {
 | 
			
		||||
            BankItemDetail::Individual(individual_item) => {
 | 
			
		||||
                param = func(param, individual_item.entity_id).await?;
 | 
			
		||||
            },
 | 
			
		||||
            BankItemDetail::Stacked(stacked_item) => {
 | 
			
		||||
                for entity_id in &stacked_item.entity_ids {
 | 
			
		||||
                    param = func(param, *entity_id).await?;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        Ok(param)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#[derive(thiserror::Error, Debug)]
 | 
			
		||||
pub enum BankError {
 | 
			
		||||
    #[error("bank full")]
 | 
			
		||||
    BankFull,
 | 
			
		||||
    #[error("stack full")]
 | 
			
		||||
    StackFull,
 | 
			
		||||
    #[error("meseta full")]
 | 
			
		||||
    MesetaFull,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
pub enum AddItemResult {
 | 
			
		||||
@ -216,254 +142,6 @@ pub enum AddItemResult {
 | 
			
		||||
    Meseta,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
pub struct Bank(Vec<BankItem>);
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
pub struct BankState {
 | 
			
		||||
    character_id: CharacterEntityId,
 | 
			
		||||
    item_id_counter: u32,
 | 
			
		||||
    pub name: BankName,
 | 
			
		||||
    bank: Bank,
 | 
			
		||||
    pub meseta: Meseta,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl BankState {
 | 
			
		||||
    pub fn new(character_id: CharacterEntityId, name: BankName, mut bank: Bank, meseta: Meseta) -> BankState {
 | 
			
		||||
        bank.0.sort();
 | 
			
		||||
        BankState {
 | 
			
		||||
            character_id,
 | 
			
		||||
            item_id_counter: 0,
 | 
			
		||||
            name,
 | 
			
		||||
            bank,
 | 
			
		||||
            meseta,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn count(&self) -> usize {
 | 
			
		||||
        self.bank.0.len()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn initialize_item_ids(&mut self, base_item_id: u32) {
 | 
			
		||||
        for (i, item) in self.bank.0.iter_mut().enumerate() {
 | 
			
		||||
            item.item_id = ClientItemId(base_item_id + i as u32);
 | 
			
		||||
        }
 | 
			
		||||
        self.item_id_counter = base_item_id + self.bank.0.len() as u32;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn add_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> {
 | 
			
		||||
        if self.meseta.0 + amount > 999999 {
 | 
			
		||||
            return Err(ItemStateError::FullOfMeseta)
 | 
			
		||||
        }
 | 
			
		||||
        self.meseta.0 += amount;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn remove_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> {
 | 
			
		||||
        if amount > self.meseta.0 {
 | 
			
		||||
            return Err(ItemStateError::InvalidMesetaRemoval(amount))
 | 
			
		||||
        }
 | 
			
		||||
        self.meseta.0 -= amount;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn add_inventory_item(&mut self, item: InventoryItem) -> Result<AddItemResult, BankError> {
 | 
			
		||||
        match item.item {
 | 
			
		||||
            InventoryItemDetail::Individual(iitem) => {
 | 
			
		||||
                if self.bank.0.len() >= 30 {
 | 
			
		||||
                    Err(BankError::BankFull)
 | 
			
		||||
                }
 | 
			
		||||
                else {
 | 
			
		||||
                    self.bank.0.push(BankItem {
 | 
			
		||||
                        item_id: item.item_id,
 | 
			
		||||
                        item: BankItemDetail::Individual(iitem)
 | 
			
		||||
                    });
 | 
			
		||||
                    self.bank.0.sort();
 | 
			
		||||
                    Ok(AddItemResult::NewItem)
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            InventoryItemDetail::Stacked(sitem) => {
 | 
			
		||||
                let existing_stack = self.bank.0
 | 
			
		||||
                    .iter_mut()
 | 
			
		||||
                    .filter_map(|item| item.item.stacked_mut())
 | 
			
		||||
                    .find(|item| {
 | 
			
		||||
                        item.tool == sitem.tool
 | 
			
		||||
                    });
 | 
			
		||||
                match existing_stack {
 | 
			
		||||
                    Some(existing_stack) => {
 | 
			
		||||
                        if existing_stack.entity_ids.len() + sitem.entity_ids.len() > sitem.tool.max_stack() {
 | 
			
		||||
                            Err(BankError::StackFull)
 | 
			
		||||
                        }
 | 
			
		||||
                        else {
 | 
			
		||||
                            existing_stack.entity_ids.append(&mut sitem.entity_ids.clone());
 | 
			
		||||
                            Ok(AddItemResult::AddToStack)
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    None => {
 | 
			
		||||
                        if self.bank.0.len() >= 30 {
 | 
			
		||||
                            Err(BankError::BankFull)
 | 
			
		||||
                        }
 | 
			
		||||
                        else {
 | 
			
		||||
                            self.bank.0.push(BankItem {
 | 
			
		||||
                                item_id: item.item_id,
 | 
			
		||||
                                item: BankItemDetail::Stacked(sitem)
 | 
			
		||||
                            });
 | 
			
		||||
                            self.bank.0.sort();
 | 
			
		||||
                            Ok(AddItemResult::NewItem)
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn take_item(&mut self, item_id: &ClientItemId, amount: u32) -> Option<BankItem> {
 | 
			
		||||
        let idx = self.bank.0
 | 
			
		||||
            .iter()
 | 
			
		||||
            .position(|i| i.item_id == *item_id)?;
 | 
			
		||||
        match &mut self.bank.0[idx].item {
 | 
			
		||||
            BankItemDetail::Individual(_individual_item) => {
 | 
			
		||||
                Some(self.bank.0.remove(idx))
 | 
			
		||||
            },
 | 
			
		||||
            BankItemDetail::Stacked(stacked_item) => {
 | 
			
		||||
                let remove_all = match stacked_item.entity_ids.len().cmp(&(amount as usize)) {
 | 
			
		||||
                    Ordering::Equal => true,
 | 
			
		||||
                    Ordering::Greater => false,
 | 
			
		||||
                    Ordering::Less => return None,
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                if remove_all {
 | 
			
		||||
                    Some(self.bank.0.remove(idx))
 | 
			
		||||
                }
 | 
			
		||||
                else {
 | 
			
		||||
                    let entity_ids = stacked_item.entity_ids.drain(..(amount as usize)).collect();
 | 
			
		||||
                    self.item_id_counter += 1;
 | 
			
		||||
                    Some(BankItem {
 | 
			
		||||
                        item_id: ClientItemId(self.item_id_counter),
 | 
			
		||||
                        item: BankItemDetail::Stacked(StackedItemDetail {
 | 
			
		||||
                            entity_ids,
 | 
			
		||||
                            tool: stacked_item.tool,
 | 
			
		||||
                        })})
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn as_client_bank_items(&self) -> character::Bank {
 | 
			
		||||
        self.bank.0.iter()
 | 
			
		||||
            .enumerate()
 | 
			
		||||
            .fold(character::Bank::default(), |mut bank, (slot, item)| {
 | 
			
		||||
                bank.item_count = (slot + 1) as u32;
 | 
			
		||||
                let bytes = item.item.as_client_bytes();
 | 
			
		||||
                bank.items[slot].data1.copy_from_slice(&bytes[0..12]);
 | 
			
		||||
                bank.items[slot].data2.copy_from_slice(&bytes[12..16]);
 | 
			
		||||
                bank.items[slot].item_id = item.item_id.0;
 | 
			
		||||
 | 
			
		||||
                bank
 | 
			
		||||
            })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn as_client_bank_request(&self) -> Vec<character::BankItem> {
 | 
			
		||||
        self.bank.0.iter()
 | 
			
		||||
            .map(|item| {
 | 
			
		||||
                let bytes = item.item.as_client_bytes();
 | 
			
		||||
                let mut data1 = [0; 12];
 | 
			
		||||
                let mut data2 = [0; 4];
 | 
			
		||||
                data1.copy_from_slice(&bytes[0..12]);
 | 
			
		||||
                data2.copy_from_slice(&bytes[12..16]);
 | 
			
		||||
                let amount = match &item.item {
 | 
			
		||||
                    BankItemDetail::Individual(_individual_bank_item) => {
 | 
			
		||||
                        1
 | 
			
		||||
                    },
 | 
			
		||||
                    BankItemDetail::Stacked(stacked_bank_item) => {
 | 
			
		||||
                        stacked_bank_item.count()
 | 
			
		||||
                    },
 | 
			
		||||
                };
 | 
			
		||||
                character::BankItem {
 | 
			
		||||
                    data1,
 | 
			
		||||
                    data2,
 | 
			
		||||
                    item_id: item.item_id.0,
 | 
			
		||||
                    amount: amount as u16,
 | 
			
		||||
                    flags: 1,
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
            .collect()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn as_bank_entity(&self) -> BankEntity {
 | 
			
		||||
        BankEntity {
 | 
			
		||||
            items: self.bank.0.iter()
 | 
			
		||||
                .map(|item| {
 | 
			
		||||
                    match &item.item {
 | 
			
		||||
                        BankItemDetail::Individual(item) => {
 | 
			
		||||
                            BankItemEntity::Individual(ItemEntity {
 | 
			
		||||
                                id: item.entity_id,
 | 
			
		||||
                                item: item.item.clone(),
 | 
			
		||||
                            })
 | 
			
		||||
                        },
 | 
			
		||||
                        BankItemDetail::Stacked(items) => {
 | 
			
		||||
                            BankItemEntity::Stacked(items.entity_ids.iter()
 | 
			
		||||
                                                    .map(|id| {
 | 
			
		||||
                                                        ItemEntity {
 | 
			
		||||
                                                            id: *id,
 | 
			
		||||
                                                            item: ItemDetail::Tool(items.tool)
 | 
			
		||||
                                                        }
 | 
			
		||||
                                                    })
 | 
			
		||||
                                                    .collect())
 | 
			
		||||
                        },
 | 
			
		||||
                    }
 | 
			
		||||
                })
 | 
			
		||||
                .collect()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl std::cmp::PartialEq for BankItem {
 | 
			
		||||
    fn eq(&self, other: &BankItem) -> bool {
 | 
			
		||||
        let mut self_bytes = [0u8; 4];
 | 
			
		||||
        let mut other_bytes = [0u8; 4];
 | 
			
		||||
        self_bytes.copy_from_slice(&self.item.as_client_bytes()[0..4]);
 | 
			
		||||
        other_bytes.copy_from_slice(&other.item.as_client_bytes()[0..4]);
 | 
			
		||||
 | 
			
		||||
        let self_value = u32::from_be_bytes(self_bytes);
 | 
			
		||||
        let other_value = u32::from_be_bytes(other_bytes);
 | 
			
		||||
 | 
			
		||||
        self_value.eq(&other_value)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl std::cmp::Eq for BankItem {}
 | 
			
		||||
 | 
			
		||||
impl std::cmp::PartialOrd for BankItem {
 | 
			
		||||
    fn partial_cmp(&self, other: &BankItem) -> Option<std::cmp::Ordering> {
 | 
			
		||||
        let mut self_bytes = [0u8; 4];
 | 
			
		||||
        let mut other_bytes = [0u8; 4];
 | 
			
		||||
        self_bytes.copy_from_slice(&self.item.as_client_bytes()[0..4]);
 | 
			
		||||
        other_bytes.copy_from_slice(&other.item.as_client_bytes()[0..4]);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        let self_value = u32::from_be_bytes(self_bytes);
 | 
			
		||||
        let other_value = u32::from_be_bytes(other_bytes);
 | 
			
		||||
 | 
			
		||||
        self_value.partial_cmp(&other_value)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl std::cmp::Ord for BankItem {
 | 
			
		||||
    fn cmp(&self, other: &BankItem) -> std::cmp::Ordering {
 | 
			
		||||
        let mut self_bytes = [0u8; 4];
 | 
			
		||||
        let mut other_bytes = [0u8; 4];
 | 
			
		||||
        self_bytes.copy_from_slice(&self.item.as_client_bytes()[0..4]);
 | 
			
		||||
        other_bytes.copy_from_slice(&other.item.as_client_bytes()[0..4]);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        let self_value = u32::from_le_bytes(self_bytes);
 | 
			
		||||
        let other_value = u32::from_le_bytes(other_bytes);
 | 
			
		||||
 | 
			
		||||
        self_value.cmp(&other_value)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
pub struct ItemState {
 | 
			
		||||
    character_inventory: HashMap<CharacterEntityId, InventoryState>,
 | 
			
		||||
@ -582,7 +260,7 @@ impl ItemState {
 | 
			
		||||
            .collect::<Result<Vec<_>, _>>()?;
 | 
			
		||||
 | 
			
		||||
        let bank_meseta = entity_gateway.get_bank_meseta(&character.id, &BankName("".into())).await?;
 | 
			
		||||
        let bank_state = BankState::new(character.id, BankName("".into()), Bank(bank_items), bank_meseta);
 | 
			
		||||
        let bank_state = BankState::new(character.id, BankName("".into()), Bank::new(bank_items), bank_meseta);
 | 
			
		||||
 | 
			
		||||
        self.character_inventory.insert(character.id, inventory_state);
 | 
			
		||||
        self.character_bank.insert(character.id, bank_state);
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@ use crate::ship::ship::{ShipError};
 | 
			
		||||
use crate::ship::items::ClientItemId;
 | 
			
		||||
use crate::ship::items::inventory::InventoryItem;
 | 
			
		||||
use crate::ship::items::state::IndividualItemDetail;
 | 
			
		||||
use crate::ship::items::state::BankState;
 | 
			
		||||
use crate::ship::items::bank::BankState;
 | 
			
		||||
use crate::ship::items::floor::FloorItem;
 | 
			
		||||
use crate::ship::location::AreaClient;
 | 
			
		||||
use std::convert::TryInto;
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user