diff --git a/FFXIVClientStructs/FFXIV/Client/Game/Event/EventHandler.cs b/FFXIVClientStructs/FFXIV/Client/Game/Event/EventHandler.cs
index 002f18ea4d..03116473b2 100644
--- a/FFXIVClientStructs/FFXIV/Client/Game/Event/EventHandler.cs
+++ b/FFXIVClientStructs/FFXIV/Client/Game/Event/EventHandler.cs
@@ -62,6 +62,22 @@ public unsafe partial struct EventHandler {
[VirtualFunction(209)]
public partial uint GetNameplateIconForObject(GameObject* gameObject);
+ /// Changes the currently playing timelines based on the difference between oldSharedTimelineState and newSharedTimelineState.
+ /// The game object to update.
+ /// The new SharedTimelineState value.
+ /// The old SharedTimelineState value.
+ /// Seems to modify newSharedTimelineState in in some EventHandlers.
+ [VirtualFunction(253)]
+ public partial void UpdateSharedTimelineState(GameObject* gameObject, ushort oldSharedTimelineState, ushort newSharedTimelineState, ulong timelineIndices);
+
+ /// Changes the currently playing timelines based on a bitmask.
+ /// The game object to update.
+ /// The new SharedTimelineState value.
+ /// The new timeline bitmask.
+ /// Unused and can be left empty.
+ [VirtualFunction(254)]
+ public partial void UpdateTimelinesByBitmask(GameObject* gameObject, uint sharedTimelineState, uint bitmask, ulong unused);
+
[VirtualFunction(258)]
public partial void GetDescription(Utf8String* outDescription);
diff --git a/FFXIVClientStructs/FFXIV/Client/Game/InstanceContent/ContentDirector.cs b/FFXIVClientStructs/FFXIV/Client/Game/InstanceContent/ContentDirector.cs
index 6a326b4f24..20c10b2c39 100644
--- a/FFXIVClientStructs/FFXIV/Client/Game/InstanceContent/ContentDirector.cs
+++ b/FFXIVClientStructs/FFXIV/Client/Game/InstanceContent/ContentDirector.cs
@@ -34,6 +34,19 @@ public unsafe partial struct ContentDirector {
[VirtualFunction(329)]
public partial uint GetContentTimeMax();
+ /// Changes the state of a map effect.
+ /// Index into MapEffects.
+ /// The new state for this MapEffect.
+ /// Which timeline to play.
+ [MemberFunction("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC ?? 8B FA 41 0F B7 E8")]
+ public partial void ApplyMapEffect(uint index, ushort state, ushort timelineIndex);
+
+ /// Handles changes the timeline for a map effect
+ /// Index into MapEffects.
+ /// Which timeline to play.
+ [MemberFunction("E8 ?? ?? ?? ?? 3A C3 74 ?? 44 0F B7 C5")]
+ public partial bool PlayMapEffectTimeline(uint index, ushort timelineIndex);
+
[GenerateInterop]
[StructLayout(LayoutKind.Explicit, Size = 0x608)]
public partial struct MapEffectList {
@@ -45,7 +58,7 @@ public partial struct MapEffectList {
[StructLayout(LayoutKind.Explicit, Size = 0xC)]
public struct MapEffectItem {
- [FieldOffset(0x00)] public uint LayoutId; // ContentDirectorManagedSG.Unknown0
+ [FieldOffset(0x00)] public uint LayoutId;
[FieldOffset(0x05)] public byte Unknown1; // ContentDirectorManagedSG.Unknown1
[FieldOffset(0x08)] public ushort State;
[FieldOffset(0x0A)] public byte Flags;
diff --git a/FFXIVClientStructs/FFXIV/Client/Game/Network/MapEffectPacket.cs b/FFXIVClientStructs/FFXIV/Client/Game/Network/MapEffectPacket.cs
new file mode 100644
index 0000000000..445d3cea0a
--- /dev/null
+++ b/FFXIVClientStructs/FFXIV/Client/Game/Network/MapEffectPacket.cs
@@ -0,0 +1,11 @@
+namespace FFXIVClientStructs.FFXIV.Client.Game.Network;
+
+[GenerateInterop]
+[StructLayout(LayoutKind.Explicit, Size = 0x10)]
+public partial struct MapEffectPacket {
+ [FieldOffset(0x00)] public uint EventId;
+ [FieldOffset(0x04)] public ushort State;
+ [FieldOffset(0x06)] public ushort TimelineIndex;
+ /// Which map effect this applies to.
+ [FieldOffset(0x08)] public byte Index;
+}
diff --git a/FFXIVClientStructs/FFXIV/Client/Game/Object/EventObject.cs b/FFXIVClientStructs/FFXIV/Client/Game/Object/EventObject.cs
index 853becd45d..d7b035b668 100644
--- a/FFXIVClientStructs/FFXIV/Client/Game/Object/EventObject.cs
+++ b/FFXIVClientStructs/FFXIV/Client/Game/Object/EventObject.cs
@@ -1,3 +1,5 @@
+using EventHandler = FFXIVClientStructs.FFXIV.Client.Game.Event.EventHandler;
+
namespace FFXIVClientStructs.FFXIV.Client.Game.Object;
// Client::Game::Object::EventObject
@@ -5,12 +7,33 @@ namespace FFXIVClientStructs.FFXIV.Client.Game.Object;
[GenerateInterop]
[Inherits]
[StructLayout(LayoutKind.Explicit, Size = 0x1C0)]
-public partial struct EventObject {
+public unsafe partial struct EventObject {
[FieldOffset(0x1A0), CExporterExcel("EObj")] public nint EObjRowPtr;
[FieldOffset(0x1A8), CExporterExcel("ExportedSG")] public nint ExportedSGRowPtr;
[FieldOffset(0x1B2)] public ushort SharedTimelineState; // ActorControl category 0x199 (SetSharedTimelineState) sets this field
[FieldOffset(0x1B8)] public byte Flags;
+ /// Changes the currently playing timelines based on a bitmask.
+ /// Only sets SharedTimelineState, does not have any other effect in this function.
+ /// Each bit represents a timeline index in the SharedGroup. Setting a bit means play, an unset bit means stop.
+ /// Unused, can be left as zero.
+ [MemberFunction("E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 4D 85 F6 0F 84 ?? ?? ?? ?? 8B 44 24 70 BE ?? ?? ?? ??")]
+ public partial void PlayAnimation(uint sharedTimelineState, uint bitmask, ulong unknown);
+ /// Changes the currently playing timelines based on a bitmask.
+ /// The new SharedTimelineState value.
+ /// The new timeline bitmask.
+ [MemberFunction("48 89 5C 24 ?? 41 56 48 83 EC ?? 48 89 6C 24 ?? 44 0F B7 F2")]
+ public partial void UpdateTimelinesByBitmask(ushort sharedTimelineState, uint bitmask);
+
+ /// Should point to array that can fit up to 32 pointers.
+ /// Num elements filled.
[MemberFunction("E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 4D 85 F6 0F 84 ?? ?? ?? ?? 8B 44 24 70 BE ?? ?? ?? ??")]
- public partial void PlayAnimation(uint entityId, uint actionId, ulong unknown);
+ public partial int GetEventHandlers(EventHandler** outHandlers);
+
+ /// Changes the currently playing timelines based on the difference between oldSharedTimelineState and newSharedTimelineState.
+ /// The new SharedTimelineState value.
+ /// If we should check if the layout is ready before continuing.
+ /// Unused and can be left empty.
+ [MemberFunction("E8 ?? ?? ?? ?? 49 8B 06 44 0F B6 C5")]
+ public partial void SetSharedTimelineState(ushort sharedTimelineState, bool checkIfLayoutReady, ulong unused);
}
diff --git a/FFXIVClientStructs/FFXIV/Client/Game/Object/GameObject.cs b/FFXIVClientStructs/FFXIV/Client/Game/Object/GameObject.cs
index 03444ff933..1a7c9a37e2 100644
--- a/FFXIVClientStructs/FFXIV/Client/Game/Object/GameObject.cs
+++ b/FFXIVClientStructs/FFXIV/Client/Game/Object/GameObject.cs
@@ -124,6 +124,18 @@ public unsafe partial struct GameObject {
[VirtualFunction(17)]
public partial void SetDrawObject(DrawObject* drawObject);
+ /// Start playing this timeline.
+ [VirtualFunction(20)]
+ public partial bool PlayTimeline(uint index, uint a2);
+
+ /// Stops this timeline's playback.
+ [VirtualFunction(21)]
+ public partial bool StopTimeline(uint index);
+
+ /// Checks if this timeline is currently playing.
+ [VirtualFunction(22)]
+ public partial bool IsTimelinePlaying(uint index);
+
[VirtualFunction(23)]
public partial DrawObject* GetDrawObject();
@@ -147,6 +159,12 @@ public unsafe partial struct GameObject {
[VirtualFunction(48)]
public partial uint GetNameId();
+ /// Changes the currently playing timelines based on the difference between oldSharedTimelineState and newSharedTimelineState.
+ /// The old SharedTimelineState value.
+ /// The new SharedTimelineState value.
+ [VirtualFunction(49)]
+ public partial void UpdateSharedTimelineState(ushort oldSharedTimelineState, ushort newSharedTimelineState);
+
[VirtualFunction(54)]
public partial TargetType GetTargetType();
@@ -253,6 +271,10 @@ public unsafe partial struct GameObject {
[MemberFunction("E8 ?? ?? ?? ?? 48 85 FF 0F 84 ?? ?? ?? ?? F3 0F 10 97")]
public partial Vector3* GetNamePlateWorldPosition(Vector3* vector);
+ /// If we have a SharedGroupLayoutInstance and it's loaded.
+ [MemberFunction("E8 ?? ?? ?? ?? 84 C0 74 ?? 33 FF 48 89 B4 24")]
+ public partial bool IsSharedGroupLoaded();
+
[StructLayout(LayoutKind.Explicit, Size = 0x08)]
public struct NamePlateColors {
[FieldOffset(0x00), CExporterIgnore] public ulong Data;
diff --git a/FFXIVClientStructs/FFXIV/Client/LayoutEngine/Group/SharedGroupLayoutInstance.cs b/FFXIVClientStructs/FFXIV/Client/LayoutEngine/Group/SharedGroupLayoutInstance.cs
index 9589c845d1..135ab14c44 100644
--- a/FFXIVClientStructs/FFXIV/Client/LayoutEngine/Group/SharedGroupLayoutInstance.cs
+++ b/FFXIVClientStructs/FFXIV/Client/LayoutEngine/Group/SharedGroupLayoutInstance.cs
@@ -46,7 +46,11 @@ public unsafe partial struct SharedGroupLayoutInstance {
[FieldOffset(0x120)] public uint PrefabFlags1; // 0x1 = load started; 0x3 = load failed or contents added; 0x4 = failed to add contents
[FieldOffset(0x12C)] public uint PrefabFlags2; // 0x8 = colliders active
- // [FieldOffset(0x14C), FixedSizeArray] internal FixedSizeArray16 _timelineIndices; // used by EventObjAnimation ActorControl packet, some kind of simple lookup table
+ /// Used by EventObjAnimation ActorControl packet, it's some kind of simple lookup table.
+ [FieldOffset(0x154), FixedSizeArray] internal FixedSizeArray16 _timelineIndices;
+
+ /// Set in PlayAnimation, it's an index into TimelineIndices.
+ [FieldOffset(0x16D)] public byte PlayingTimelineIndex;
[MemberFunction("E8 ?? ?? ?? ?? 41 FF C7 4D 8D 76 ?? 44 3B 7B")]
public partial bool InitAnimationHandlers(void* fileData);
@@ -71,6 +75,27 @@ public unsafe partial struct SharedGroupLayoutInstance {
[MemberFunction("33 C0 81 F9 ?? ?? ?? ?? 0F 42 C1")]
public static partial ByteColor* GetObjectStainColorByIndex(byte stainIndex);
+ /// Checks if this timeline is currently playing.
+ /// Stops this timeline's playback.
+ [MemberFunction("E8 ?? ?? ?? ?? 85 EF 74 ?? 84 C0")]
+ public partial bool IsTimelinePlaying(uint index);
+
+ /// Stops this timeline's playback.
+ /// Index into TimelineIndices.
+ [MemberFunction("E8 ?? ?? ?? ?? EB ?? 41 80 F9")]
+ public partial bool StopTimeline(uint index);
+
+ /// Start playing this timeline.
+ /// Index into TimelineIndices.
+ ///
+ [MemberFunction("E8 ?? ?? ?? ?? 0F B6 B6")]
+ public partial bool PlayTimeline(uint index, uint a1);
+
+ /// Checks if the timeline index is in range, and actually backed by something.
+ /// Index into TimelineIndices.
+ [MemberFunction("E8 ?? ?? ?? ?? 84 C0 74 ?? 3B FD")]
+ public partial bool IsTimelineIndexValid(uint index);
+
///
/// Attempts to set the stain on the models in this group.
///
diff --git a/FFXIVClientStructs/FFXIV/Client/LayoutEngine/ILayoutInstance.cs b/FFXIVClientStructs/FFXIV/Client/LayoutEngine/ILayoutInstance.cs
index 7c5bbf9117..6cc78071be 100644
--- a/FFXIVClientStructs/FFXIV/Client/LayoutEngine/ILayoutInstance.cs
+++ b/FFXIVClientStructs/FFXIV/Client/LayoutEngine/ILayoutInstance.cs
@@ -69,6 +69,10 @@ public unsafe partial struct Identifier {
[VirtualFunction(21)]
public partial bool HavePrimary();
+ /// If the primary object has been loaded.
+ [VirtualFunction(21)]
+ public partial bool IsPrimaryLoaded();
+
[VirtualFunction(23)]
public partial Graphics.Scene.Object* GetGraphics();
@@ -151,6 +155,10 @@ public unsafe partial struct Identifier {
[VirtualFunction(72)]
public partial Vector4* GetBoundingSphereImpl(Vector4* result);
+ /// If we have a primary object and its been loaded. Basically HavePrimary() and IsPrimaryLoaded() in one call.
+ [MemberFunction("E8 ?? ?? ?? ?? 3C ?? 75 ?? 8B D5")]
+ public partial bool IsPrimaryReady();
+
// vf73: getWorldBB, uses AABB with padded vec3's
// vf74: get transform split into RT matrix + scale
diff --git a/FFXIVClientStructs/FFXIV/Client/Network/PacketDispatcher.cs b/FFXIVClientStructs/FFXIV/Client/Network/PacketDispatcher.cs
index 60e577b6d1..f0782d17b6 100644
--- a/FFXIVClientStructs/FFXIV/Client/Network/PacketDispatcher.cs
+++ b/FFXIVClientStructs/FFXIV/Client/Network/PacketDispatcher.cs
@@ -93,6 +93,9 @@ public static partial void HandleActorControlPacket(
[MemberFunction("40 53 57 48 81 EC ?? ?? ?? ?? 48 8B FA 8B")]
public static partial void HandleActorCastPacket(uint entityId, ActorCastPacket* packet);
+ [MemberFunction("E8 ?? ?? ?? ?? B0 ?? 48 8B 5C 24 ?? 48 8B 74 24 ?? 48 83 C4 ?? 5F C3 0F B6 7B")]
+ public static partial void HandleMapEffectPacket(MapEffectPacket* packet);
+
[VirtualFunction(1)]
public partial void OnReceivePacket(uint targetId, nint packet);
}
diff --git a/ida/data.yml b/ida/data.yml
index 1d68a8df71..66c9ada98a 100644
--- a/ida/data.yml
+++ b/ida/data.yml
@@ -9636,6 +9636,7 @@ classes:
17: GetTransform
18: SetTransform
21: HavePrimary
+ 22: IsPrimaryLoaded
23: GetGraphics
24: GetGraphics2
25: SetGraphics
@@ -9663,6 +9664,8 @@ classes:
70: GetTransformImpl
71: SetTransformImpl
72: GetBoundingSphereImpl
+ funcs:
+ 0x1406FA0E0: IsPrimaryReady
Client::LayoutEngine::LayoutWorld:
instances:
- ea: 0x142A8BB60
@@ -9870,6 +9873,10 @@ classes:
0x1407077C0: InitAnimationHandlers
0x1407071A0: InitTimelines
0x1406FE9F0: ReapplyStain
+ 0x1407065C0: IsTimelineIndexValid
+ 0x140706420: PlayTimeline
+ 0x140706520: StopTimeline
+ 0x1407065F0: IsTimelinePlaying
0x1407985B0: GetObjectStainColorByIndex # static
Client::LayoutEngine::Layer::TreasureLayoutInstance:
vtbls:
@@ -11606,16 +11613,19 @@ classes:
15: SetTargetStatus
16: SetDistance
17: SetDrawObject
+ 20: PlayTimeline
+ 21: StopTimeline
+ 22: IsTimelinePlaying
23: GetDrawObject
24: GetCharacterBase
25: UpdateRadius
26: Highlight
- #30: TryGetEventHandler
30: GetEventHandlersImpl
34: SetReadyToDraw
36: Update
47: GetCenterPosition
48: GetNameId
+ 49: UpdateSharedTimelineState
54: GetTargetType
55: PositionModified
56: RotationModified
@@ -11641,6 +11651,7 @@ classes:
0x1408985D0: UpdateVisualRotation
0x140898780: UpdateVisualScale
0x140898AB0: GetEventHandlers
+ 0x1408991E0: IsSharedGroupLoaded
0x140899230: IsReadyToDraw
0x140899B70: SetEventId
0x140899BE0: SetFateId
@@ -16034,6 +16045,8 @@ classes:
232: GetOccupiedConditionId
233: GetOccupiedPermissionId
251: GetScriptVersion
+ 253: UpdateSharedTimelineState
+ 254: UpdateTimelinesByBitmask
258: GetDescription
259: GetReliefText
260: GetTimeRemaining # pass current unix timestamp as a2
@@ -17990,6 +18003,7 @@ classes:
funcs:
0x140B6D8A0: ctor
0x140B6F470: ApplyMapEffect
+ 0x140B6F2E0: PlayMapEffectTimeline
0x140B703F0: IsPlayCutscene
Client::Game::InstanceContent::InstanceContentDirector:
vtbls:
@@ -26219,7 +26233,10 @@ classes:
- ea: 0x1422C4DB0
base: Client::Game::Object::GameObject
funcs:
+ 0x140898A40: GetEventHandlers
+ 0x14174DCC0: SetSharedTimelineState
0x14174DE10: PlayAnimation
+ 0x14174DF40: UpdateTimelinesByBitmask
Client::Game::Object::Treasure:
vtbls:
- ea: 0x1422C5020