Fly text support

This commit is contained in:
liam 2021-08-10 15:02:37 -04:00
parent 5c4550762b
commit 9fe8395287
7 changed files with 653 additions and 0 deletions

View file

@ -49,6 +49,7 @@
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002EFormat_002ESettingsUpgrade_002EAlignmentTabFillStyleMigration/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002EFormat_002ESettingsUpgrade_002EAlignmentTabFillStyleMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=clientopcode/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=clientopcode/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Dalamud/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Dalamud/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Flytext/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Gpose/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Gpose/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=lumina/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=lumina/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Materia/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Materia/@EntryIndexedValue">True</s:Boolean>

View file

@ -0,0 +1,321 @@
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Hooking;
using FFXIVClientStructs.FFXIV.Client.UI;
using Serilog;
namespace Dalamud.Game.Internal.Gui
{
/// <summary>
/// This class facilitates interacting with and creating native in-game "fly text".
/// </summary>
public sealed class FlyTextGui : IDisposable
{
/// <summary>
/// The native function responsible for adding fly text to the UI. See <see cref="FlyTextGuiAddressResolver.AddFlyText"/>.
/// </summary>
private readonly AddFlyTextDelegate addFlyTextNative;
/// <summary>
/// The hook that fires when the game creates a fly text element. See <see cref="FlyTextGuiAddressResolver.CreateFlyText"/>.
/// </summary>
private readonly Hook<CreateFlyTextDelegate> createFlyTextHook;
private readonly Stopwatch hookTimer;
/// <summary>
/// Initializes a new instance of the <see cref="FlyTextGui"/> class.
/// </summary>
/// <param name="scanner">The SigScanner instance.</param>
/// <param name="dalamud">The Dalamud instance.</param>
internal FlyTextGui(SigScanner scanner, Dalamud dalamud)
{
this.Dalamud = dalamud;
this.hookTimer = new Stopwatch();
this.Address = new FlyTextGuiAddressResolver();
this.Address.Setup(scanner);
this.addFlyTextNative = Marshal.GetDelegateForFunctionPointer<AddFlyTextDelegate>(this.Address.AddFlyText);
this.createFlyTextHook = new Hook<CreateFlyTextDelegate>(this.Address.CreateFlyText, this.CreateFlyTextDetour);
}
/// <summary>
/// The delegate defining the type for the FlyText event.
/// </summary>
/// <param name="kind">The FlyTextKind. See <see cref="FlyTextKind"/>.</param>
/// <param name="val1">Value1 passed to the native flytext function.</param>
/// <param name="val2">Value2 passed to the native flytext function. Seems unused.</param>
/// <param name="text1">Text1 passed to the native flytext function.</param>
/// <param name="text2">Text2 passed to the native flytext function.</param>
/// <param name="color">Color passed to the native flytext function. Changes flytext color.</param>
/// <param name="icon">Icon ID passed to the native flytext function. Only displays with select FlyTextKind.</param>
/// <param name="yOffset">The vertical offset to place the flytext at. 0 is default. Negative values result
/// in text appearing higher on the screen. This does not change where the element begins to fade.</param>
/// <param name="handled">Whether this flytext has been handled. If a subscriber sets this to true, the FlyText will not appear.</param>
public delegate void FlyTextDelegate(
ref FlyTextKind kind,
ref int val1,
ref int val2,
ref SeString text1,
ref SeString text2,
ref uint color,
ref uint icon,
ref float yOffset,
ref bool handled);
/// <summary>
/// Private delegate for the native CreateFlyText function's hook.
/// </summary>
private delegate IntPtr CreateFlyTextDelegate(
IntPtr addonFlyText,
int kind,
int val1,
int val2,
IntPtr text2,
uint color,
uint icon,
IntPtr text1,
float unk3);
/// <summary>
/// Private delegate for the native AddFlyText function pointer.
/// </summary>
private delegate void AddFlyTextDelegate(
IntPtr addonFlyText,
uint actorIndex,
uint messageMax,
IntPtr numbers,
uint offsetNum,
uint offsetNumMax,
IntPtr strings,
uint offsetStr,
uint offsetStrMax,
int unknown);
/// <summary>
/// The FlyText event that can be subscribed to.
/// </summary>
public event FlyTextDelegate? OnFlyText;
private Dalamud Dalamud { get; }
private FlyTextGuiAddressResolver Address { get; }
/// <summary>
/// Disposes of managed and unmanaged resources.
/// </summary>
public void Dispose()
{
this.createFlyTextHook.Disable();
this.createFlyTextHook.Dispose();
}
/// <summary>
/// Displays a fly text in-game on the local player.
/// </summary>
/// <param name="kind">The FlyTextKind. See <see cref="FlyTextKind"/>.</param>
/// <param name="actorIndex">The index of the actor to place flytext on. Indexing unknown. 1 places flytext on local player.</param>
/// <param name="val1">Value1 passed to the native flytext function.</param>
/// <param name="val2">Value2 passed to the native flytext function. Seems unused.</param>
/// <param name="text1">Text1 passed to the native flytext function.</param>
/// <param name="text2">Text2 passed to the native flytext function.</param>
/// <param name="color">Color passed to the native flytext function. Changes flytext color.</param>
/// <param name="icon">Icon ID passed to the native flytext function. Only displays with select FlyTextKind.</param>
public unsafe void AddFlyText(FlyTextKind kind, uint actorIndex, uint val1, uint val2, SeString text1, SeString text2, uint color, uint icon)
{
// Known valid flytext region within the atk arrays
int numIndex = 28;
int strIndex = 25;
uint numOffset = 147;
uint strOffset = 28;
// Get the UI module and flytext addon pointers
var ui = (UIModule*)this.Dalamud.Framework.Gui.GetUIModule();
var flytext = this.Dalamud.Framework.Gui.GetUiObjectByName("_FlyText", 1);
if (ui == null || flytext == IntPtr.Zero)
return;
// Get the number and string arrays we need
var atkArrayDataHolder = ui->RaptureAtkModule.AtkModule.AtkArrayDataHolder;
var numArray = atkArrayDataHolder._NumberArrays[numIndex];
var strArray = atkArrayDataHolder._StringArrays[strIndex];
// Write the values to the arrays using a known valid flytext region
// Whether or not to enable this set of values for displaying flytext
numArray->IntArray[numOffset + 0] = 1; // Some kind of "Enabled" flag for this section
numArray->IntArray[numOffset + 1] = (int)kind;
numArray->IntArray[numOffset + 2] = unchecked((int)val1);
numArray->IntArray[numOffset + 3] = unchecked((int)val2);
numArray->IntArray[numOffset + 4] = 5; // Unknown
numArray->IntArray[numOffset + 5] = unchecked((int)color);
numArray->IntArray[numOffset + 6] = unchecked((int)icon);
numArray->IntArray[numOffset + 7] = 0; // Unknown
numArray->IntArray[numOffset + 8] = 0; // Unknown, has something to do with yOffset
fixed (byte* pText1 = text1.Encode())
{
fixed (byte* pText2 = text2.Encode())
{
strArray->StringArray[strOffset + 0] = pText1;
strArray->StringArray[strOffset + 1] = pText2;
this.addFlyTextNative(
flytext,
actorIndex,
1,
(IntPtr)numArray,
numOffset,
9,
(IntPtr)strArray,
strOffset,
2,
0);
}
}
}
/// <summary>
/// Enables this module.
/// </summary>
internal void Enable()
{
this.createFlyTextHook.Enable();
}
private IntPtr CreateFlyTextDetour(
IntPtr addonFlyText,
int kind,
int val1,
int val2,
IntPtr text2,
uint color,
uint icon,
IntPtr text1,
float yOffset)
{
var retVal = IntPtr.Zero;
try
{
this.hookTimer.Restart();
Log.Verbose("[FlyText] Enter CreateFlyText detour!");
var handled = false;
var tmpKind = (FlyTextKind)kind;
var tmpVal1 = val1;
var tmpVal2 = val2;
var tmpText1 = this.Dalamud.SeStringManager.Parse(text1);
var tmpText2 = this.Dalamud.SeStringManager.Parse(text2);
var tmpColor = color;
var tmpIcon = icon;
var tmpYOffset = yOffset;
var cmpText1 = tmpText1.ToString();
var cmpText2 = tmpText2.ToString();
Log.Verbose($"[FlyText] Called with addonFlyText({addonFlyText.ToInt64():X}) " +
$"kind({((FlyTextKind)kind).ToString()}) val1({val1}) val2({val2}) " +
$"text1({text1.ToInt64():X}, \"{tmpText1}\") text2({text2.ToInt64():X}, \"{tmpText2}\") " +
$"color({color:X}) icon({icon}) yOffset({yOffset})");
Log.Verbose("[FlyText] Calling flytext events!");
this.OnFlyText?.Invoke(
ref tmpKind,
ref tmpVal1,
ref tmpVal2,
ref tmpText1,
ref tmpText2,
ref tmpColor,
ref tmpIcon,
ref tmpYOffset,
ref handled);
// If handled, ignore the original call
if (handled)
{
Log.Verbose("[FlyText] FlyText was handled.");
// Returning null to AddFlyText from CreateFlyText will result
// in the operation being dropped entirely.
return IntPtr.Zero;
}
// Check if any values have changed
var dirty = tmpKind != (FlyTextKind)kind ||
tmpVal1 != val1 ||
tmpVal2 != val2 ||
tmpText1.ToString() != cmpText1 ||
tmpText2.ToString() != cmpText2 ||
tmpColor != color ||
tmpIcon != icon ||
Math.Abs(tmpYOffset - yOffset) > float.Epsilon;
// If not dirty, make the original call
if (!dirty)
{
Log.Verbose("[FlyText] Calling flytext with original args.");
return this.createFlyTextHook.Original(addonFlyText, kind, val1, val2, text2, color, icon, text1, yOffset);
}
var terminated1 = Terminate(tmpText1.Encode());
var terminated2 = Terminate(tmpText2.Encode());
var pText1 = Marshal.AllocHGlobal(terminated1.Length);
var pText2 = Marshal.AllocHGlobal(terminated2.Length);
Marshal.Copy(terminated1, 0, pText1, terminated1.Length);
Marshal.Copy(terminated2, 0, pText2, terminated2.Length);
Log.Verbose("[FlyText] Allocated and set strings.");
retVal = this.createFlyTextHook.Original(
addonFlyText,
(int)tmpKind,
tmpVal1,
tmpVal2,
pText2,
tmpColor,
tmpIcon,
pText1,
tmpYOffset);
Log.Verbose("[FlyText] Returned from original. Delaying free task.");
Task.Delay(2000).ContinueWith(_ =>
{
try
{
Marshal.FreeHGlobal(pText1);
Marshal.FreeHGlobal(pText2);
Log.Verbose("[FlyText] Freed strings.");
}
catch (Exception e)
{
Log.Verbose(e, "[FlyText] Exception occurred freeing strings in task.");
}
});
this.hookTimer.Stop();
Log.Verbose($"[FlyText] Hook took {this.hookTimer.ElapsedTicks} ticks, {this.hookTimer.ElapsedMilliseconds}ms.");
}
catch (Exception e)
{
Log.Error(e, "Exception occurred in CreateFlyTextDetour!");
}
return retVal;
}
private static byte[] Terminate(byte[] source)
{
var terminated = new byte[source.Length + 1];
Array.Copy(source, 0, terminated, 0, source.Length);
terminated[^1] = 0;
return terminated;
}
}
}

