mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 10:17:22 +01:00
Fly text support
This commit is contained in:
parent
5c4550762b
commit
9fe8395287
7 changed files with 653 additions and 0 deletions
|
|
@ -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>
|
||||||
|
|
|
||||||
321
Dalamud/Game/Internal/Gui/FlyTextGui.cs
Normal file
321
Dalamud/Game/Internal/Gui/FlyTextGui.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
32
Dalamud/Game/Internal/Gui/FlyTextGuiAddressResolver.cs
Normal file
32
Dalamud/Game/Internal/Gui/FlyTextGuiAddressResolver.cs
Normal 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
214
Dalamud/Game/Internal/Gui/FlyTextKind.cs
Normal file
214
Dalamud/Game/Internal/Gui/FlyTextKind.cs
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue