From 8a03c8248018d6c3cb346a67741005268c9c1435 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Sun, 23 Mar 2025 17:43:06 -0400 Subject: [PATCH] Create Inventory struct, move inventory filling logic to ZoneConnection This still uses hardcoded item ids, but this should much more extensible. --- src/bin/kawari-world.rs | 84 +++++------------------------------------ src/world/connection.rs | 72 +++++++++++++++++++++++++++++++++-- src/world/inventory.rs | 73 +++++++++++++++++++++++++++++++++++ src/world/mod.rs | 3 ++ 4 files changed, 155 insertions(+), 77 deletions(-) create mode 100644 src/world/inventory.rs diff --git a/src/bin/kawari-world.rs b/src/bin/kawari-world.rs index e372f80..fce66ca 100644 --- a/src/bin/kawari-world.rs +++ b/src/bin/kawari-world.rs @@ -17,7 +17,7 @@ use kawari::world::ipc::{ StatusEffect, }; use kawari::world::{ - ChatHandler, Zone, ZoneConnection, + ChatHandler, Inventory, Zone, ZoneConnection, ipc::{ ActorControl, ActorControlCategory, ActorControlSelf, PlayerEntry, PlayerSetup, PlayerSpawn, PlayerStats, SocialList, @@ -63,6 +63,7 @@ async fn main() { spawn_index: 0, zone: None, position: Position::default(), + inventory: Inventory::new(), }; tokio::spawn(async move { @@ -85,8 +86,6 @@ async fn main() { connection.player_data = database.find_player_data(actor_id.parse::().unwrap()); - println!("player data: {:#?}", connection.player_data); - // We have send THEM a keep alive { connection @@ -189,75 +188,16 @@ async fn main() { .await; } - let item_ids = [ - (12, 0x00003b1d), - (11, 0x0000114a), - (10, 0x00003b1c), - (9, 0x00003b1a), - (8, 0x00003b1b), - (7, 0x00000ea7), - (6, 0x00000ce1), - (4, 0x00000dc1), - (3, 0x00000ba8), - (0, 0x00000641), - ]; + let chara_details = database + .find_chara_make(connection.player_data.content_id); - // send inventory - { - for (slot, id) in &item_ids { - let ipc = ServerZoneIpcSegment { - op_code: ServerZoneIpcType::ItemInfo, - timestamp: timestamp_secs(), - data: ServerZoneIpcData::ItemInfo(ItemInfo { - container: ContainerType::Equipped, - slot: *slot, - quantity: 1, - catalog_id: *id, - condition: 30000, - ..Default::default() - }), - ..Default::default() - }; + // fill inventory + connection.inventory.equip_racial_items( + chara_details.chara_make.customize.race, + ); - connection - .send_segment(PacketSegment { - source_actor: connection - .player_data - .actor_id, - target_actor: connection - .player_data - .actor_id, - segment_type: SegmentType::Ipc { - data: ipc, - }, - }) - .await; - } - } - - // inform the client they have 10 items equipped - { - let ipc = ServerZoneIpcSegment { - op_code: ServerZoneIpcType::ContainerInfo, - timestamp: timestamp_secs(), - data: ServerZoneIpcData::ContainerInfo( - ContainerInfo { - container: ContainerType::Equipped, - num_items: item_ids.len() as u32, - ..Default::default() - }, - ), - ..Default::default() - }; - - connection - .send_segment(PacketSegment { - source_actor: connection.player_data.actor_id, - target_actor: connection.player_data.actor_id, - segment_type: SegmentType::Ipc { data: ipc }, - }) - .await; - } + // Send inventory + connection.send_inventory().await; // Control Data { @@ -308,11 +248,7 @@ async fn main() { .await; } - let chara_details = database - .find_chara_make(connection.player_data.content_id); - let zone_id = chara_details.zone_id; - connection.zone = Some(Zone::load(zone_id)); // Player Setup diff --git a/src/world/connection.rs b/src/world/connection.rs index 26c156e..5d2c6c6 100644 --- a/src/world/connection.rs +++ b/src/world/connection.rs @@ -9,10 +9,10 @@ use crate::{ }; use super::{ - Zone, + Inventory, Item, Zone, ipc::{ - ActorSetPos, ClientZoneIpcSegment, InitZone, ServerZoneIpcData, ServerZoneIpcSegment, - ServerZoneIpcType, UpdateClassInfo, WeatherChange, + ActorSetPos, ClientZoneIpcSegment, ContainerInfo, ContainerType, InitZone, ItemInfo, + ServerZoneIpcData, ServerZoneIpcSegment, ServerZoneIpcType, UpdateClassInfo, WeatherChange, }, }; @@ -34,6 +34,7 @@ pub struct ZoneConnection { pub spawn_index: u8, pub position: Position, + pub inventory: Inventory, } impl ZoneConnection { @@ -169,4 +170,69 @@ impl ZoneConnection { self.spawn_index += 1; self.spawn_index } + + pub async fn send_inventory(&mut self) { + // item list + { + let equipped = self.inventory.equipped.clone(); + + let mut send_slot = async |slot_index: u16, item: &Item| { + let ipc = ServerZoneIpcSegment { + op_code: ServerZoneIpcType::ItemInfo, + timestamp: timestamp_secs(), + data: ServerZoneIpcData::ItemInfo(ItemInfo { + 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: ipc }, + }) + .await; + }; + + send_slot(0, &equipped.main_hand).await; + send_slot(1, &equipped.off_hand).await; + send_slot(2, &equipped.head).await; + send_slot(3, &equipped.body).await; + send_slot(4, &equipped.hands).await; + send_slot(6, &equipped.legs).await; + send_slot(7, &equipped.feet).await; + send_slot(8, &equipped.ears).await; + send_slot(9, &equipped.neck).await; + send_slot(10, &equipped.wrists).await; + send_slot(11, &equipped.right_ring).await; + send_slot(12, &equipped.left_ring).await; + send_slot(13, &equipped.soul_crystal).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.inventory.equipped.num_items(), + ..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: ipc }, + }) + .await; + } + } } diff --git a/src/world/inventory.rs b/src/world/inventory.rs new file mode 100644 index 0000000..188e36d --- /dev/null +++ b/src/world/inventory.rs @@ -0,0 +1,73 @@ +#[derive(Default, Copy, Clone)] +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)] +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, +} + +impl EquippedContainer { + pub 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 + } +} + +pub struct Inventory { + pub equipped: EquippedContainer, +} + +impl Inventory { + pub fn new() -> Self { + Self { + equipped: EquippedContainer::default(), + } + } + + /// Equip the starting items for a given race + pub fn equip_racial_items(&mut self, race_id: u8) { + // TODO: don't hardcode + self.equipped.main_hand = Item::new(1, 0x00000641); + self.equipped.body = Item::new(1, 0x00000ba8); + self.equipped.hands = Item::new(1, 0x00000dc1); + self.equipped.legs = Item::new(1, 0x00000ce1); + self.equipped.feet = Item::new(1, 0x00000ea7); + 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); + } +} diff --git a/src/world/mod.rs b/src/world/mod.rs index 9a40076..321e7e1 100644 --- a/src/world/mod.rs +++ b/src/world/mod.rs @@ -11,3 +11,6 @@ pub use connection::{PlayerData, ZoneConnection}; mod database; pub use database::{CharacterData, WorldDatabase}; + +mod inventory; +pub use inventory::{EquippedContainer, Inventory, Item};