View file

@ -0,0 +1,32 @@
using System;
namespace Dalamud.Game.Internal.Gui
{
/// <summary>
/// An address resolver for the <see cref="FlyTextGui"/> class.
/// </summary>
public class FlyTextGuiAddressResolver : BaseAddressResolver
{
/// <summary>
/// Gets the address of the native AddFlyText method, which occurs
/// when the game adds fly text elements to the UI. Multiple fly text
/// elements can be added in a single AddFlyText call.
/// </summary>
public IntPtr AddFlyText { get; private set; }
/// <summary>
/// Gets the address of the native CreateFlyText method, which occurs
/// when the game creates a new fly text element. This method is called
/// once per fly text element, and can be called multiple times per
/// AddFlyText call.
/// </summary>
public IntPtr CreateFlyText { get; private set; }
/// <inheritdoc/>
protected override void Setup64Bit(SigScanner sig)
{
this.AddFlyText = sig.ScanText("E8 ?? ?? ?? ?? FF C7 41 D1 C7");
this.CreateFlyText = sig.ScanText("48 89 74 24 ?? 48 89 7C 24 ?? 41 56 48 83 EC 40 48 63 FA");
}
}
}

View file

@ -0,0 +1,214 @@
namespace Dalamud.Game.Internal.Gui
{
/// <summary>
/// Enum of FlyTextKind values. Members suffixed with
/// a number seem to be a duplicate, or perform duplicate behavior.
/// </summary>
public enum FlyTextKind
{
/// <summary>
/// Val1 in serif font, Text2 in sans-serif as subtitle.
/// Used for autos and incoming DoTs.
/// </summary>
AutoAttack = 0,
/// <summary>
/// Val1 in serif font, Text2 in sans-serif as subtitle.
/// Does a bounce effect on appearance.
/// </summary>
DirectHit = 1,
/// <summary>
/// Val1 in larger serif font with exclamation, with Text2
/// in sans-serif as subtitle. Does a bigger bounce effect on appearance.
/// </summary>
CriticalHit = 2,
/// <summary>
/// Val1 in even larger serif font with 2 exclamations, Text2 in
/// sans-serif as subtitle. Does a large bounce effect on appearance.
/// Does not scroll up or down the screen.
/// </summary>
CriticalDirectHit = 3,
/// <summary>
/// AutoAttack with sans-serif Text1 to the left of the Val1.
/// </summary>
NamedAttack = 4,
/// <summary>
/// DirectHit with sans-serif Text1 to the left of the Val1.
/// </summary>
NamedDirectHit = 5,
/// <summary>
/// CriticalHit with sans-serif Text1 to the left of the Val1.
/// </summary>
NamedCriticalHit = 6,
/// <summary>
/// CriticalDirectHit with sans-serif Text1 to the left of the Val1.
/// </summary>
NamedCriticalDirectHit = 7,
/// <summary>
/// All caps, serif MISS.
/// </summary>
Miss = 8,
/// <summary>
/// Sans-serif Text1 next to all caps serif MISS.
/// </summary>
NamedMiss = 9,
/// <summary>
/// All caps serif DODGE.
/// </summary>
Dodge = 10,
/// <summary>
/// Sans-serif Text1 next to all caps serif DODGE.
/// </summary>
NamedDodge = 11,
/// <summary>
/// Icon next to sans-serif Text1.
/// </summary>
NamedIcon = 12,
NamedIcon2 = 13,
/// <summary>
/// Serif Val1 with all caps condensed font EXP with Text2 in sans-serif as subtitle.
/// </summary>
Exp = 14,
/// <summary>
/// Sans-serif Text1 next to serif Val1 with all caps condensed font MP with Text2 in sans-serif as subtitle.
/// </summary>
NamedMp = 15,
/// <summary>
/// Sans-serif Text1 next to serif Val1 with all caps condensed font TP with Text2 in sans-serif as subtitle.
/// </summary>
NamedTp = 16,
NamedAttack2 = 17,
NamedMp2 = 18,
NamedTp2 = 19,
/// <summary>
/// Sans-serif Text1 next to serif Val1 with all caps condensed font EP with Text2 in sans-serif as subtitle.
/// </summary>
NamedEp = 20,
/// <summary>
/// Displays nothing.
/// </summary>
None = 21,
/// <summary>
/// All caps serif INVULNERABLE.
/// </summary>
Invulnerable = 22,
/// <summary>
/// All caps sans-serif condensed font INTERRUPTED!
/// Does a large bounce effect on appearance.
/// Does not scroll up or down the screen.
/// </summary>
Interrupted = 23,
/// <summary>
/// AutoAttack with no Text2.
/// </summary>
AutoAttackNoText = 24,
AutoAttackNoText2 = 25,
CriticalHit2 = 26,
AutoAttackNoText3 = 27,
NamedCriticalHit2 = 28,
/// <summary>
/// Same as NamedCriticalHit with a green (cannot change) MP in condensed font to the right of Val1.
/// Does a jiggle effect to the right on appearance.
/// </summary>
NamedCriticalHitWithMp = 29,
/// <summary>
/// Same as NamedCriticalHit with a yellow (cannot change) TP in condensed font to the right of Val1.
/// Does a jiggle effect to the right on appearance.
/// </summary>
NamedCriticalHitWithTp = 30,
/// <summary>
/// Same as NamedIcon with sans-serif "has no effect!" to the right.
/// </summary>
NamedIconHasNoEffect = 31,
/// <summary>
/// Same as NamedIcon but Text1 is slightly faded. Used for buff expiration.
/// </summary>
NamedIconFaded = 32,
NamedIconFaded2 = 33,
/// <summary>
/// Text1 in sans-serif font.
/// </summary>
Named = 34,
/// <summary>
/// Same as NamedIcon with sans-serif "(fully resisted)" to the right.
/// </summary>
NamedIconFullyResisted = 35,
/// <summary>
/// All caps serif 'INCAPACITATED!'.
/// </summary>
Incapacitated = 36,
/// <summary>
/// Text1 with sans-serif "(fully resisted)" to the right.
/// </summary>
NamedFullyResisted = 37,
/// <summary>
/// Text1 with sans-serif "has no effect!" to the right.
/// </summary>
NamedHasNoEffect = 38,
NamedAttack3 = 39,
NamedMp3 = 40,
NamedTp3 = 41,
/// <summary>
/// Same as NamedIcon with serif "INVULNERABLE!" beneath the Text1.
/// </summary>
NamedIconInvulnerable = 42,
/// <summary>
/// All caps serif RESIST.
/// </summary>
Resist = 43,
/// <summary>
/// Same as NamedIcon but places the given icon in the item icon outline.
/// </summary>
NamedIconWithItemOutline = 44,
AutoAttackNoText4 = 45,
CriticalHit3 = 46,
/// <summary>
/// All caps serif REFLECT.
/// </summary>
Reflect = 47,
/// <summary>
/// All caps serif REFLECTED.
/// </summary>
Reflected = 48,
DirectHit2 = 49,
CriticalHit5 = 50,
CriticalDirectHit2 = 51,
}
}

