mirror of
https://github.com/redstrate/Kawari.git
synced 2025-05-06 04:37:46 +00:00
Overhaul how we send inventory packets again
I wanted to add armory chest support but the current state of the inventory was a little frustrating. Adding new containers was too difficult, so I made the system *even more* generic and easier to use. I have also split it up into it's own module with a nicer file layout. Oh yeah, and armory chest works too now.
This commit is contained in:
parent
e7fb661244
commit
8d384c4bd0
16 changed files with 486 additions and 416 deletions
|
@ -7,6 +7,7 @@ use kawari::common::workdefinitions::{CharaMake, RemakeMode};
|
||||||
use kawari::common::{GameData, ObjectId, timestamp_secs};
|
use kawari::common::{GameData, ObjectId, timestamp_secs};
|
||||||
use kawari::common::{Position, determine_initial_starting_zone};
|
use kawari::common::{Position, determine_initial_starting_zone};
|
||||||
use kawari::config::get_config;
|
use kawari::config::get_config;
|
||||||
|
use kawari::inventory::{Inventory, Item};
|
||||||
use kawari::ipc::chat::{ServerChatIpcData, ServerChatIpcSegment};
|
use kawari::ipc::chat::{ServerChatIpcData, ServerChatIpcSegment};
|
||||||
use kawari::ipc::kawari::{CustomIpcData, CustomIpcSegment, CustomIpcType};
|
use kawari::ipc::kawari::{CustomIpcData, CustomIpcSegment, CustomIpcType};
|
||||||
use kawari::ipc::zone::{
|
use kawari::ipc::zone::{
|
||||||
|
@ -24,8 +25,8 @@ use kawari::packet::{
|
||||||
send_keep_alive, send_packet,
|
send_keep_alive, send_packet,
|
||||||
};
|
};
|
||||||
use kawari::world::{
|
use kawari::world::{
|
||||||
Actor, ClientHandle, ClientId, EffectsBuilder, FromServer, Inventory, Item, LuaPlayer,
|
Actor, ClientHandle, ClientId, EffectsBuilder, FromServer, LuaPlayer, PlayerData, ServerHandle,
|
||||||
PlayerData, ServerHandle, StatusEffects, ToServer, WorldDatabase,
|
StatusEffects, ToServer, WorldDatabase,
|
||||||
};
|
};
|
||||||
use kawari::world::{ChatHandler, Zone, ZoneConnection};
|
use kawari::world::{ChatHandler, Zone, ZoneConnection};
|
||||||
|
|
||||||
|
|
82
src/inventory/equipped.rs
Normal file
82
src/inventory/equipped.rs
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use super::{Item, Storage};
|
||||||
|
|
||||||
|
#[derive(Default, Clone, Copy, Deserialize, Serialize, Debug)]
|
||||||
|
pub struct EquippedStorage {
|
||||||
|
pub main_hand: Item,
|
||||||
|
pub off_hand: Item,
|
||||||
|
pub head: Item,
|
||||||
|
pub body: Item,
|
||||||
|
pub hands: Item,
|
||||||
|
pub belt: Item,
|
||||||
|
pub legs: Item,
|
||||||
|
pub feet: Item,
|
||||||
|
pub ears: Item,
|
||||||
|
pub neck: Item,
|
||||||
|
pub wrists: Item,
|
||||||
|
pub right_ring: Item,
|
||||||
|
pub left_ring: Item,
|
||||||
|
pub soul_crystal: Item,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Storage for EquippedStorage {
|
||||||
|
fn max_slots(&self) -> u32 {
|
||||||
|
14
|
||||||
|
}
|
||||||
|
|
||||||
|
fn num_items(&self) -> u32 {
|
||||||
|
self.main_hand.quantity
|
||||||
|
+ self.off_hand.quantity
|
||||||
|
+ self.head.quantity
|
||||||
|
+ self.body.quantity
|
||||||
|
+ self.hands.quantity
|
||||||
|
+ self.legs.quantity
|
||||||
|
+ self.feet.quantity
|
||||||
|
+ self.ears.quantity
|
||||||
|
+ self.neck.quantity
|
||||||
|
+ self.wrists.quantity
|
||||||
|
+ self.right_ring.quantity
|
||||||
|
+ self.left_ring.quantity
|
||||||
|
+ self.soul_crystal.quantity
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_slot_mut(&mut self, index: u16) -> &mut Item {
|
||||||
|
match index {
|
||||||
|
0 => &mut self.main_hand,
|
||||||
|
1 => &mut self.off_hand,
|
||||||
|
2 => &mut self.head,
|
||||||
|
3 => &mut self.body,
|
||||||
|
4 => &mut self.hands,
|
||||||
|
6 => &mut self.legs,
|
||||||
|
7 => &mut self.feet,
|
||||||
|
8 => &mut self.ears,
|
||||||
|
9 => &mut self.neck,
|
||||||
|
10 => &mut self.wrists,
|
||||||
|
11 => &mut self.right_ring,
|
||||||
|
12 => &mut self.left_ring,
|
||||||
|
13 => &mut self.soul_crystal,
|
||||||
|
_ => panic!("{} is not a valid src_container_index?!?", index),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_slot(&self, index: u16) -> &Item {
|
||||||
|
match index {
|
||||||
|
0 => &self.main_hand,
|
||||||
|
1 => &self.off_hand,
|
||||||
|
2 => &self.head,
|
||||||
|
3 => &self.body,
|
||||||
|
4 => &self.hands,
|
||||||
|
5 => &self.belt,
|
||||||
|
6 => &self.legs,
|
||||||
|
7 => &self.feet,
|
||||||
|
8 => &self.ears,
|
||||||
|
9 => &self.neck,
|
||||||
|
10 => &self.wrists,
|
||||||
|
11 => &self.right_ring,
|
||||||
|
12 => &self.left_ring,
|
||||||
|
13 => &self.soul_crystal,
|
||||||
|
_ => panic!("{} is not a valid src_container_index?!?", index),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
34
src/inventory/generic.rs
Normal file
34
src/inventory/generic.rs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use super::{Item, Storage};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
|
pub struct GenericStorage<const N: usize> {
|
||||||
|
pub slots: Vec<Item>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> GenericStorage<N> {
|
||||||
|
pub fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
slots: vec![Item::default(); N],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> Storage for GenericStorage<N> {
|
||||||
|
fn max_slots(&self) -> u32 {
|
||||||
|
N as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn num_items(&self) -> u32 {
|
||||||
|
self.slots.iter().filter(|item| item.quantity > 0).count() as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_slot_mut(&mut self, index: u16) -> &mut Item {
|
||||||
|
self.slots.get_mut(index as usize).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_slot(&self, index: u16) -> &Item {
|
||||||
|
self.slots.get(index as usize).unwrap()
|
||||||
|
}
|
||||||
|
}
|
14
src/inventory/item.rs
Normal file
14
src/inventory/item.rs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
/// Represents an item, or if the quanity is zero an empty slot.
|
||||||
|
#[derive(Default, Copy, Clone, Serialize, Deserialize, Debug)]
|
||||||
|
pub struct Item {
|
||||||
|
pub quantity: u32,
|
||||||
|
pub id: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Item {
|
||||||
|
pub fn new(quantity: u32, id: u32) -> Self {
|
||||||
|
Self { quantity, id }
|
||||||
|
}
|
||||||
|
}
|
293
src/inventory/mod.rs
Normal file
293
src/inventory/mod.rs
Normal file
|
@ -0,0 +1,293 @@
|
||||||
|
use physis::common::Language;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::common::GameData;
|
||||||
|
|
||||||
|
use crate::ipc::zone::InventoryModify;
|
||||||
|
|
||||||
|
mod equipped;
|
||||||
|
pub use equipped::EquippedStorage;
|
||||||
|
|
||||||
|
mod generic;
|
||||||
|
pub use generic::GenericStorage;
|
||||||
|
|
||||||
|
mod item;
|
||||||
|
pub use item::Item;
|
||||||
|
|
||||||
|
mod storage;
|
||||||
|
pub use storage::{ContainerType, Storage};
|
||||||
|
|
||||||
|
const MAX_NORMAL_STORAGE: usize = 35;
|
||||||
|
const MAX_LARGE_STORAGE: usize = 50;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
|
pub struct Inventory {
|
||||||
|
pub equipped: EquippedStorage,
|
||||||
|
pub pages: [GenericStorage<MAX_NORMAL_STORAGE>; 4],
|
||||||
|
pub armoury_main_hand: GenericStorage<MAX_LARGE_STORAGE>,
|
||||||
|
pub armoury_head: GenericStorage<MAX_NORMAL_STORAGE>,
|
||||||
|
pub armoury_body: GenericStorage<MAX_NORMAL_STORAGE>,
|
||||||
|
pub armoury_hands: GenericStorage<MAX_NORMAL_STORAGE>,
|
||||||
|
pub armoury_legs: GenericStorage<MAX_NORMAL_STORAGE>,
|
||||||
|
pub armoury_feet: GenericStorage<MAX_NORMAL_STORAGE>,
|
||||||
|
pub armoury_off_hand: GenericStorage<MAX_NORMAL_STORAGE>,
|
||||||
|
pub armoury_earring: GenericStorage<MAX_NORMAL_STORAGE>,
|
||||||
|
pub armoury_necklace: GenericStorage<MAX_NORMAL_STORAGE>,
|
||||||
|
pub armoury_bracelet: GenericStorage<MAX_NORMAL_STORAGE>,
|
||||||
|
pub armoury_rings: GenericStorage<MAX_LARGE_STORAGE>,
|
||||||
|
pub armoury_soul_crystal: GenericStorage<MAX_NORMAL_STORAGE>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Inventory {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
equipped: EquippedStorage::default(),
|
||||||
|
pages: std::array::from_fn(|_| GenericStorage::default()),
|
||||||
|
armoury_main_hand: GenericStorage::default(),
|
||||||
|
armoury_head: GenericStorage::default(),
|
||||||
|
armoury_body: GenericStorage::default(),
|
||||||
|
armoury_hands: GenericStorage::default(),
|
||||||
|
armoury_legs: GenericStorage::default(),
|
||||||
|
armoury_feet: GenericStorage::default(),
|
||||||
|
armoury_off_hand: GenericStorage::default(),
|
||||||
|
armoury_earring: GenericStorage::default(),
|
||||||
|
armoury_necklace: GenericStorage::default(),
|
||||||
|
armoury_bracelet: GenericStorage::default(),
|
||||||
|
armoury_rings: GenericStorage::default(),
|
||||||
|
armoury_soul_crystal: GenericStorage::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> IntoIterator for &'a Inventory {
|
||||||
|
type Item = (ContainerType, &'a dyn Storage);
|
||||||
|
type IntoIter = InventoryIterator<'a>;
|
||||||
|
fn into_iter(self) -> InventoryIterator<'a> {
|
||||||
|
InventoryIterator {
|
||||||
|
inventory: self,
|
||||||
|
curr: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct InventoryIterator<'a> {
|
||||||
|
inventory: &'a Inventory,
|
||||||
|
curr: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for InventoryIterator<'a> {
|
||||||
|
type Item = (ContainerType, &'a dyn Storage);
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let curr = self.curr;
|
||||||
|
self.curr += 1;
|
||||||
|
|
||||||
|
if curr >= 17 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let container_type = match curr {
|
||||||
|
// inventory
|
||||||
|
0 => ContainerType::Inventory0,
|
||||||
|
1 => ContainerType::Inventory1,
|
||||||
|
2 => ContainerType::Inventory2,
|
||||||
|
3 => ContainerType::Inventory3,
|
||||||
|
|
||||||
|
// armory
|
||||||
|
4 => ContainerType::ArmoryOffWeapon,
|
||||||
|
5 => ContainerType::ArmoryHead,
|
||||||
|
6 => ContainerType::ArmoryBody,
|
||||||
|
7 => ContainerType::ArmoryHand,
|
||||||
|
8 => ContainerType::ArmoryLeg,
|
||||||
|
9 => ContainerType::ArmoryFoot,
|
||||||
|
10 => ContainerType::ArmoryEarring,
|
||||||
|
11 => ContainerType::ArmoryNeck,
|
||||||
|
12 => ContainerType::ArmoryWrist,
|
||||||
|
13 => ContainerType::ArmoryRing,
|
||||||
|
14 => ContainerType::ArmorySoulCrystal,
|
||||||
|
15 => ContainerType::ArmoryWeapon,
|
||||||
|
|
||||||
|
// equipped
|
||||||
|
16 => ContainerType::Equipped,
|
||||||
|
_ => panic!("Inventory iterator invalid!"),
|
||||||
|
};
|
||||||
|
|
||||||
|
Some((
|
||||||
|
container_type,
|
||||||
|
self.inventory.get_container(&container_type),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Inventory {
|
||||||
|
/// Equip the starting items for a given race
|
||||||
|
pub fn equip_racial_items(&mut self, race_id: u8, gender: u8, game_data: &mut GameData) {
|
||||||
|
let exh = game_data.game_data.read_excel_sheet_header("Race").unwrap();
|
||||||
|
let exd = game_data
|
||||||
|
.game_data
|
||||||
|
.read_excel_sheet("Race", &exh, Language::English, 0)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let world_row = &exd.read_row(&exh, race_id as u32).unwrap()[0];
|
||||||
|
|
||||||
|
let get_column = |column_index: usize| {
|
||||||
|
let physis::exd::ColumnData::Int32(item_id) = &world_row.data[column_index] else {
|
||||||
|
panic!("Unexpected type!");
|
||||||
|
};
|
||||||
|
|
||||||
|
*item_id
|
||||||
|
};
|
||||||
|
|
||||||
|
if gender == 0 {
|
||||||
|
self.equipped.body = Item::new(1, get_column(2) as u32);
|
||||||
|
self.equipped.hands = Item::new(1, get_column(3) as u32);
|
||||||
|
self.equipped.legs = Item::new(1, get_column(4) as u32);
|
||||||
|
self.equipped.feet = Item::new(1, get_column(5) as u32);
|
||||||
|
} else {
|
||||||
|
self.equipped.body = Item::new(1, get_column(6) as u32);
|
||||||
|
self.equipped.hands = Item::new(1, get_column(7) as u32);
|
||||||
|
self.equipped.legs = Item::new(1, get_column(8) as u32);
|
||||||
|
self.equipped.feet = Item::new(1, get_column(9) as u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: don't hardcode
|
||||||
|
self.equipped.main_hand = Item::new(1, 0x00000641);
|
||||||
|
self.equipped.ears = Item::new(1, 0x00003b1b);
|
||||||
|
self.equipped.neck = Item::new(1, 0x00003b1a);
|
||||||
|
self.equipped.wrists = Item::new(1, 0x00003b1c);
|
||||||
|
self.equipped.right_ring = Item::new(1, 0x0000114a);
|
||||||
|
self.equipped.left_ring = Item::new(1, 0x00003b1d);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn process_action(&mut self, action: &InventoryModify) {
|
||||||
|
if action.operation_type == 78 {
|
||||||
|
// discard
|
||||||
|
let src_container = self.get_container_mut(&action.src_storage_id);
|
||||||
|
let src_slot = src_container.get_slot_mut(action.src_container_index);
|
||||||
|
*src_slot = Item::default();
|
||||||
|
} else {
|
||||||
|
// NOTE: only swaps items for now
|
||||||
|
|
||||||
|
let src_item;
|
||||||
|
// get the source item
|
||||||
|
{
|
||||||
|
let src_container = self.get_container_mut(&action.src_storage_id);
|
||||||
|
let src_slot = src_container.get_slot_mut(action.src_container_index);
|
||||||
|
src_item = *src_slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
let dst_item;
|
||||||
|
// move into dst item
|
||||||
|
{
|
||||||
|
let dst_container = self.get_container_mut(&action.dst_storage_id);
|
||||||
|
let dst_slot = dst_container.get_slot_mut(action.dst_container_index);
|
||||||
|
|
||||||
|
dst_item = *dst_slot;
|
||||||
|
dst_slot.clone_from(&src_item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// move dst item into src slot
|
||||||
|
{
|
||||||
|
let src_container = self.get_container_mut(&action.src_storage_id);
|
||||||
|
let src_slot = src_container.get_slot_mut(action.src_container_index);
|
||||||
|
src_slot.clone_from(&dst_item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_in_next_free_slot(&mut self, item: Item) {
|
||||||
|
for page in &mut self.pages {
|
||||||
|
for slot in &mut page.slots {
|
||||||
|
if slot.quantity == 0 {
|
||||||
|
slot.clone_from(&item);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_container_mut(&mut self, container_type: &ContainerType) -> &mut dyn Storage {
|
||||||
|
match container_type {
|
||||||
|
ContainerType::Inventory0 => &mut self.pages[0],
|
||||||
|
ContainerType::Inventory1 => &mut self.pages[1],
|
||||||
|
ContainerType::Inventory2 => &mut self.pages[2],
|
||||||
|
ContainerType::Inventory3 => &mut self.pages[3],
|
||||||
|
ContainerType::Equipped => &mut self.equipped,
|
||||||
|
ContainerType::ArmoryOffWeapon => &mut self.armoury_off_hand,
|
||||||
|
ContainerType::ArmoryHead => &mut self.armoury_head,
|
||||||
|
ContainerType::ArmoryBody => &mut self.armoury_body,
|
||||||
|
ContainerType::ArmoryHand => &mut self.armoury_hands,
|
||||||
|
ContainerType::ArmoryLeg => &mut self.armoury_legs,
|
||||||
|
ContainerType::ArmoryFoot => &mut self.armoury_feet,
|
||||||
|
ContainerType::ArmoryEarring => &mut self.armoury_earring,
|
||||||
|
ContainerType::ArmoryNeck => &mut self.armoury_necklace,
|
||||||
|
ContainerType::ArmoryWrist => &mut self.armoury_bracelet,
|
||||||
|
ContainerType::ArmoryRing => &mut self.armoury_rings,
|
||||||
|
ContainerType::ArmorySoulCrystal => &mut self.armoury_soul_crystal,
|
||||||
|
ContainerType::ArmoryWeapon => &mut self.armoury_main_hand,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_container(&self, container_type: &ContainerType) -> &dyn Storage {
|
||||||
|
match container_type {
|
||||||
|
ContainerType::Inventory0 => &self.pages[0],
|
||||||
|
ContainerType::Inventory1 => &self.pages[1],
|
||||||
|
ContainerType::Inventory2 => &self.pages[2],
|
||||||
|
ContainerType::Inventory3 => &self.pages[3],
|
||||||
|
ContainerType::Equipped => &self.equipped,
|
||||||
|
ContainerType::ArmoryOffWeapon => &self.armoury_off_hand,
|
||||||
|
ContainerType::ArmoryHead => &self.armoury_head,
|
||||||
|
ContainerType::ArmoryBody => &self.armoury_body,
|
||||||
|
ContainerType::ArmoryHand => &self.armoury_hands,
|
||||||
|
ContainerType::ArmoryLeg => &self.armoury_legs,
|
||||||
|
ContainerType::ArmoryFoot => &self.armoury_feet,
|
||||||
|
ContainerType::ArmoryEarring => &self.armoury_earring,
|
||||||
|
ContainerType::ArmoryNeck => &self.armoury_necklace,
|
||||||
|
ContainerType::ArmoryWrist => &self.armoury_bracelet,
|
||||||
|
ContainerType::ArmoryRing => &self.armoury_rings,
|
||||||
|
ContainerType::ArmorySoulCrystal => &self.armoury_soul_crystal,
|
||||||
|
ContainerType::ArmoryWeapon => &self.armoury_main_hand,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_main_weapon_id(&self, game_data: &mut GameData) -> u64 {
|
||||||
|
game_data
|
||||||
|
.get_primary_model_id(self.equipped.main_hand.id)
|
||||||
|
.unwrap_or(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_model_ids(&self, game_data: &mut GameData) -> [u32; 10] {
|
||||||
|
[
|
||||||
|
game_data
|
||||||
|
.get_primary_model_id(self.equipped.head.id)
|
||||||
|
.unwrap_or(0) as u32,
|
||||||
|
game_data
|
||||||
|
.get_primary_model_id(self.equipped.body.id)
|
||||||
|
.unwrap_or(0) as u32,
|
||||||
|
game_data
|
||||||
|
.get_primary_model_id(self.equipped.hands.id)
|
||||||
|
.unwrap_or(0) as u32,
|
||||||
|
game_data
|
||||||
|
.get_primary_model_id(self.equipped.legs.id)
|
||||||
|
.unwrap_or(0) as u32,
|
||||||
|
game_data
|
||||||
|
.get_primary_model_id(self.equipped.feet.id)
|
||||||
|
.unwrap_or(0) as u32,
|
||||||
|
game_data
|
||||||
|
.get_primary_model_id(self.equipped.ears.id)
|
||||||
|
.unwrap_or(0) as u32,
|
||||||
|
game_data
|
||||||
|
.get_primary_model_id(self.equipped.neck.id)
|
||||||
|
.unwrap_or(0) as u32,
|
||||||
|
game_data
|
||||||
|
.get_primary_model_id(self.equipped.wrists.id)
|
||||||
|
.unwrap_or(0) as u32,
|
||||||
|
game_data
|
||||||
|
.get_primary_model_id(self.equipped.left_ring.id)
|
||||||
|
.unwrap_or(0) as u32,
|
||||||
|
game_data
|
||||||
|
.get_primary_model_id(self.equipped.right_ring.id)
|
||||||
|
.unwrap_or(0) as u32,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
38
src/inventory/storage.rs
Normal file
38
src/inventory/storage.rs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
use binrw::binrw;
|
||||||
|
|
||||||
|
use super::Item;
|
||||||
|
|
||||||
|
#[binrw]
|
||||||
|
#[brw(little)]
|
||||||
|
#[brw(repr = u16)]
|
||||||
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
|
||||||
|
pub enum ContainerType {
|
||||||
|
#[default]
|
||||||
|
Inventory0 = 0,
|
||||||
|
Inventory1 = 1,
|
||||||
|
Inventory2 = 2,
|
||||||
|
Inventory3 = 3,
|
||||||
|
|
||||||
|
Equipped = 1000,
|
||||||
|
|
||||||
|
ArmoryOffWeapon = 3200,
|
||||||
|
ArmoryHead = 3201,
|
||||||
|
ArmoryBody = 3202,
|
||||||
|
ArmoryHand = 3203,
|
||||||
|
ArmoryLeg = 3205,
|
||||||
|
ArmoryFoot = 3206,
|
||||||
|
ArmoryEarring = 3207,
|
||||||
|
ArmoryNeck = 3208,
|
||||||
|
ArmoryWrist = 3209,
|
||||||
|
ArmoryRing = 3300,
|
||||||
|
ArmorySoulCrystal = 3400,
|
||||||
|
ArmoryWeapon = 3500,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a generic item storage.
|
||||||
|
pub trait Storage: Sync {
|
||||||
|
fn max_slots(&self) -> u32;
|
||||||
|
fn num_items(&self) -> u32;
|
||||||
|
fn get_slot_mut(&mut self, index: u16) -> &mut Item;
|
||||||
|
fn get_slot(&self, index: u16) -> &Item;
|
||||||
|
}
|
|
@ -1,18 +1,6 @@
|
||||||
use binrw::binrw;
|
use binrw::binrw;
|
||||||
|
|
||||||
#[binrw]
|
use crate::inventory::ContainerType;
|
||||||
#[brw(little)]
|
|
||||||
#[brw(repr = u16)]
|
|
||||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
|
|
||||||
pub enum ContainerType {
|
|
||||||
#[default]
|
|
||||||
Inventory0 = 0,
|
|
||||||
Inventory1 = 1,
|
|
||||||
Inventory2 = 2,
|
|
||||||
Inventory3 = 3,
|
|
||||||
Equipped = 1000,
|
|
||||||
ArmouryBody = 3202,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[binrw]
|
#[binrw]
|
||||||
#[brw(little)]
|
#[brw(little)]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use binrw::binrw;
|
use binrw::binrw;
|
||||||
|
|
||||||
use super::ContainerType;
|
use crate::inventory::ContainerType;
|
||||||
|
|
||||||
#[binrw]
|
#[binrw]
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
|
@ -51,7 +51,7 @@ mod tests {
|
||||||
assert_eq!(modify_inventory.src_stack, 1);
|
assert_eq!(modify_inventory.src_stack, 1);
|
||||||
assert_eq!(modify_inventory.src_catalog_id, 0);
|
assert_eq!(modify_inventory.src_catalog_id, 0);
|
||||||
assert_eq!(modify_inventory.dst_actor_id, 0);
|
assert_eq!(modify_inventory.dst_actor_id, 0);
|
||||||
assert_eq!(modify_inventory.dst_storage_id, ContainerType::ArmouryBody);
|
assert_eq!(modify_inventory.dst_storage_id, ContainerType::ArmoryBody);
|
||||||
assert_eq!(modify_inventory.dst_container_index, 0);
|
assert_eq!(modify_inventory.dst_container_index, 0);
|
||||||
assert_eq!(modify_inventory.dst_stack, 0);
|
assert_eq!(modify_inventory.dst_stack, 0);
|
||||||
assert_eq!(modify_inventory.dst_catalog_id, 0);
|
assert_eq!(modify_inventory.dst_catalog_id, 0);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use binrw::binrw;
|
use binrw::binrw;
|
||||||
|
|
||||||
use super::ContainerType;
|
use crate::inventory::ContainerType;
|
||||||
|
|
||||||
#[binrw]
|
#[binrw]
|
||||||
#[brw(little)]
|
#[brw(little)]
|
||||||
|
|
|
@ -48,7 +48,7 @@ mod action_request;
|
||||||
pub use action_request::ActionRequest;
|
pub use action_request::ActionRequest;
|
||||||
|
|
||||||
mod container_info;
|
mod container_info;
|
||||||
pub use container_info::{ContainerInfo, ContainerType};
|
pub use container_info::ContainerInfo;
|
||||||
|
|
||||||
mod item_info;
|
mod item_info;
|
||||||
pub use item_info::ItemInfo;
|
pub use item_info::ItemInfo;
|
||||||
|
|
|
@ -36,6 +36,9 @@ pub mod opcodes;
|
||||||
/// IPC
|
/// IPC
|
||||||
pub mod ipc;
|
pub mod ipc;
|
||||||
|
|
||||||
|
/// Inventory and storage management.
|
||||||
|
pub mod inventory;
|
||||||
|
|
||||||
/// Used in the encryption key.
|
/// Used in the encryption key.
|
||||||
const GAME_VERSION: u16 = 7000;
|
const GAME_VERSION: u16 = 7000;
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ use tokio::{io::AsyncReadExt, net::TcpStream};
|
||||||
use crate::{
|
use crate::{
|
||||||
RECEIVE_BUFFER_SIZE,
|
RECEIVE_BUFFER_SIZE,
|
||||||
blowfish::Blowfish,
|
blowfish::Blowfish,
|
||||||
common::{timestamp_secs, workdefinitions::CharaMake},
|
common::timestamp_secs,
|
||||||
config::get_config,
|
config::get_config,
|
||||||
opcodes::ServerLobbyIpcType,
|
opcodes::ServerLobbyIpcType,
|
||||||
packet::oodle::OodleNetwork,
|
packet::oodle::OodleNetwork,
|
||||||
|
|
|
@ -12,12 +12,13 @@ use crate::{
|
||||||
OBFUSCATION_ENABLED_MODE,
|
OBFUSCATION_ENABLED_MODE,
|
||||||
common::{GameData, ObjectId, Position, timestamp_secs},
|
common::{GameData, ObjectId, Position, timestamp_secs},
|
||||||
config::get_config,
|
config::get_config,
|
||||||
|
inventory::{Inventory, Item},
|
||||||
ipc::chat::ServerChatIpcSegment,
|
ipc::chat::ServerChatIpcSegment,
|
||||||
ipc::zone::{
|
ipc::zone::{
|
||||||
ActorControlSelf, ActorMove, ActorSetPos, ClientZoneIpcSegment, CommonSpawn, ContainerInfo,
|
ActorControlSelf, ActorMove, ActorSetPos, ClientZoneIpcSegment, CommonSpawn, ContainerInfo,
|
||||||
ContainerType, DisplayFlag, Equip, InitZone, ItemInfo, NpcSpawn, ObjectKind, PlayerStats,
|
DisplayFlag, Equip, InitZone, ItemInfo, NpcSpawn, ObjectKind, PlayerStats, PlayerSubKind,
|
||||||
PlayerSubKind, ServerZoneIpcData, ServerZoneIpcSegment, StatusEffect, StatusEffectList,
|
ServerZoneIpcData, ServerZoneIpcSegment, StatusEffect, StatusEffectList, UpdateClassInfo,
|
||||||
UpdateClassInfo, WeatherChange,
|
WeatherChange,
|
||||||
},
|
},
|
||||||
opcodes::ServerZoneIpcType,
|
opcodes::ServerZoneIpcType,
|
||||||
packet::{
|
packet::{
|
||||||
|
@ -27,8 +28,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
Actor, CharacterData, Event, Inventory, Item, LuaPlayer, StatusEffects, WorldDatabase, Zone,
|
Actor, CharacterData, Event, LuaPlayer, StatusEffects, WorldDatabase, Zone, lua::Task,
|
||||||
inventory::Container, lua::Task,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
|
@ -443,23 +443,14 @@ impl ZoneConnection {
|
||||||
pub async fn send_inventory(&mut self, send_appearance_update: bool) {
|
pub async fn send_inventory(&mut self, send_appearance_update: bool) {
|
||||||
let mut sequence = 0;
|
let mut sequence = 0;
|
||||||
|
|
||||||
// pages
|
for (container_type, container) in &self.player_data.inventory.clone() {
|
||||||
for (i, page) in self.player_data.inventory.pages.clone().iter().enumerate() {
|
|
||||||
let kind = match i {
|
|
||||||
0 => ContainerType::Inventory0,
|
|
||||||
1 => ContainerType::Inventory1,
|
|
||||||
2 => ContainerType::Inventory2,
|
|
||||||
3 => ContainerType::Inventory3,
|
|
||||||
_ => panic!("Shouldn't be anything else!"),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut send_slot = async |slot_index: u16, item: &Item| {
|
let mut send_slot = async |slot_index: u16, item: &Item| {
|
||||||
let ipc = ServerZoneIpcSegment {
|
let ipc = ServerZoneIpcSegment {
|
||||||
op_code: ServerZoneIpcType::UpdateItem,
|
op_code: ServerZoneIpcType::UpdateItem,
|
||||||
timestamp: timestamp_secs(),
|
timestamp: timestamp_secs(),
|
||||||
data: ServerZoneIpcData::UpdateItem(ItemInfo {
|
data: ServerZoneIpcData::UpdateItem(ItemInfo {
|
||||||
sequence,
|
sequence,
|
||||||
container: kind,
|
container: container_type,
|
||||||
slot: slot_index,
|
slot: slot_index,
|
||||||
quantity: item.quantity,
|
quantity: item.quantity,
|
||||||
catalog_id: item.id,
|
catalog_id: item.id,
|
||||||
|
@ -478,8 +469,8 @@ impl ZoneConnection {
|
||||||
.await;
|
.await;
|
||||||
};
|
};
|
||||||
|
|
||||||
for (i, slot) in page.slots.iter().enumerate() {
|
for i in 0..container.max_slots() {
|
||||||
send_slot(i as u16, slot).await;
|
send_slot(i as u16, container.get_slot(i as u16)).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
// inform the client of container state
|
// inform the client of container state
|
||||||
|
@ -488,8 +479,8 @@ impl ZoneConnection {
|
||||||
op_code: ServerZoneIpcType::ContainerInfo,
|
op_code: ServerZoneIpcType::ContainerInfo,
|
||||||
timestamp: timestamp_secs(),
|
timestamp: timestamp_secs(),
|
||||||
data: ServerZoneIpcData::ContainerInfo(ContainerInfo {
|
data: ServerZoneIpcData::ContainerInfo(ContainerInfo {
|
||||||
container: kind,
|
container: container_type,
|
||||||
num_items: page.num_items(),
|
num_items: container.num_items(),
|
||||||
sequence,
|
sequence,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
|
@ -508,63 +499,6 @@ impl ZoneConnection {
|
||||||
sequence += 1;
|
sequence += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// equipped
|
|
||||||
{
|
|
||||||
let equipped = self.player_data.inventory.equipped;
|
|
||||||
|
|
||||||
let mut send_slot = async |slot_index: u16, item: &Item| {
|
|
||||||
let ipc = ServerZoneIpcSegment {
|
|
||||||
op_code: ServerZoneIpcType::UpdateItem,
|
|
||||||
timestamp: timestamp_secs(),
|
|
||||||
data: ServerZoneIpcData::UpdateItem(ItemInfo {
|
|
||||||
sequence,
|
|
||||||
container: ContainerType::Equipped,
|
|
||||||
slot: slot_index,
|
|
||||||
quantity: item.quantity,
|
|
||||||
catalog_id: item.id,
|
|
||||||
condition: 30000,
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
self.send_segment(PacketSegment {
|
|
||||||
source_actor: self.player_data.actor_id,
|
|
||||||
target_actor: self.player_data.actor_id,
|
|
||||||
segment_type: SegmentType::Ipc,
|
|
||||||
data: SegmentData::Ipc { data: ipc },
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
};
|
|
||||||
|
|
||||||
for (i, slot) in equipped.into_iter().enumerate() {
|
|
||||||
send_slot(i as u16, &slot).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// inform the client they have items equipped
|
|
||||||
{
|
|
||||||
let ipc = ServerZoneIpcSegment {
|
|
||||||
op_code: ServerZoneIpcType::ContainerInfo,
|
|
||||||
timestamp: timestamp_secs(),
|
|
||||||
data: ServerZoneIpcData::ContainerInfo(ContainerInfo {
|
|
||||||
container: ContainerType::Equipped,
|
|
||||||
num_items: self.player_data.inventory.equipped.num_items(),
|
|
||||||
sequence,
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
self.send_segment(PacketSegment {
|
|
||||||
source_actor: self.player_data.actor_id,
|
|
||||||
target_actor: self.player_data.actor_id,
|
|
||||||
segment_type: SegmentType::Ipc,
|
|
||||||
data: SegmentData::Ipc { data: ipc },
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
// send them an appearance update
|
// send them an appearance update
|
||||||
if send_appearance_update {
|
if send_appearance_update {
|
||||||
let ipc;
|
let ipc;
|
||||||
|
|
|
@ -8,10 +8,11 @@ use crate::{
|
||||||
CustomizeData, GameData, Position,
|
CustomizeData, GameData, Position,
|
||||||
workdefinitions::{CharaMake, ClientSelectData, RemakeMode},
|
workdefinitions::{CharaMake, ClientSelectData, RemakeMode},
|
||||||
},
|
},
|
||||||
|
inventory::Inventory,
|
||||||
ipc::lobby::{CharacterDetails, CharacterFlag},
|
ipc::lobby::{CharacterDetails, CharacterFlag},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{Inventory, PlayerData};
|
use super::PlayerData;
|
||||||
|
|
||||||
pub struct WorldDatabase {
|
pub struct WorldDatabase {
|
||||||
connection: Mutex<Connection>,
|
connection: Mutex<Connection>,
|
||||||
|
|
|
@ -1,315 +0,0 @@
|
||||||
use physis::common::Language;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::common::GameData;
|
|
||||||
|
|
||||||
use crate::ipc::zone::{ContainerType, InventoryModify};
|
|
||||||
|
|
||||||
// TODO: rename to storage?
|
|
||||||
pub trait Container {
|
|
||||||
fn num_items(&self) -> u32;
|
|
||||||
fn get_slot_mut(&mut self, index: u16) -> &mut Item;
|
|
||||||
fn get_slot(&self, index: u16) -> &Item;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Copy, Clone, Serialize, Deserialize, Debug)]
|
|
||||||
pub struct Item {
|
|
||||||
pub quantity: u32,
|
|
||||||
pub id: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Item {
|
|
||||||
pub fn new(quantity: u32, id: u32) -> Self {
|
|
||||||
Self { quantity, id }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Clone, Copy, Deserialize, Serialize, Debug)]
|
|
||||||
pub struct EquippedContainer {
|
|
||||||
pub main_hand: Item,
|
|
||||||
pub off_hand: Item,
|
|
||||||
pub head: Item,
|
|
||||||
pub body: Item,
|
|
||||||
pub hands: Item,
|
|
||||||
pub legs: Item,
|
|
||||||
pub feet: Item,
|
|
||||||
pub ears: Item,
|
|
||||||
pub neck: Item,
|
|
||||||
pub wrists: Item,
|
|
||||||
pub right_ring: Item,
|
|
||||||
pub left_ring: Item,
|
|
||||||
pub soul_crystal: Item,
|
|
||||||
|
|
||||||
// only for the iterator, so it can skip over it
|
|
||||||
pub belt: Item,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Container for EquippedContainer {
|
|
||||||
fn num_items(&self) -> u32 {
|
|
||||||
self.main_hand.quantity
|
|
||||||
+ self.off_hand.quantity
|
|
||||||
+ self.head.quantity
|
|
||||||
+ self.body.quantity
|
|
||||||
+ self.hands.quantity
|
|
||||||
+ self.legs.quantity
|
|
||||||
+ self.feet.quantity
|
|
||||||
+ self.ears.quantity
|
|
||||||
+ self.neck.quantity
|
|
||||||
+ self.wrists.quantity
|
|
||||||
+ self.right_ring.quantity
|
|
||||||
+ self.left_ring.quantity
|
|
||||||
+ self.soul_crystal.quantity
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_slot_mut(&mut self, index: u16) -> &mut Item {
|
|
||||||
match index {
|
|
||||||
0 => &mut self.main_hand,
|
|
||||||
1 => &mut self.off_hand,
|
|
||||||
2 => &mut self.head,
|
|
||||||
3 => &mut self.body,
|
|
||||||
4 => &mut self.hands,
|
|
||||||
6 => &mut self.legs,
|
|
||||||
7 => &mut self.feet,
|
|
||||||
8 => &mut self.ears,
|
|
||||||
9 => &mut self.neck,
|
|
||||||
10 => &mut self.wrists,
|
|
||||||
11 => &mut self.right_ring,
|
|
||||||
12 => &mut self.left_ring,
|
|
||||||
13 => &mut self.soul_crystal,
|
|
||||||
_ => panic!("{} is not a valid src_container_index?!?", index),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_slot(&self, index: u16) -> &Item {
|
|
||||||
match index {
|
|
||||||
0 => &self.main_hand,
|
|
||||||
1 => &self.off_hand,
|
|
||||||
2 => &self.head,
|
|
||||||
3 => &self.body,
|
|
||||||
4 => &self.hands,
|
|
||||||
5 => &self.belt,
|
|
||||||
6 => &self.legs,
|
|
||||||
7 => &self.feet,
|
|
||||||
8 => &self.ears,
|
|
||||||
9 => &self.neck,
|
|
||||||
10 => &self.wrists,
|
|
||||||
11 => &self.right_ring,
|
|
||||||
12 => &self.left_ring,
|
|
||||||
13 => &self.soul_crystal,
|
|
||||||
_ => panic!("{} is not a valid src_container_index?!?", index),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> IntoIterator for &'a EquippedContainer {
|
|
||||||
type Item = &'a Item;
|
|
||||||
type IntoIter = EquippedContainerIterator<'a>;
|
|
||||||
fn into_iter(self) -> EquippedContainerIterator<'a> {
|
|
||||||
EquippedContainerIterator {
|
|
||||||
equipped: self,
|
|
||||||
curr: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct EquippedContainerIterator<'a> {
|
|
||||||
equipped: &'a EquippedContainer,
|
|
||||||
curr: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Iterator for EquippedContainerIterator<'a> {
|
|
||||||
type Item = &'a Item;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
let curr = self.curr;
|
|
||||||
self.curr += 1;
|
|
||||||
|
|
||||||
if self.curr >= 14 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(self.equipped.get_slot(curr))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
||||||
pub struct InventoryPage {
|
|
||||||
pub slots: Vec<Item>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InventoryPage {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
slots: vec![Item::default(); 35],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Container for InventoryPage {
|
|
||||||
fn num_items(&self) -> u32 {
|
|
||||||
self.slots.iter().filter(|item| item.quantity > 0).count() as u32
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_slot_mut(&mut self, index: u16) -> &mut Item {
|
|
||||||
self.slots.get_mut(index as usize).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_slot(&self, index: u16) -> &Item {
|
|
||||||
self.slots.get(index as usize).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
||||||
pub struct Inventory {
|
|
||||||
pub equipped: EquippedContainer,
|
|
||||||
pub pages: [InventoryPage; 4],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Inventory {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
equipped: EquippedContainer::default(),
|
|
||||||
pages: std::array::from_fn(|_| InventoryPage::default()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Inventory {
|
|
||||||
/// Equip the starting items for a given race
|
|
||||||
pub fn equip_racial_items(&mut self, race_id: u8, gender: u8, game_data: &mut GameData) {
|
|
||||||
let exh = game_data.game_data.read_excel_sheet_header("Race").unwrap();
|
|
||||||
let exd = game_data
|
|
||||||
.game_data
|
|
||||||
.read_excel_sheet("Race", &exh, Language::English, 0)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let world_row = &exd.read_row(&exh, race_id as u32).unwrap()[0];
|
|
||||||
|
|
||||||
let get_column = |column_index: usize| {
|
|
||||||
let physis::exd::ColumnData::Int32(item_id) = &world_row.data[column_index] else {
|
|
||||||
panic!("Unexpected type!");
|
|
||||||
};
|
|
||||||
|
|
||||||
*item_id
|
|
||||||
};
|
|
||||||
|
|
||||||
if gender == 0 {
|
|
||||||
self.equipped.body = Item::new(1, get_column(2) as u32);
|
|
||||||
self.equipped.hands = Item::new(1, get_column(3) as u32);
|
|
||||||
self.equipped.legs = Item::new(1, get_column(4) as u32);
|
|
||||||
self.equipped.feet = Item::new(1, get_column(5) as u32);
|
|
||||||
} else {
|
|
||||||
self.equipped.body = Item::new(1, get_column(6) as u32);
|
|
||||||
self.equipped.hands = Item::new(1, get_column(7) as u32);
|
|
||||||
self.equipped.legs = Item::new(1, get_column(8) as u32);
|
|
||||||
self.equipped.feet = Item::new(1, get_column(9) as u32);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: don't hardcode
|
|
||||||
self.equipped.main_hand = Item::new(1, 0x00000641);
|
|
||||||
self.equipped.ears = Item::new(1, 0x00003b1b);
|
|
||||||
self.equipped.neck = Item::new(1, 0x00003b1a);
|
|
||||||
self.equipped.wrists = Item::new(1, 0x00003b1c);
|
|
||||||
self.equipped.right_ring = Item::new(1, 0x0000114a);
|
|
||||||
self.equipped.left_ring = Item::new(1, 0x00003b1d);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn process_action(&mut self, action: &InventoryModify) {
|
|
||||||
if action.operation_type == 78 {
|
|
||||||
// discard
|
|
||||||
let src_container = self.get_container(&action.src_storage_id);
|
|
||||||
let src_slot = src_container.get_slot_mut(action.src_container_index);
|
|
||||||
*src_slot = Item::default();
|
|
||||||
} else {
|
|
||||||
// NOTE: only swaps items for now
|
|
||||||
|
|
||||||
let src_item;
|
|
||||||
// get the source item
|
|
||||||
{
|
|
||||||
let src_container = self.get_container(&action.src_storage_id);
|
|
||||||
let src_slot = src_container.get_slot_mut(action.src_container_index);
|
|
||||||
src_item = *src_slot;
|
|
||||||
}
|
|
||||||
|
|
||||||
let dst_item;
|
|
||||||
// move into dst item
|
|
||||||
{
|
|
||||||
let dst_container = self.get_container(&action.dst_storage_id);
|
|
||||||
let dst_slot = dst_container.get_slot_mut(action.dst_container_index);
|
|
||||||
|
|
||||||
dst_item = *dst_slot;
|
|
||||||
dst_slot.clone_from(&src_item);
|
|
||||||
}
|
|
||||||
|
|
||||||
// move dst item into src slot
|
|
||||||
{
|
|
||||||
let src_container = self.get_container(&action.src_storage_id);
|
|
||||||
let src_slot = src_container.get_slot_mut(action.src_container_index);
|
|
||||||
src_slot.clone_from(&dst_item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_in_next_free_slot(&mut self, item: Item) {
|
|
||||||
for page in &mut self.pages {
|
|
||||||
for slot in &mut page.slots {
|
|
||||||
if slot.quantity == 0 {
|
|
||||||
slot.clone_from(&item);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_container(&mut self, container_type: &ContainerType) -> &mut dyn Container {
|
|
||||||
match container_type {
|
|
||||||
ContainerType::Inventory0 => &mut self.pages[0],
|
|
||||||
ContainerType::Inventory1 => &mut self.pages[1],
|
|
||||||
ContainerType::Inventory2 => &mut self.pages[2],
|
|
||||||
ContainerType::Inventory3 => &mut self.pages[3],
|
|
||||||
ContainerType::Equipped => &mut self.equipped,
|
|
||||||
ContainerType::ArmouryBody => todo!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_main_weapon_id(&self, game_data: &mut GameData) -> u64 {
|
|
||||||
game_data
|
|
||||||
.get_primary_model_id(self.equipped.main_hand.id)
|
|
||||||
.unwrap_or(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_model_ids(&self, game_data: &mut GameData) -> [u32; 10] {
|
|
||||||
[
|
|
||||||
game_data
|
|
||||||
.get_primary_model_id(self.equipped.head.id)
|
|
||||||
.unwrap_or(0) as u32,
|
|
||||||
game_data
|
|
||||||
.get_primary_model_id(self.equipped.body.id)
|
|
||||||
.unwrap_or(0) as u32,
|
|
||||||
game_data
|
|
||||||
.get_primary_model_id(self.equipped.hands.id)
|
|
||||||
.unwrap_or(0) as u32,
|
|
||||||
game_data
|
|
||||||
.get_primary_model_id(self.equipped.legs.id)
|
|
||||||
.unwrap_or(0) as u32,
|
|
||||||
game_data
|
|
||||||
.get_primary_model_id(self.equipped.feet.id)
|
|
||||||
.unwrap_or(0) as u32,
|
|
||||||
game_data
|
|
||||||
.get_primary_model_id(self.equipped.ears.id)
|
|
||||||
.unwrap_or(0) as u32,
|
|
||||||
game_data
|
|
||||||
.get_primary_model_id(self.equipped.neck.id)
|
|
||||||
.unwrap_or(0) as u32,
|
|
||||||
game_data
|
|
||||||
.get_primary_model_id(self.equipped.wrists.id)
|
|
||||||
.unwrap_or(0) as u32,
|
|
||||||
game_data
|
|
||||||
.get_primary_model_id(self.equipped.left_ring.id)
|
|
||||||
.unwrap_or(0) as u32,
|
|
||||||
game_data
|
|
||||||
.get_primary_model_id(self.equipped.right_ring.id)
|
|
||||||
.unwrap_or(0) as u32,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -12,9 +12,6 @@ pub use connection::{
|
||||||
mod database;
|
mod database;
|
||||||
pub use database::{CharacterData, WorldDatabase};
|
pub use database::{CharacterData, WorldDatabase};
|
||||||
|
|
||||||
mod inventory;
|
|
||||||
pub use inventory::{EquippedContainer, Inventory, Item};
|
|
||||||
|
|
||||||
mod lua;
|
mod lua;
|
||||||
pub use lua::{EffectsBuilder, LuaPlayer};
|
pub use lua::{EffectsBuilder, LuaPlayer};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue