diff --git a/FFXIVClientStructs/FFXIV/Client/Game/Control/Control.cs b/FFXIVClientStructs/FFXIV/Client/Game/Control/Control.cs index 4b06062bf..053506fd9 100644 --- a/FFXIVClientStructs/FFXIV/Client/Game/Control/Control.cs +++ b/FFXIVClientStructs/FFXIV/Client/Game/Control/Control.cs @@ -15,6 +15,7 @@ public unsafe partial struct Control { [FieldOffset(0x7518)] public bool IsWalkingDuringAutorun; [FieldOffset(0x7637)] public bool IsWalking; + [FieldOffset(0x7688)] public SpectatorSystem* SpectatorSystem; [FieldOffset(0x7698)] public uint LocalPlayerEntityId; [FieldOffset(0x76A0)] public BattleChara* LocalPlayer; [FieldOffset(0x76B0)] public Matrix4x4 ViewProjectionMatrix; @@ -40,4 +41,12 @@ public enum FlightAllowedStatus { DefaultCase = 7, PlayerOrMountNull = 8 } + + /// Creates a new SpectatorSystem. + [MemberFunction("E8 ?? ?? ?? ?? 33 DB 48 8B FD 66 90")] + public partial void CreateSpectatorControl(); + + /// Calls the destructor for SpectatorSystem and sets it back to null. + [MemberFunction("E8 ?? ?? ?? ?? 33 D2 48 8D 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 8B F0")] + public partial void FreeSpectatorControl(); } diff --git a/FFXIVClientStructs/FFXIV/Client/Game/Control/SpectatorSystem.cs b/FFXIVClientStructs/FFXIV/Client/Game/Control/SpectatorSystem.cs new file mode 100644 index 000000000..80d0f97df --- /dev/null +++ b/FFXIVClientStructs/FFXIV/Client/Game/Control/SpectatorSystem.cs @@ -0,0 +1,36 @@ +using FFXIVClientStructs.FFXIV.Client.Game.Character; +using FFXIVClientStructs.FFXIV.Common.Math; + +namespace FFXIVClientStructs.FFXIV.Client.Game.Control; + +// Client::Game::Control::SpectatorSystem +[GenerateInterop] +[StructLayout(LayoutKind.Explicit, Size = 0xD0)] +public unsafe partial struct SpectatorSystem { + [FieldOffset(0x08), FixedSizeArray] internal FixedSizeArray8 _spectatorSpots; + [FieldOffset(0xAC)] public uint FocusObjectId; + [FieldOffset(0xB0)] public uint FocusSpotIndex; + [FieldOffset(0xB4)] public float FocusX; + [FieldOffset(0xB8)] public float FocusY; + [FieldOffset(0xBC)] public float FocusZ; + [FieldOffset(0xC0)] public float FocusDirH; + [FieldOffset(0xC4)] public float FocusDirV; + + /// Moves the spectator camera to a specific object. + [MemberFunction("E8 ?? ?? ?? ?? 48 8B 83 ?? ?? ?? ?? 45 85 FF")] + public partial void FocusOnObject(uint objectId); + + /// Moves the spectator camera to a spot at this index. + [MemberFunction("E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 0F B6 86 ?? ?? ?? ?? 3B D8")] + public partial void FocusOnSpot(uint index, bool a3); + + [GenerateInterop] + [StructLayout(LayoutKind.Explicit, Size = 0x14)] + public unsafe partial struct SpectatorSpot { + [FieldOffset(0x00)] public float X; // Spectator.X + [FieldOffset(0x04)] public float Y; // Spectator.Y + [FieldOffset(0x08)] public float Z; // Spectator.Z + [FieldOffset(0x0C)] public float DirH; // Spectator.DirH + [FieldOffset(0x10)] public float DirV; // Spectator.DirV + } +} diff --git a/FFXIVClientStructs/FFXIV/Client/Game/Network/SpectatorListPacket.cs b/FFXIVClientStructs/FFXIV/Client/Game/Network/SpectatorListPacket.cs new file mode 100644 index 000000000..b1a53ff44 --- /dev/null +++ b/FFXIVClientStructs/FFXIV/Client/Game/Network/SpectatorListPacket.cs @@ -0,0 +1,8 @@ +namespace FFXIVClientStructs.FFXIV.Client.Game.Network; + +[GenerateInterop] +[StructLayout(LayoutKind.Explicit, Size = 0x24)] +public partial struct SpectatorListPacket { + [FieldOffset(0x0), FixedSizeArray] internal FixedSizeArray8 _spectatorIds; + [FieldOffset(0x20)] private byte Flag; // Used as a count for spectator ids, maybe? +} diff --git a/FFXIVClientStructs/FFXIV/Client/Game/SpectatorManager.cs b/FFXIVClientStructs/FFXIV/Client/Game/SpectatorManager.cs new file mode 100644 index 000000000..f28c13b0b --- /dev/null +++ b/FFXIVClientStructs/FFXIV/Client/Game/SpectatorManager.cs @@ -0,0 +1,55 @@ +using FFXIVClientStructs.FFXIV.Common.Component.Excel; +using FFXIVClientStructs.FFXIV.Component.GUI; + +namespace FFXIVClientStructs.FFXIV.Client.Game; + +// Client::Game::SpectatorManager +/// +/// Initializes and manages the spectator system +/// +[GenerateInterop] +[StructLayout(LayoutKind.Explicit, Size = 0x4E0)] +public unsafe partial struct SpectatorManager { + [StaticAddress("4C 89 2D ?? ?? ?? ?? 45 33 C0", 3, isPointer: true)] + public static partial SpectatorManager* Instance(); + + [FieldOffset(0x008)] public ExcelSheet* Sheet; + [FieldOffset(0x010)] public SpectatorManagerWaiter* Waiter; + [FieldOffset(0x08C)] public uint SpectatorRowId; + [FieldOffset(0x094)] private uint Unk94; + [FieldOffset(0x098)] private uint Unk98; + [FieldOffset(0x090)] private byte Unk90; + [FieldOffset(0x0A1)] private byte Unknown40; // Set with Spectator.Unknown40 in ReadSpectatorRow + [FieldOffset(0x0A2)] public byte SheetStatus; // 0 = Missing, 1 = Sheet loaded, 2 = Row read + [FieldOffset(0x0DA)] private byte UnkDA; // 1, 2 and 100 (default?) are known flags. 2 shows the spectator management UI + + /// Initializes the SpectatorManager global. + [MemberFunction("48 89 5C 24 ?? 55 41 54 41 55 41 56 41 57 48 83 EC ?? 44 8B E1")] + public static partial void Initialize(uint spectatorRowId, uint a3, byte a4, uint a5); + + /// Updates and manages the Excel waiters here. + [MemberFunction("48 89 5C 24 ?? 56 48 83 EC ?? F3 0F 10 89")] + public partial void Update(); + + /// Reads spectator data from Excel, puts most of it into Control.SpectatorSystem. + [MemberFunction("48 8D 05 ?? ?? ?? ?? 48 89 47 ?? 48 89 5F ?? 48 89 77 ?? EB ?? 48 8B FE 44 8B 83")] + public partial void ReadSpectatorRow([CExporterExcel("Spectator")] void *spectatorRow); + + /// Changes an internal flag to tell the spectator UI to show itself. + [MemberFunction("40 53 48 83 EC ?? 80 A1 ?? ?? ?? ?? ?? 48 8B D9 E8 ?? ?? ?? ?? 80 8B")] + public partial void ShowSpectatorUI(); + + /// Used by AgentPvPSpectator to check if the regular PvP spectator UI should be visible. + [MemberFunction("E8 ?? ?? ?? ?? 84 C0 0F 84 ?? ?? ?? ?? 48 8B 83 ?? ?? ?? ?? 41 B0")] + public partial bool ShouldShowSpectatorUI(); + + [GenerateInterop] + [Inherits] + [StructLayout(LayoutKind.Explicit, Size = 0x50)] + public partial struct SpectatorManagerWaiter { + [FieldOffset(0x030)] private void* Unk30; + [FieldOffset(0x038)] public void* Arg; + [FieldOffset(0x040)] public void* Callback; + [FieldOffset(0x048)] private void* Unk48; + } +} diff --git a/FFXIVClientStructs/FFXIV/Client/Game/UI/InstanceContent.cs b/FFXIVClientStructs/FFXIV/Client/Game/UI/InstanceContent.cs index 114c655b4..3dc71080c 100644 --- a/FFXIVClientStructs/FFXIV/Client/Game/UI/InstanceContent.cs +++ b/FFXIVClientStructs/FFXIV/Client/Game/UI/InstanceContent.cs @@ -1,5 +1,6 @@ using FFXIVClientStructs.FFXIV.Client.System.Memory; using FFXIVClientStructs.FFXIV.Client.UI.Agent; +using FFXIVClientStructs.FFXIV.Client.Game.Network; namespace FFXIVClientStructs.FFXIV.Client.Game.UI; @@ -10,6 +11,7 @@ public unsafe partial struct InstanceContent { [StaticAddress("48 8D 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 84 C0 0F 94 C3", 3)] public static partial InstanceContent* Instance(); + [FieldOffset(0x38), FixedSizeArray] internal FixedSizeArray8 _spectatorIds; [FieldOffset(0x6C)] public byte RankedCrystallineConflictHostingDataCenterId; // saved as uint, used as byte? checks against 0xFF too [FieldOffset(0x70)] public bool IsLimitedTimeBonusActive; @@ -27,6 +29,9 @@ public unsafe partial struct InstanceContent { [MemberFunction("48 83 EC 28 84 D2 74 33")] public partial bool IsRouletteIncomplete(byte roulette); + [MemberFunction("8B 02 4C 8B C9 89 41")] + public partial void ProcessSpectatorListPacket(SpectatorListPacket* packet); + public bool IsRouletteComplete(byte roulette) => !IsRouletteIncomplete(roulette); [GenerateInterop] diff --git a/FFXIVClientStructs/FFXIV/Client/Network/PacketDispatcher.cs b/FFXIVClientStructs/FFXIV/Client/Network/PacketDispatcher.cs index 0444224e1..a1d179cb8 100644 --- a/FFXIVClientStructs/FFXIV/Client/Network/PacketDispatcher.cs +++ b/FFXIVClientStructs/FFXIV/Client/Network/PacketDispatcher.cs @@ -99,6 +99,9 @@ public static partial void HandleActorControlPacket( [MemberFunction("4C 8D 89 ?? ?? ?? ?? 48 8B D1")] public static partial void HandleAchievementsPacket(AchievementsPacket* packet); + [MemberFunction("E8 ?? ?? ?? ?? B0 ?? 48 8B 5C 24 ?? 48 8B 74 24 ?? 48 83 C4 ?? 5F C3 48 8B CB E8 ?? ?? ?? ?? B0 ?? 48 8B 5C 24 ?? 48 8B 74 24 ?? 48 83 C4 ?? 5F C3 48 8B CB E8 ?? ?? ?? ?? 48 8B 5C 24")] + public static partial void HandleSpectatorListPacket(SpectatorListPacket* packet); + [VirtualFunction(1)] public partial void OnReceivePacket(uint targetId, nint packet); } diff --git a/FFXIVClientStructs/FFXIV/Client/UI/Agent/AgentPvPSpectator.cs b/FFXIVClientStructs/FFXIV/Client/UI/Agent/AgentPvPSpectator.cs new file mode 100644 index 000000000..dbd8fdbf9 --- /dev/null +++ b/FFXIVClientStructs/FFXIV/Client/UI/Agent/AgentPvPSpectator.cs @@ -0,0 +1,20 @@ +namespace FFXIVClientStructs.FFXIV.Client.UI.Agent; + +// Client::UI::Agent::AgentPvPSpectator +// Client::UI::Agent::AgentInterface +// Component::GUI::AtkModuleInterface::AtkEventInterface +[Agent(AgentId.PvPSpectator)] +[GenerateInterop] +[Inherits] +[StructLayout(LayoutKind.Explicit, Size = 0xC0)] +public unsafe partial struct AgentPvPSpectator { + [FieldOffset(0x88)] public AgentPvPSpectatorData* Data; + + [MemberFunction("E8 ?? ?? ?? ?? 80 BB ?? ?? ?? ?? ?? 0F 84 ?? ?? ?? ?? 48 8B 4B ?? 48 89 AC 24")] + public partial void UpdateAddonVisibility(); +} + +[StructLayout(LayoutKind.Explicit, Size = 0x378)] +public unsafe partial struct AgentPvPSpectatorData { + [FieldOffset(0x354)] public uint AddonPvPSpectatorCameraList; +} diff --git a/ida/data.yml b/ida/data.yml index 1e85bcab2..084a7683b 100644 --- a/ida/data.yml +++ b/ida/data.yml @@ -47,6 +47,7 @@ globals: 0x142A71554: g_animationFactorRemainder 0x142A71AB0: g_animationFactor #percentage of a 30fps frame rendered this frame 0x142A76110: g_PerformanceFrequency + 0x142A823E8: g_Client::Game::Control::Control_SpectatorSystem 0x142A823F8: g_Client::Game::Control::Control_LocalPlayerEntityId 0x142A82400: g_Client::Game::Control::Control_LocalPlayer 0x142A91500: g_NounPlaceholder_n @@ -629,6 +630,14 @@ classes: 0x141962EC0: ExecuteEmote # (emoteId, targetInfo) 0x141963780: CanExecuteEmote # (emoteId) 0x141964030: Update + Client::Game::Control::SpectatorSystem: + vtbls: + - ea: 0x142170A70 + vfuncs: + 0: dtor + funcs: + 0x14062D2F0: FocusOnObject + 0x14062D390: FocusOnSpot Application::Network::NetworkModule: funcs: 0x141CF2680: ctor @@ -956,6 +965,26 @@ classes: 0x141A7C840: GetAdjustedTimestamp # static 0x141A7C860: GetBonusGuaranteeForTimestamp # static 0x141A7C8A0: GetSatisfactionBonusesForNpc # static + Client::Game::SpectatorManager: + vtbls: + - ea: 0x1422CB8F8 + vfuncs: + 0: dtor + instances: + - ea: 0x142ACAC58 + pointer: true + funcs: + 0x141783290: Initialize + 0x141783490: GetInstance + 0x141785260: GetInstance2 + 0x141782A10: Update + 0x141782D60: ShowSpectatorUI + 0x1417834F0: ReadSpectatorRow + 0x141782C80: ShouldShowSpectatorUI + Client::Game::SpectatorManagerWaiter: + vtbls: + - ea: 0x1422CB988 + base: Common::Component::Excel::ExcelSheetWaiter Client::Game::InstanceContent::ContentSheetWaiterInterface: vtbls: - ea: 0x14218BBC8 @@ -3603,6 +3632,7 @@ classes: - ea: 0x1421BA2B8 funcs: 0x140C27C10: GetPenaltyRemainingInMinutes + 0x140C28280: ProcessSpectatorListPacket 0x140C29720: GetContentInterface 0x140C2A5A0: IsRouletteIncomplete Client::Game::UI::GuildOrderReward: @@ -4293,6 +4323,7 @@ classes: 0x142084B50: Finalizer vfuncs: 0: dtor + 1: Finish Component::Excel::ExcelModuleInterface: vtbls: - ea: 0x14211ED90 @@ -6557,6 +6588,7 @@ classes: 0x140B485E0: HandleDeepDungeonPartyInfoPacket 0x140B48620: HandleDeepDungeonChestInfoPacket 0x140B48660: HandleDeepDungeonBlacklistPacket + 0x140B48840: HandleSpectatorListPacket 0x140B497B0: HandlePrepareZoningPacket 0x140B49CB0: HandleMapEffectPacket 0x140B4A290: HandleQuestManagerUnkArray2SecondHalfPacket @@ -8493,6 +8525,8 @@ classes: 0x14062C630: Initialize 0x14062C6C0: Terminate 0x14062C900: Update + 0x14062CEF0: CreateSpectatorControl + 0x14062CF70: FreeSpectatorControl 0x141832940: GetFlightAllowedStatus # static Client::Game::Control::CameraManager: funcs: @@ -14181,6 +14215,8 @@ classes: vtbls: - ea: 0x1421BFF58 base: Client::UI::Agent::AgentInterface + funcs: + 0x140D62EC0: UpdateAddonVisibility Client::UI::Agent::AgentAquarium: vtbls: - ea: 0x1421ED3B0