mirror of
https://github.com/redstrate/Kawari.git
synced 2025-04-22 07:27:44 +00:00
Allow unequipping items, add packet for updating equipped model ids
The unequipped item currently disappears into the aether, but this works now!
This commit is contained in:
parent
bc8535cce2
commit
0c76d847d5
8 changed files with 150 additions and 19 deletions
|
@ -149,6 +149,11 @@
|
||||||
"name": "ActionResult",
|
"name": "ActionResult",
|
||||||
"opcode": 508,
|
"opcode": 508,
|
||||||
"size": 124
|
"size": 124
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Equip",
|
||||||
|
"opcode": 468,
|
||||||
|
"size": 72
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"ClientZoneIpcType": [
|
"ClientZoneIpcType": [
|
||||||
|
@ -260,7 +265,7 @@
|
||||||
{
|
{
|
||||||
"name": "InventoryModify",
|
"name": "InventoryModify",
|
||||||
"opcode": 564,
|
"opcode": 564,
|
||||||
"size": 112
|
"size": 48
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"ServerLobbyIpcType": [
|
"ServerLobbyIpcType": [
|
||||||
|
|
BIN
resources/tests/equip.bin
Normal file
BIN
resources/tests/equip.bin
Normal file
Binary file not shown.
|
@ -19,8 +19,8 @@ use kawari::world::ipc::{
|
||||||
ServerZoneIpcData, ServerZoneIpcSegment, SocialListRequestType,
|
ServerZoneIpcData, ServerZoneIpcSegment, SocialListRequestType,
|
||||||
};
|
};
|
||||||
use kawari::world::{
|
use kawari::world::{
|
||||||
Actor, ClientHandle, ClientId, EffectsBuilder, FromServer, LuaPlayer, PlayerData, ServerHandle,
|
Actor, ClientHandle, ClientId, EffectsBuilder, FromServer, Item, LuaPlayer, PlayerData,
|
||||||
StatusEffects, ToServer, WorldDatabase,
|
ServerHandle, StatusEffects, ToServer, WorldDatabase,
|
||||||
};
|
};
|
||||||
use kawari::world::{
|
use kawari::world::{
|
||||||
ChatHandler, Inventory, Zone, ZoneConnection,
|
ChatHandler, Inventory, Zone, ZoneConnection,
|
||||||
|
@ -283,7 +283,7 @@ async fn client_loop(
|
||||||
);
|
);
|
||||||
|
|
||||||
// Send inventory
|
// Send inventory
|
||||||
connection.send_inventory().await;
|
connection.send_inventory(false).await;
|
||||||
|
|
||||||
// set chara gear param
|
// set chara gear param
|
||||||
connection
|
connection
|
||||||
|
@ -815,6 +815,9 @@ async fn client_loop(
|
||||||
}
|
}
|
||||||
ClientZoneIpcData::InventoryModify(action) => {
|
ClientZoneIpcData::InventoryModify(action) => {
|
||||||
tracing::info!("Client is modifying inventory! {action:#?}");
|
tracing::info!("Client is modifying inventory! {action:#?}");
|
||||||
|
|
||||||
|
connection.inventory.process_action(&action);
|
||||||
|
connection.send_inventory(true).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ use super::{
|
||||||
chat_handler::CUSTOMIZE_DATA,
|
chat_handler::CUSTOMIZE_DATA,
|
||||||
ipc::{
|
ipc::{
|
||||||
ActorControlSelf, ActorMove, ActorSetPos, BattleNpcSubKind, ClientZoneIpcSegment,
|
ActorControlSelf, ActorMove, ActorSetPos, BattleNpcSubKind, ClientZoneIpcSegment,
|
||||||
CommonSpawn, ContainerInfo, ContainerType, InitZone, ItemInfo, NpcSpawn, ObjectKind,
|
CommonSpawn, ContainerInfo, ContainerType, Equip, InitZone, ItemInfo, NpcSpawn, ObjectKind,
|
||||||
ServerZoneIpcData, ServerZoneIpcSegment, StatusEffect, StatusEffectList, UpdateClassInfo,
|
ServerZoneIpcData, ServerZoneIpcSegment, StatusEffect, StatusEffectList, UpdateClassInfo,
|
||||||
WeatherChange,
|
WeatherChange,
|
||||||
},
|
},
|
||||||
|
@ -447,7 +447,7 @@ impl ZoneConnection {
|
||||||
self.spawn_index
|
self.spawn_index
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send_inventory(&mut self) {
|
pub async fn send_inventory(&mut self, send_appearance_update: bool) {
|
||||||
// item list
|
// item list
|
||||||
{
|
{
|
||||||
let equipped = self.inventory.equipped;
|
let equipped = self.inventory.equipped;
|
||||||
|
@ -510,6 +510,46 @@ impl ZoneConnection {
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// send them an appearance update
|
||||||
|
if send_appearance_update {
|
||||||
|
let ipc;
|
||||||
|
{
|
||||||
|
let mut game_data = self.gamedata.lock().unwrap();
|
||||||
|
let equipped = &self.inventory.equipped;
|
||||||
|
|
||||||
|
ipc = ServerZoneIpcSegment {
|
||||||
|
op_code: ServerZoneIpcType::Equip,
|
||||||
|
timestamp: timestamp_secs(),
|
||||||
|
data: ServerZoneIpcData::Equip(Equip {
|
||||||
|
main_weapon_id: 0,
|
||||||
|
sub_weapon_id: 0,
|
||||||
|
crest_enable: 0,
|
||||||
|
pattern_invalid: 0,
|
||||||
|
model_ids: [
|
||||||
|
game_data.get_primary_model_id(equipped.head.id) as u32,
|
||||||
|
game_data.get_primary_model_id(equipped.body.id) as u32,
|
||||||
|
game_data.get_primary_model_id(equipped.hands.id) as u32,
|
||||||
|
game_data.get_primary_model_id(equipped.legs.id) as u32,
|
||||||
|
game_data.get_primary_model_id(equipped.feet.id) as u32,
|
||||||
|
game_data.get_primary_model_id(equipped.ears.id) as u32,
|
||||||
|
game_data.get_primary_model_id(equipped.neck.id) as u32,
|
||||||
|
game_data.get_primary_model_id(equipped.wrists.id) as u32,
|
||||||
|
game_data.get_primary_model_id(equipped.left_ring.id) as u32,
|
||||||
|
game_data.get_primary_model_id(equipped.right_ring.id) as u32,
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
..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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send_message(&mut self, message: &str) {
|
pub async fn send_message(&mut self, message: &str) {
|
||||||
|
|
|
@ -5,6 +5,8 @@ use physis::{
|
||||||
|
|
||||||
use crate::config::get_config;
|
use crate::config::get_config;
|
||||||
|
|
||||||
|
use super::ipc::InventoryModify;
|
||||||
|
|
||||||
#[derive(Default, Copy, Clone)]
|
#[derive(Default, Copy, Clone)]
|
||||||
pub struct Item {
|
pub struct Item {
|
||||||
pub quantity: u32,
|
pub quantity: u32,
|
||||||
|
@ -111,4 +113,29 @@ impl Inventory {
|
||||||
self.equipped.right_ring = Item::new(1, 0x0000114a);
|
self.equipped.right_ring = Item::new(1, 0x0000114a);
|
||||||
self.equipped.left_ring = Item::new(1, 0x00003b1d);
|
self.equipped.left_ring = Item::new(1, 0x00003b1d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn process_action(&mut self, action: &InventoryModify) {
|
||||||
|
// equipped
|
||||||
|
if action.src_storage_id == 1000 {
|
||||||
|
let slot = match action.src_container_index {
|
||||||
|
0 => &mut self.equipped.main_hand,
|
||||||
|
1 => &mut self.equipped.off_hand,
|
||||||
|
2 => &mut self.equipped.head,
|
||||||
|
3 => &mut self.equipped.body,
|
||||||
|
4 => &mut self.equipped.hands,
|
||||||
|
6 => &mut self.equipped.legs,
|
||||||
|
7 => &mut self.equipped.feet,
|
||||||
|
8 => &mut self.equipped.ears,
|
||||||
|
9 => &mut self.equipped.neck,
|
||||||
|
10 => &mut self.equipped.wrists,
|
||||||
|
11 => &mut self.equipped.right_ring,
|
||||||
|
12 => &mut self.equipped.left_ring,
|
||||||
|
13 => &mut self.equipped.soul_crystal,
|
||||||
|
_ => panic!("Not a valid src_container_index?!?"),
|
||||||
|
};
|
||||||
|
|
||||||
|
// it only unequips for now, doesn't move the item
|
||||||
|
*slot = Item::default();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
48
src/world/ipc/equip.rs
Normal file
48
src/world/ipc/equip.rs
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
use binrw::binrw;
|
||||||
|
|
||||||
|
#[binrw]
|
||||||
|
#[brw(little)]
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct Equip {
|
||||||
|
pub main_weapon_id: u64,
|
||||||
|
pub sub_weapon_id: u64,
|
||||||
|
pub crest_enable: u8,
|
||||||
|
#[brw(pad_before = 1)]
|
||||||
|
pub pattern_invalid: u16,
|
||||||
|
#[brw(pad_after = 12)]
|
||||||
|
pub model_ids: [u32; 10],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::{fs::read, io::Cursor, path::PathBuf};
|
||||||
|
|
||||||
|
use binrw::BinRead;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn read_containerinfo() {
|
||||||
|
let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||||
|
d.push("resources/tests/equip.bin");
|
||||||
|
|
||||||
|
let buffer = read(d).unwrap();
|
||||||
|
let mut buffer = Cursor::new(&buffer);
|
||||||
|
|
||||||
|
let equip = Equip::read_le(&mut buffer).unwrap();
|
||||||
|
assert_eq!(equip.main_weapon_id, 4297785545);
|
||||||
|
assert_eq!(equip.sub_weapon_id, 0);
|
||||||
|
assert_eq!(equip.crest_enable, 0);
|
||||||
|
assert_eq!(equip.pattern_invalid, 1);
|
||||||
|
assert_eq!(equip.model_ids[0], 0);
|
||||||
|
assert_eq!(equip.model_ids[1], 0);
|
||||||
|
assert_eq!(equip.model_ids[2], 131156);
|
||||||
|
assert_eq!(equip.model_ids[3], 131156);
|
||||||
|
assert_eq!(equip.model_ids[4], 131156);
|
||||||
|
assert_eq!(equip.model_ids[5], 131073);
|
||||||
|
assert_eq!(equip.model_ids[6], 131073);
|
||||||
|
assert_eq!(equip.model_ids[7], 131073);
|
||||||
|
assert_eq!(equip.model_ids[8], 0);
|
||||||
|
assert_eq!(equip.model_ids[9], 131073);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,21 +3,20 @@ use binrw::binrw;
|
||||||
#[binrw]
|
#[binrw]
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct InventoryModify {
|
pub struct InventoryModify {
|
||||||
context_id: u32,
|
pub context_id: u32,
|
||||||
operation_type: u8,
|
pub operation_type: u8,
|
||||||
#[brw(pad_before = 3)]
|
#[brw(pad_before = 3)]
|
||||||
src_actor_id: u32,
|
pub src_actor_id: u32,
|
||||||
src_storage_id: u32,
|
pub src_storage_id: u32,
|
||||||
src_container_index: i16,
|
pub src_container_index: i16,
|
||||||
#[brw(pad_before = 4)]
|
#[brw(pad_before = 4)]
|
||||||
src_stack: u32,
|
pub src_stack: u32,
|
||||||
src_catalog_id: u32,
|
pub src_catalog_id: u32,
|
||||||
dst_actor_id: u32,
|
pub dst_actor_id: u32,
|
||||||
dst_storage_id: u32,
|
pub dst_storage_id: u32,
|
||||||
dst_container_index: i16,
|
pub dst_container_index: i16,
|
||||||
#[brw(pad_before = 2)]
|
pub dst_stack: u32,
|
||||||
dst_stack: u32,
|
pub dst_catalog_id: u32,
|
||||||
dst_catalog_id: u32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -71,6 +71,9 @@ pub use actor_set_pos::ActorSetPos;
|
||||||
mod inventory_modify;
|
mod inventory_modify;
|
||||||
pub use inventory_modify::InventoryModify;
|
pub use inventory_modify::InventoryModify;
|
||||||
|
|
||||||
|
mod equip;
|
||||||
|
pub use equip::Equip;
|
||||||
|
|
||||||
use crate::common::Position;
|
use crate::common::Position;
|
||||||
use crate::common::read_string;
|
use crate::common::read_string;
|
||||||
use crate::common::write_string;
|
use crate::common::write_string;
|
||||||
|
@ -207,6 +210,8 @@ pub enum ServerZoneIpcData {
|
||||||
},
|
},
|
||||||
/// Sent to inform the client the consequences of their actions
|
/// Sent to inform the client the consequences of their actions
|
||||||
ActionResult(ActionResult),
|
ActionResult(ActionResult),
|
||||||
|
/// Sent to to the client to update their appearance
|
||||||
|
Equip(Equip),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[binrw]
|
#[binrw]
|
||||||
|
@ -458,6 +463,10 @@ mod tests {
|
||||||
ServerZoneIpcType::ActionResult,
|
ServerZoneIpcType::ActionResult,
|
||||||
ServerZoneIpcData::ActionResult(ActionResult::default()),
|
ServerZoneIpcData::ActionResult(ActionResult::default()),
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
ServerZoneIpcType::Equip,
|
||||||
|
ServerZoneIpcData::Equip(Equip::default()),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
for (opcode, data) in &ipc_types {
|
for (opcode, data) in &ipc_types {
|
||||||
|
|
Loading…
Add table
Reference in a new issue