From 1914531d8902f8c211f7d4b0b65dbe385878e4b8 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Sun, 11 May 2025 09:27:29 -0400 Subject: [PATCH] Add commands to unlock actions, and aetherytes This doesn't mean teleporting works *yet*, as it needs to be scripted. The unlocked action/aetheryte is also not persisted yet. --- USAGE.md | 2 ++ src/bin/kawari-world.rs | 9 ++++++++- src/ipc/zone/actor_control.rs | 16 ++++++++++++++++ src/ipc/zone/mod.rs | 1 + src/world/chat_handler.rs | 15 +++++++++++++-- 5 files changed, 40 insertions(+), 3 deletions(-) diff --git a/USAGE.md b/USAGE.md index 858801e..bb3ddc9 100644 --- a/USAGE.md +++ b/USAGE.md @@ -87,6 +87,7 @@ These special debug commands start with `!` and are custom to Kawari. * Territory `183`, Event `1245186` plays the Gridania opening sequence * `!spawnclone`: Spawn a clone of yourself * `!classjob `: Changes to another class/job +* `!unlockaction `: Unlock an action, for example: `1` for Return and `4` for Teleport. ### GM commands @@ -97,3 +98,4 @@ These GM commands are implemented in the FFXIV protocol, but only some of them a * `//gm wireframe`: Toggle wireframe rendering for the environment * `//gm item `: Gives yourself an item. This can only place a single item in the first page of your inventory currently. * `//gm lv `: Sets your current level +* `//gm aetheryte `: Unlock an Aetheryte. diff --git a/src/bin/kawari-world.rs b/src/bin/kawari-world.rs index 07295e5..24f15b5 100644 --- a/src/bin/kawari-world.rs +++ b/src/bin/kawari-world.rs @@ -521,7 +521,7 @@ async fn client_loop( .await; } } - ClientZoneIpcData::GMCommand { command, arg0, .. } => { + ClientZoneIpcData::GMCommand { command, arg0, arg1, .. } => { tracing::info!("Got a game master command!"); match &command { @@ -555,6 +555,13 @@ async fn client_loop( connection.player_data.inventory.add_in_next_free_slot(Item { id: *arg0, quantity: 1 }); connection.send_inventory(false).await; } + GameMasterCommandType::Aetheryte => { + let on = *arg0 == 0; + let id = *arg1; + + connection.actor_control_self(ActorControlSelf { + category: ActorControlCategory::LearnTeleport { id, unlocked: on } }).await; + } } } ClientZoneIpcData::ZoneJump { diff --git a/src/ipc/zone/actor_control.rs b/src/ipc/zone/actor_control.rs index b97591a..408f8e0 100644 --- a/src/ipc/zone/actor_control.rs +++ b/src/ipc/zone/actor_control.rs @@ -47,6 +47,22 @@ pub enum ActorControlCategory { unk1: u32, pose: u32, }, + #[brw(magic = 0x1FDu16)] + LearnTeleport { + #[brw(pad_before = 2)] //padding + id: u32, + #[br(map = read_bool_from::)] + #[bw(map = write_bool_as::)] + unlocked: bool, + }, + #[brw(magic = 0x29u16)] + ToggleActionUnlock { + #[brw(pad_before = 2)] //padding + id: u32, + #[br(map = read_bool_from::)] + #[bw(map = write_bool_as::)] + unlocked: bool, + }, } #[binrw] diff --git a/src/ipc/zone/mod.rs b/src/ipc/zone/mod.rs index 1be6197..706519e 100644 --- a/src/ipc/zone/mod.rs +++ b/src/ipc/zone/mod.rs @@ -144,6 +144,7 @@ pub enum GameMasterCommandType { ToggleWireframe = 0x26, ChangeTerritory = 0x58, GiveItem = 0xC8, + Aetheryte = 0x5E, } #[binrw] diff --git a/src/world/chat_handler.rs b/src/world/chat_handler.rs index 23ee364..f453473 100644 --- a/src/world/chat_handler.rs +++ b/src/world/chat_handler.rs @@ -1,8 +1,9 @@ use crate::{ common::{CustomizeData, ObjectId, ObjectTypeId, timestamp_secs}, ipc::zone::{ - ActorControl, ActorControlCategory, BattleNpcSubKind, ChatMessage, CommonSpawn, EventStart, - NpcSpawn, ObjectKind, OnlineStatus, ServerZoneIpcData, ServerZoneIpcSegment, + ActorControl, ActorControlCategory, ActorControlSelf, BattleNpcSubKind, ChatMessage, + CommonSpawn, EventStart, NpcSpawn, ObjectKind, OnlineStatus, ServerZoneIpcData, + ServerZoneIpcSegment, }, opcodes::ServerZoneIpcType, packet::{PacketSegment, SegmentData, SegmentType}, @@ -203,6 +204,16 @@ impl ChatHandler { }) .await; } + "!unlockaction" => { + let parts: Vec<&str> = chat_message.message.split(' ').collect(); + let id = parts[1].parse::().unwrap(); + + connection + .actor_control_self(ActorControlSelf { + category: ActorControlCategory::ToggleActionUnlock { id, unlocked: true }, + }) + .await; + } _ => {} } }