Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public unsafe partial struct Character {
[FieldOffset(0x1CE8)] public byte ActorControlFlags;

[FieldOffset(0x21E0)] public Balloon Balloon;
[FieldOffset(0x2260)] public NpcYellBalloon YellBalloon;

[FieldOffset(0x22E8)] public float Alpha;

Expand Down
159 changes: 159 additions & 0 deletions FFXIVClientStructs/FFXIV/Client/Game/NpcYellBalloon.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
using FFXIVClientStructs.FFXIV.Client.System.String;

namespace FFXIVClientStructs.FFXIV.Client.Game;

// Client::Game::NpcYellBalloon
// Probably part of Client::Game::Character::Character
[GenerateInterop]
[StructLayout(LayoutKind.Explicit, Size = 0x88)]
public unsafe partial struct NpcYellBalloon {

/// <summary>
/// The text that this balloon will display.
/// </summary>
/// <remarks>
/// Empty when balloon is <see cref="NpcYellBalloonState.Inactive"/>.
/// </remarks>
[FieldOffset(0x0)] public Utf8String Text;

/// <summary>
/// Pointer to the <see cref="Character.Character"/> that contains this balloon.
/// </summary>
[FieldOffset(0x68)] public Character.Character* Character;

/// <summary>
/// How much longer (in seconds) the balloon should remain open.
/// </summary>
/// <remarks>
/// Starting value set by OpenBalloon. Starts counting down once in <see cref="NpcYellBalloonState.Active"/> state. If this is set
/// to zero before the balloon is activated, the balloon will remain open indefinitely, subject to the value of <see cref="CloseType"/>.
/// </remarks>
[FieldOffset(0x70)] public float PlayTimer;

/// <summary>
/// How much longer (in seconds) before the balloon should actually be opened with AgentScreenLog.
/// </summary>
/// <remarks>
/// Starting value is set by OpenBalloon. Resets to one second when balloon exits <see cref="NpcYellBalloonState.Waiting"/> state.
/// </remarks>
[FieldOffset(0x74)] public float DelayTime;

/// <summary>
/// The balloon's current state.
/// </summary>
[FieldOffset(0x78)] public NpcYellBalloonState State;

/// <summary>
/// Controls how the balloon determines when to close.
/// </summary>
/// <remarks>
/// If OpenBalloon's reducedCloseChecks parameter is true, this is <see cref="NpcYellBalloonCloseType.ReducedCloseChecks"/>, else if
/// playTime is zero this is <see cref="NpcYellBalloonCloseType.HoldOpen"/>, else this is <see cref="NpcYellBalloonCloseType.Normal"/>.
/// </remarks>
[FieldOffset(0x7C)] public NpcYellBalloonCloseType CloseType;

/// <summary>
/// The bone index to which the balloon is attached on the character.
/// </summary>
[FieldOffset(0x80)] public byte ParentBone;

/// <summary>
/// Miscellaneous balloon behaviors.
/// </summary>
[FieldOffset(0x81)] public NpcYellBalloonFlags Flags;

/// <summary>
/// Prepares the balloon to be opened during the next applicable Update.
/// </summary>
/// <param name="str">A null-terminated string containing the text to display.</param>
/// <param name="playTime">Time in seconds that the balloon should remain visible. If zero, the balloon will remain open indefinitely (until <see cref="CloseBalloon"/> is called).</param>
/// <param name="softOpen">If this is true, the bubble will fade in more gently than the normal "popping" in.</param>
/// <param name="openDelay">Time in seconds to wait before actually opening the balloon.</param>
/// <param name="printToLog">Whether the balloon text should also be printed to the chat log.</param>
/// <param name="reducedCloseChecks">Skips certain non-timer checks for closing the balloon. Unknown purpose. Usually false.</param>
/// <param name="ignoreRangeCheck">Ignore whether the character is "in range" when checking whether to display the balloon.</param>
/// <param name="parentBone">The bone index to which the balloon is visually attached. A value of 25 is used if the specified bone does not exist.</param>
[MemberFunction("E8 ?? ?? ?? ?? 0F 28 B4 24 ?? ?? ?? ?? 48 8D 4D"), GenerateStringOverloads]
public partial void OpenBalloon(CStringPointer str, float playTime, bool softOpen, float openDelay, bool printToLog, bool reducedCloseChecks, bool ignoreRangeCheck, byte parentBone);

/// <summary>
/// Closes and resets the balloon.
/// </summary>
/// <remarks>
/// Only required if balloon is set to hold open (or needs to be closed early).
/// </remarks>
[MemberFunction("E8 ?? ?? ?? ?? 66 3B BB ?? ?? ?? ?? 75")]
public partial void CloseBalloon();
}

public enum NpcYellBalloonState : int {

/// <summary>
/// Balloon is inactive and in the default state.
/// </summary>
Inactive = 0,

/// <summary>
/// Balloon is waiting to open (until <see cref="NpcYellBalloon.DelayTime"/> expires).
/// </summary>
Waiting = 1,

/// <summary>
/// Balloon is open.
/// </summary>
Active = 2,

/// <summary>
/// Balloon is attempting to open.
/// </summary>
Activating = 3,
}

public enum NpcYellBalloonCloseType : int {

/// <summary>
/// Performs normal timer checks every active update cycle.
/// </summary>
Normal = 0,

/// <summary>
/// Ignore <see cref="NpcYellBalloon.PlayTimer"/> and hold open indefinitely until <see cref="NpcYellBalloon.CloseBalloon"/> is called.
/// </summary>
HoldOpen = 1,

/// <remarks>
/// Similar to <see cref="Normal"/>, but skips some non-<see cref="NpcYellBalloon.PlayTimer"/> checks.
/// </remarks>
ReducedCloseChecks = 2,
}

[Flags]
public enum NpcYellBalloonFlags : byte {
None = 0,

/// <summary>
/// All balloons will have this flag while in use.
/// </summary>
/// <remarks>
/// If this is not set, the balloon will not be opened (or will be immediately closed).
/// </remarks>
Valid = 1,

/// <summary>
/// The balloon fades in instead of opening with a "pop".
/// </summary>
/// <remarks>
/// Is passed as the bool third parameter to AgentScreenLog::OpenBalloon, and probably has the same effect as the Balloon EXD's boolean "Slowly" column.
/// </remarks>
SoftOpen = 2,

/// <remarks>
/// Call AgentScreenLog::OpenBalloon regardless of character range test result.
/// </remarks>
IgnoreRangeCheck = 4,

/// <remarks>
/// Also call RaptureLogModule::PrintMessage with balloon text when the balloon is opened.
/// </remarks>
PrintToLog = 8,
}
112 changes: 110 additions & 2 deletions FFXIVClientStructs/FFXIV/Client/Game/UI/NpcYell.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,113 @@
using FFXIVClientStructs.FFXIV.Client.Game.Object;
using FFXIVClientStructs.FFXIV.Client.System.String;

namespace FFXIVClientStructs.FFXIV.Client.Game.UI;

// Client::Game::UI::NpcYell
[StructLayout(LayoutKind.Explicit, Size = 0x1750)]
public struct NpcYell;
[GenerateInterop]
[StructLayout(LayoutKind.Explicit, Size = 0x2850)]
public partial struct NpcYell {

/// <remarks>
/// Entries at index <see cref="UnhandledYellCount"/> and above are stale.
/// </remarks>
[FieldOffset(0x48), FixedSizeArray] internal FixedSizeArray32<NpcYellSlot> _yellSlots;

/// <remarks>
/// Entries at index <see cref="UnhandledYellCount"/> and above are stale.
/// </remarks>
[FieldOffset(0x548), FixedSizeArray] internal FixedSizeArray32<NpcYellInfo> _yellInfo;

/// <summary>
/// The number of new <see cref="YellSlots"/> and <see cref="YellInfo"/> entries that have not yet been handled.
/// </summary>
[FieldOffset(0x2848)] public byte UnhandledYellCount;
[FieldOffset(0x2849)] private byte Unk_2849; // Looks like a counter that has something to do specifically with entries that show a BattleTalk.
[FieldOffset(0x284A)] private bool Unk_284A;

[StructLayout(LayoutKind.Explicit, Size = 0x28)]
public struct NpcYellSlot {
[FieldOffset(0x0)] public GameObjectId ObjectId;
[FieldOffset(0xC)] public uint NameId;
[FieldOffset(0x10)] private uint Unk_MessageParams; // A (probably) four-long array of integer parameters for the message string.
[FieldOffset(0x20)] private float Unk_20; // Probably a delay. Has effective frame delta subracted each update.
}

[StructLayout(LayoutKind.Explicit, Size = 0x118)]
public struct NpcYellInfo {
[FieldOffset(0x0)] public uint NpcYellRowId;
[FieldOffset(0x8)] public GameObjectId ObjectId;
[FieldOffset(0x10)] public uint NameId;
[FieldOffset(0x18)] public Utf8String Name;
[FieldOffset(0x80)] public Utf8String Message;
[FieldOffset(0xE8)] private uint Unk_MessageParams; // A (probably) four-long array of integer parameters for the message string.
[FieldOffset(0xF8)] private float Unk_F8; // Countdown timer of unknown use. Has effective frame delta subracted each update.

/// <summary>
/// How long (in seconds) to display the message as a balloon.
/// </summary>
/// <remarks>
/// Only used if <see cref="OutputType"/> includes <see cref="NpcYellOutputFlags.ShowBalloon"/>.
/// </remarks>
[FieldOffset(0xFC)] public float BalloonTime;

/// <summary>
/// How long (in seconds) to display the message in the BattleTalk addon.
/// </summary>
/// <remarks>
/// Only used if <see cref="OutputType"/> includes <see cref="NpcYellOutputFlags.ShowBattleTalk"/>.
/// </remarks>
[FieldOffset(0x100)] public float BattleTalkTime;

/// <summary>
/// The method(s) used to show the dialogue to the player.
/// </summary>
[FieldOffset(0x104)] public NpcYellOutputFlags OutputType;

/// <remarks>
/// Controls which UIModule::ShowBattleTalk overload is called.
/// </remarks>
[FieldOffset(0x108)] private uint Unk_108;

/// <summary>
/// The index of the character bone to which to attach the <see cref="NpcYellBalloon"/>.
/// </summary>
/// <remarks>
/// Only used if <see cref="OutputType"/> includes <see cref="NpcYellOutputFlags.ShowBalloon"/>.
/// </remarks>
[FieldOffset(0x10C)] public byte ParentBone;
[FieldOffset(0x110)] public NpcYellFlags Flags;
}

[Flags]
public enum NpcYellOutputFlags : byte {
None = 0,
PrintToLog = 1,
ShowBalloon = 2,
ShowBattleTalk = 4,
}

[Flags]
public enum NpcYellFlags : byte {
None = 0,
/// <summary>
/// Controls the corresponding flag in the <see cref="NpcYellBalloon"/>.
/// </summary>
SkipCloseChecks = 1,
/// <summary>
/// Controls the corresponding flag in the <see cref="NpcYellBalloon"/>.
/// </summary>
/// <remarks>
/// Also checked when formatting NPC name.
/// </remarks>
SkipRangeCheck = 2,
Unk_4 = 4,
Unk_8 = 8,
/// <remarks>
/// This is the default value after initialization.
/// </remarks>
Unk_10 = 0x10,
Unk_20 = 0x20,
Unk_40 = 0x40,
}
}
11 changes: 11 additions & 0 deletions ida/data.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1505,6 +1505,16 @@ classes:
0x14188EB00: Terminate
#oldfail 0x141831340: StartTimerMode # (Balloon* this, float timer, ushort id) if id == 1 use default id else use id
#oldfail 0x141831380: StartOtherMode # (Balloon* this, ushort id) id same as above
Client::Game::NpcYellBalloon:
funcs:
0x14188EC50: Reset
0x14188ECB0: Ctor
0x14188ED10: Dtor
0x14188ED70: Initialize # (this, chara*)
0x14188EDC0: Update
0x14188F0A0: OpenBalloon # (this, char*, float, bool, float, bool, bool, bool, byte)
0x14188F320: Reset2 # This is identical to Reset, but called from things outside of the class instead of from other member functions.
0x14188F360: CloseBalloon
Client::Game::Fate::FateManager:
instances:
- ea: 0x1429EC2B8
Expand Down Expand Up @@ -3178,6 +3188,7 @@ classes:
0x140D78D80: Dtor
0x140B61120: Initialize
0x140B61920: Update
0x140B62200: HandleYell
Client::Game::UI::CharaCard:
instances:
- ea: 0x1429E7C88
Expand Down