View file

@ -66,6 +66,7 @@ namespace Dalamud.Game.Internal.Gui
this.Chat = new ChatGui(this.address.ChatManager, scanner, dalamud); this.Chat = new ChatGui(this.address.ChatManager, scanner, dalamud);
this.PartyFinder = new PartyFinderGui(scanner, dalamud); this.PartyFinder = new PartyFinderGui(scanner, dalamud);
this.Toast = new ToastGui(scanner, dalamud); this.Toast = new ToastGui(scanner, dalamud);
this.FlyText = new FlyTextGui(scanner, dalamud);
this.setGlobalBgmHook = new Hook<SetGlobalBgmDelegate>(this.address.SetGlobalBgm, this.HandleSetGlobalBgmDetour); this.setGlobalBgmHook = new Hook<SetGlobalBgmDelegate>(this.address.SetGlobalBgm, this.HandleSetGlobalBgmDetour);
this.handleItemHoverHook = new Hook<HandleItemHoverDelegate>(this.address.HandleItemHover, this.HandleItemHoverDetour); this.handleItemHoverHook = new Hook<HandleItemHoverDelegate>(this.address.HandleItemHover, this.HandleItemHoverDetour);
@ -161,6 +162,11 @@ namespace Dalamud.Game.Internal.Gui
/// </summary> /// </summary>
public ToastGui Toast { get; private set; } public ToastGui Toast { get; private set; }
/// <summary>
/// Gets the <see cref="FlyText"/> instance.
/// </summary>
public FlyTextGui FlyText { get; private set; }
/// <summary> /// <summary>
/// Gets a value indicating whether the game UI is hidden. /// Gets a value indicating whether the game UI is hidden.
/// </summary> /// </summary>
@ -452,6 +458,7 @@ namespace Dalamud.Game.Internal.Gui
{ {
this.Chat.Enable(); this.Chat.Enable();
this.Toast.Enable(); this.Toast.Enable();
this.FlyText.Enable();
this.PartyFinder.Enable(); this.PartyFinder.Enable();
this.setGlobalBgmHook.Enable(); this.setGlobalBgmHook.Enable();
this.handleItemHoverHook.Enable(); this.handleItemHoverHook.Enable();
@ -468,6 +475,7 @@ namespace Dalamud.Game.Internal.Gui
{ {
this.Chat.Dispose(); this.Chat.Dispose();
this.Toast.Dispose(); this.Toast.Dispose();
this.FlyText.Dispose();
this.PartyFinder.Dispose(); this.PartyFinder.Dispose();
this.setGlobalBgmHook.Dispose(); this.setGlobalBgmHook.Dispose();
this.handleItemHoverHook.Dispose(); this.handleItemHoverHook.Dispose();

View file

@ -1,6 +1,8 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices;
using Dalamud.Data; using Dalamud.Data;
using Dalamud.Game.Text.SeStringHandling.Payloads; using Dalamud.Game.Text.SeStringHandling.Payloads;
@ -47,6 +49,24 @@ namespace Dalamud.Game.Text.SeStringHandling
return new SeString(payloads); return new SeString(payloads);
} }
/// <summary>
/// Parse a binary game message into an SeString.
/// </summary>
/// <param name="bytePtr">Pointer to a binary message payload data in SE's internal format.</param>
/// <returns>An SeString containing parsed Payload objects for each payload in the data.</returns>
public SeString Parse(IntPtr bytePtr)
{
var bytes = new List<byte>();
byte read = Marshal.ReadByte(bytePtr, 0);
for (int ofs = 1; read != 0; ofs++)
{
bytes.Add(read);
read = Marshal.ReadByte(bytePtr, ofs);
}
return this.Parse(bytes.ToArray());
}
/// <summary> /// <summary>
/// Creates an SeString representing an entire Payload chain that can be used to link an item in the chat log. /// Creates an SeString representing an entire Payload chain that can be used to link an item in the chat log.
/// </summary> /// </summary>

View file

@ -9,6 +9,7 @@ using Dalamud.Game.ClientState.Actors.Types;
using Dalamud.Game.ClientState.Actors.Types.NonPlayer; using Dalamud.Game.ClientState.Actors.Types.NonPlayer;
using Dalamud.Game.ClientState.Structs.JobGauge; using Dalamud.Game.ClientState.Structs.JobGauge;
using Dalamud.Game.Internal; using Dalamud.Game.Internal;
using Dalamud.Game.Internal.Gui;
using Dalamud.Game.Internal.Gui.Addon; using Dalamud.Game.Internal.Gui.Addon;
using Dalamud.Game.Internal.Gui.Toast; using Dalamud.Game.Internal.Gui.Toast;
using Dalamud.Game.Text; using Dalamud.Game.Text;
@ -49,6 +50,7 @@ namespace Dalamud.Interface.Internal.Windows
private UIDebug addonInspector = null; private UIDebug addonInspector = null;
// Toast fields
private string inputTextToast = string.Empty; private string inputTextToast = string.Empty;
private int toastPosition = 0; private int toastPosition = 0;
private int toastSpeed = 0; private int toastSpeed = 0;
@ -57,6 +59,17 @@ namespace Dalamud.Interface.Internal.Windows
private int questToastIconId = 0; private int questToastIconId = 0;
private bool questToastCheckmark = false; private bool questToastCheckmark = false;
// Fly text fields
private int flyActor;
private FlyTextKind flyKind;
private int flyVal1;
private int flyVal2;
private string flyText1 = string.Empty;
private string flyText2 = string.Empty;
private int flyIcon;
private Vector4 flyColor = new(1, 0, 0, 1);
// ImGui fields
private string inputTexPath = string.Empty; private string inputTexPath = string.Empty;
private TextureWrap debugTex = null; private TextureWrap debugTex = null;
private Vector2 inputTexUv0 = Vector2.Zero; private Vector2 inputTexUv0 = Vector2.Zero;
@ -98,6 +111,7 @@ namespace Dalamud.Interface.Internal.Windows
StartInfo, StartInfo,
Target, Target,
Toast, Toast,
FlyText,
ImGui, ImGui,
Tex, Tex,
Gamepad, Gamepad,
@ -226,6 +240,10 @@ namespace Dalamud.Interface.Internal.Windows
this.DrawToast(); this.DrawToast();
break; break;
case DataKind.FlyText:
this.DrawFlyText();
break;
case DataKind.ImGui: case DataKind.ImGui:
this.DrawImGui(); this.DrawImGui();
break; break;
@ -687,6 +705,45 @@ namespace Dalamud.Interface.Internal.Windows
} }
} }
private void DrawFlyText()
{
if (ImGui.BeginCombo("Kind", this.flyKind.ToString()))
{
var names = Enum.GetNames(typeof(FlyTextKind));
for (int i = 0; i < names.Length; i++)
{
if (ImGui.Selectable($"{names[i]} ({i})"))
this.flyKind = (FlyTextKind)i;
}
ImGui.EndCombo();
}
ImGui.InputText("Text1", ref this.flyText1, 200);
ImGui.InputText("Text2", ref this.flyText2, 200);
ImGui.InputInt("Val1", ref this.flyVal1);
ImGui.InputInt("Val2", ref this.flyVal2);
ImGui.InputInt("Icon ID", ref this.flyIcon);
ImGui.ColorEdit4("Color", ref this.flyColor);
ImGui.InputInt("Actor Index", ref this.flyActor);
var sendColor = ImGui.ColorConvertFloat4ToU32(this.flyColor);
if (ImGui.Button("Send"))
{
this.dalamud.Framework.Gui.FlyText.AddFlyText(
this.flyKind,
unchecked((uint)this.flyActor),
unchecked((uint)this.flyVal1),
unchecked((uint)this.flyVal2),
this.flyText1,
this.flyText2,
sendColor,
unchecked((uint)this.flyIcon));
}
}
private void DrawImGui() private void DrawImGui()
{ {
ImGui.Text("Monitor count: " + ImGui.GetPlatformIO().Monitors.Size); ImGui.Text("Monitor count: " + ImGui.GetPlatformIO().Monitors.Size);