mirror of
https://github.com/redstrate/Kawari.git
synced 2025-04-19 22:36:49 +00:00
Add GM command to give items, support equipping from inventory
Now with this command and support for *one* inventory slot, you can now freely equip items to your character for testing.
This commit is contained in:
parent
32b6840e38
commit
822d1f5139
8 changed files with 114 additions and 37 deletions
3
USAGE.md
3
USAGE.md
|
@ -92,7 +92,7 @@ These special debug commands start with `!` and are custom to Kawari.
|
|||
* Territory `181`, Event `1245185` plays the Limsa opening sequence
|
||||
* Territory `182`, Event `1245187` plays the Ul'dah opening sequence
|
||||
* Territory `183`, Event `1245186` plays the Gridania opening sequence
|
||||
|
||||
|
||||
### GM commands
|
||||
|
||||
These GM commands are implemented in the FFXIV protocol, but only some of them are implemented.
|
||||
|
@ -100,3 +100,4 @@ These GM commands are implemented in the FFXIV protocol, but only some of them a
|
|||
* `//gm teri <id>`: Changes to the specified territory
|
||||
* `//gm weather <id>`: Changes the weather
|
||||
* `//gm wireframe`: Toggle wireframe rendering for the environment
|
||||
* `//gm item <id>`: Gives yourself an item. This can only place a single item in the first page of your inventory currently.
|
||||
|
|
Binary file not shown.
|
@ -655,6 +655,11 @@ async fn client_loop(
|
|||
ActorControlCategory::ToggleWireframeRendering(),
|
||||
})
|
||||
.await,
|
||||
GameMasterCommandType::GiveItem => {
|
||||
connection.inventory.extra_slot.id = *arg;
|
||||
connection.inventory.extra_slot.quantity = 1;
|
||||
connection.send_inventory(false).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
ClientZoneIpcData::EnterZoneLine {
|
||||
|
|
|
@ -448,7 +448,37 @@ impl ZoneConnection {
|
|||
}
|
||||
|
||||
pub async fn send_inventory(&mut self, send_appearance_update: bool) {
|
||||
// item list
|
||||
// page 1
|
||||
{
|
||||
let extra_slot = self.inventory.extra_slot;
|
||||
|
||||
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::Inventory0,
|
||||
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, &extra_slot).await;
|
||||
}
|
||||
|
||||
// equipped
|
||||
{
|
||||
let equipped = self.inventory.equipped;
|
||||
|
||||
|
@ -490,6 +520,27 @@ impl ZoneConnection {
|
|||
send_slot(13, &equipped.soul_crystal).await;
|
||||
}
|
||||
|
||||
// inform the client of page 1
|
||||
{
|
||||
let ipc = ServerZoneIpcSegment {
|
||||
op_code: ServerZoneIpcType::ContainerInfo,
|
||||
timestamp: timestamp_secs(),
|
||||
data: ServerZoneIpcData::ContainerInfo(ContainerInfo {
|
||||
container: ContainerType::Inventory0,
|
||||
num_items: 1,
|
||||
..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;
|
||||
}
|
||||
|
||||
// inform the client they have items equipped
|
||||
{
|
||||
let ipc = ServerZoneIpcSegment {
|
||||
|
@ -498,6 +549,7 @@ impl ZoneConnection {
|
|||
data: ServerZoneIpcData::ContainerInfo(ContainerInfo {
|
||||
container: ContainerType::Equipped,
|
||||
num_items: self.inventory.equipped.num_items(),
|
||||
sequence: 1,
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
|
|
|
@ -5,7 +5,7 @@ use physis::{
|
|||
|
||||
use crate::config::get_config;
|
||||
|
||||
use super::ipc::InventoryModify;
|
||||
use super::ipc::{ContainerType, InventoryModify};
|
||||
|
||||
#[derive(Default, Copy, Clone)]
|
||||
pub struct Item {
|
||||
|
@ -52,10 +52,30 @@ impl EquippedContainer {
|
|||
+ self.left_ring.quantity
|
||||
+ self.soul_crystal.quantity
|
||||
}
|
||||
|
||||
pub fn get_slot(&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!("Not a valid src_container_index?!?"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Inventory {
|
||||
pub equipped: EquippedContainer,
|
||||
pub extra_slot: Item, // WIP for inventory pages
|
||||
}
|
||||
|
||||
impl Default for Inventory {
|
||||
|
@ -68,6 +88,7 @@ impl Inventory {
|
|||
pub fn new() -> Self {
|
||||
Self {
|
||||
equipped: EquippedContainer::default(),
|
||||
extra_slot: Item::default()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -116,26 +137,15 @@ impl Inventory {
|
|||
|
||||
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?!?"),
|
||||
};
|
||||
if action.src_storage_id == ContainerType::Equipped {
|
||||
let src_slot = self.equipped.get_slot(action.src_container_index);
|
||||
|
||||
// it only unequips for now, doesn't move the item
|
||||
*slot = Item::default();
|
||||
*src_slot = Item::default();
|
||||
} else if action.src_storage_id == ContainerType::Inventory0 {
|
||||
let dst_slot = self.equipped.get_slot(action.dst_container_index);
|
||||
|
||||
*dst_slot = self.extra_slot;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ pub enum ContainerType {
|
|||
Inventory2 = 2,
|
||||
Inventory3 = 3,
|
||||
Equipped = 1000,
|
||||
ArmouryBody = 3202,
|
||||
}
|
||||
|
||||
#[binrw]
|
||||
|
@ -19,7 +20,7 @@ pub enum ContainerType {
|
|||
pub struct ContainerInfo {
|
||||
pub sequence: u32,
|
||||
pub num_items: u32,
|
||||
#[brw(pad_after = 2)] // not used
|
||||
#[brw(pad_size_to = 4)]
|
||||
pub container: ContainerType,
|
||||
pub start_or_finish: u32,
|
||||
}
|
||||
|
|
|
@ -1,20 +1,27 @@
|
|||
use binrw::binrw;
|
||||
|
||||
use super::ContainerType;
|
||||
|
||||
#[binrw]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct InventoryModify {
|
||||
pub context_id: u32,
|
||||
pub operation_type: u8,
|
||||
#[brw(pad_before = 3)]
|
||||
pub operation_type: u16,
|
||||
|
||||
#[brw(pad_before = 2)]
|
||||
pub src_actor_id: u32,
|
||||
pub src_storage_id: u32,
|
||||
pub src_container_index: i16,
|
||||
#[brw(pad_before = 4)]
|
||||
#[brw(pad_size_to = 4)]
|
||||
pub src_storage_id: ContainerType,
|
||||
pub src_container_index: u16,
|
||||
#[brw(pad_before = 2)]
|
||||
pub src_stack: u32,
|
||||
pub src_catalog_id: u32,
|
||||
|
||||
pub dst_actor_id: u32,
|
||||
pub dst_storage_id: u32,
|
||||
pub dst_container_index: i16,
|
||||
#[brw(pad_size_to = 4)]
|
||||
pub dst_storage_id: ContainerType,
|
||||
pub dst_container_index: u16,
|
||||
#[brw(pad_before = 2)]
|
||||
pub dst_stack: u32,
|
||||
pub dst_catalog_id: u32,
|
||||
}
|
||||
|
@ -36,16 +43,16 @@ mod tests {
|
|||
let mut buffer = Cursor::new(&buffer);
|
||||
|
||||
let modify_inventory = InventoryModify::read_le(&mut buffer).unwrap();
|
||||
assert_eq!(modify_inventory.context_id, 0x10000002);
|
||||
assert_eq!(modify_inventory.operation_type, 70);
|
||||
assert_eq!(modify_inventory.context_id, 0x10000000);
|
||||
assert_eq!(modify_inventory.operation_type, 572);
|
||||
assert_eq!(modify_inventory.src_actor_id, 0);
|
||||
assert_eq!(modify_inventory.src_storage_id, 1000);
|
||||
assert_eq!(modify_inventory.src_container_index, 4);
|
||||
assert_eq!(modify_inventory.src_stack, 0);
|
||||
assert_eq!(modify_inventory.src_storage_id, ContainerType::Equipped);
|
||||
assert_eq!(modify_inventory.src_container_index, 3);
|
||||
assert_eq!(modify_inventory.src_stack, 1);
|
||||
assert_eq!(modify_inventory.src_catalog_id, 0);
|
||||
assert_eq!(modify_inventory.dst_actor_id, 209911808);
|
||||
assert_eq!(modify_inventory.dst_storage_id, 0);
|
||||
assert_eq!(modify_inventory.dst_container_index, 96);
|
||||
assert_eq!(modify_inventory.dst_actor_id, 0);
|
||||
assert_eq!(modify_inventory.dst_storage_id, ContainerType::ArmouryBody);
|
||||
assert_eq!(modify_inventory.dst_container_index, 0);
|
||||
assert_eq!(modify_inventory.dst_stack, 0);
|
||||
assert_eq!(modify_inventory.dst_catalog_id, 0);
|
||||
}
|
||||
|
|
|
@ -136,6 +136,7 @@ pub enum GameMasterCommandType {
|
|||
ToggleInvisibility = 0xD,
|
||||
ToggleWireframe = 0x26,
|
||||
ChangeTerritory = 0x58,
|
||||
GiveItem = 0xC8,
|
||||
}
|
||||
|
||||
#[binrw]
|
||||
|
|
Loading…
Add table
Reference in a new issue