feat: add linkshell shortcuts
This commit is contained in:
parent
9372ad35bc
commit
7df1d8818b
|
@ -126,4 +126,28 @@ internal static class InputChannelExt {
|
|||
.Where(id => id != null)
|
||||
.Cast<TextCommand>();
|
||||
}
|
||||
|
||||
internal static bool IsLinkshell(this InputChannel channel) => channel switch {
|
||||
InputChannel.Linkshell1 => true,
|
||||
InputChannel.Linkshell2 => true,
|
||||
InputChannel.Linkshell3 => true,
|
||||
InputChannel.Linkshell4 => true,
|
||||
InputChannel.Linkshell5 => true,
|
||||
InputChannel.Linkshell6 => true,
|
||||
InputChannel.Linkshell7 => true,
|
||||
InputChannel.Linkshell8 => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
internal static bool IsCrossLinkshell(this InputChannel channel) => channel switch {
|
||||
InputChannel.CrossLinkshell1 => true,
|
||||
InputChannel.CrossLinkshell2 => true,
|
||||
InputChannel.CrossLinkshell3 => true,
|
||||
InputChannel.CrossLinkshell4 => true,
|
||||
InputChannel.CrossLinkshell5 => true,
|
||||
InputChannel.CrossLinkshell6 => true,
|
||||
InputChannel.CrossLinkshell7 => true,
|
||||
InputChannel.CrossLinkshell8 => true,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ using Dalamud.Logging;
|
|||
using Dalamud.Memory;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Framework;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.String;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI.Shell;
|
||||
|
@ -46,7 +47,13 @@ internal sealed unsafe class Chat : IDisposable {
|
|||
private readonly delegate* unmanaged<Framework*, IntPtr> _getNetworkModule = null!;
|
||||
|
||||
[Signature("E8 ?? ?? ?? ?? 48 8B C8 E8 ?? ?? ?? ?? 45 8D 46 FB")]
|
||||
private readonly delegate* unmanaged<IntPtr, uint, Utf8String*> _getLinkshellName = null!;
|
||||
private readonly delegate* unmanaged<IntPtr, uint, Utf8String*> _getCrossLinkshellName = null!;
|
||||
|
||||
[Signature("3B 51 10 73 0F 8B C2 48 83 C0 0B")]
|
||||
private readonly delegate* unmanaged<IntPtr, uint, ulong*> _getLinkshellInfo = null!;
|
||||
|
||||
[Signature("E8 ?? ?? ?? ?? 4C 8B C0 FF C3")]
|
||||
private readonly delegate* unmanaged<IntPtr, ulong, byte*> _getLinkshellName = null!;
|
||||
|
||||
// Hooks
|
||||
|
||||
|
@ -92,6 +99,9 @@ internal sealed unsafe class Chat : IDisposable {
|
|||
[Signature("89 83 ?? ?? ?? ?? 48 8B 01 83 FE 13 7C 05 41 8B D4 EB 03 83 CA FF FF 90", Offset = 2)]
|
||||
private readonly int? _shellChannelOffset;
|
||||
|
||||
[Signature("4C 8D B6 ?? ?? ?? ?? 41 8B 1E 45 85 E4 74 7A 33 FF 8B EF 66 0F 1F 44 00", Offset = 3)]
|
||||
private readonly int? _linkshellCycleOffset;
|
||||
|
||||
#pragma warning restore 0649
|
||||
|
||||
// Events
|
||||
|
@ -130,15 +140,54 @@ internal sealed unsafe class Chat : IDisposable {
|
|||
}
|
||||
|
||||
internal string? GetLinkshellName(uint idx) {
|
||||
var infoProxy = this.Plugin.Functions.GetInfoProxyByIndex(2);
|
||||
if (infoProxy == IntPtr.Zero) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var lsInfo = this._getLinkshellInfo(infoProxy, idx);
|
||||
if (lsInfo == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var utf = this._getLinkshellName(infoProxy, *lsInfo);
|
||||
return utf == null ? null : MemoryHelper.ReadStringNullTerminated((IntPtr) utf);
|
||||
}
|
||||
|
||||
internal string? GetCrossLinkshellName(uint idx) {
|
||||
var infoProxy = this.Plugin.Functions.GetInfoProxyByIndex(26);
|
||||
if (infoProxy == IntPtr.Zero) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var utf = this._getLinkshellName(infoProxy, idx);
|
||||
var utf = this._getCrossLinkshellName(infoProxy, idx);
|
||||
return utf == null ? null : utf->ToString();
|
||||
}
|
||||
|
||||
internal ulong RotateLinkshellHistory(RotateMode mode) {
|
||||
if (mode == RotateMode.None && this._linkshellCycleOffset != null) {
|
||||
// for the branch at 6.08: 5E1680
|
||||
var uiModule = (IntPtr) Framework.Instance()->GetUiModule();
|
||||
*(int*) (uiModule + this._linkshellCycleOffset.Value) = -1;
|
||||
}
|
||||
|
||||
return RotateLinkshellHistoryInternal(189, mode);
|
||||
}
|
||||
|
||||
internal ulong RotateCrossLinkshellHistory(RotateMode mode) => RotateLinkshellHistoryInternal(191, mode);
|
||||
|
||||
private static ulong RotateLinkshellHistoryInternal(int vfunc, RotateMode mode) {
|
||||
var idx = mode switch {
|
||||
RotateMode.Forward => 1,
|
||||
RotateMode.Reverse => -1,
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
var uiModule = Framework.Instance()->GetUiModule();
|
||||
var cycleFunc = (delegate* unmanaged<UIModule*, int, ulong>) uiModule->vfunc[vfunc];
|
||||
return cycleFunc(uiModule, idx);
|
||||
}
|
||||
|
||||
private readonly Dictionary<string, Keybind> _keybinds = new();
|
||||
internal IReadOnlyDictionary<string, Keybind> Keybinds => this._keybinds;
|
||||
|
||||
|
|
|
@ -27,7 +27,6 @@ internal sealed class ChatLog : IUiComponent {
|
|||
private int _lastTab;
|
||||
private InputChannel? _tempChannel;
|
||||
private TellTarget? _tellTarget;
|
||||
private int _tellIdx;
|
||||
|
||||
private PayloadHandler PayloadHandler { get; }
|
||||
private Dictionary<string, ChatType> TextCommandChannels { get; } = new();
|
||||
|
@ -70,18 +69,17 @@ internal sealed class ChatLog : IUiComponent {
|
|||
|
||||
if (info.Channel is InputChannel.Tell) {
|
||||
if (info.Rotate != RotateMode.None) {
|
||||
this._tellIdx = prevTemp != InputChannel.Tell
|
||||
var idx = prevTemp != InputChannel.Tell
|
||||
? 0
|
||||
: info.Rotate == RotateMode.Reverse
|
||||
? -1
|
||||
: 1;
|
||||
|
||||
var tellInfo = this.Ui.Plugin.Functions.Chat.GetTellHistoryInfo(this._tellIdx);
|
||||
var tellInfo = this.Ui.Plugin.Functions.Chat.GetTellHistoryInfo(idx);
|
||||
if (tellInfo != null && reason != null) {
|
||||
this._tellTarget = new TellTarget(tellInfo.Name, (ushort) tellInfo.World, tellInfo.ContentId, reason.Value);
|
||||
}
|
||||
} else {
|
||||
this._tellIdx = 0;
|
||||
this._tellTarget = null;
|
||||
|
||||
if (target != null) {
|
||||
|
@ -89,9 +87,20 @@ internal sealed class ChatLog : IUiComponent {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
this._tellIdx = 0;
|
||||
this._tellTarget = null;
|
||||
}
|
||||
|
||||
var mode = prevTemp == null
|
||||
? RotateMode.None
|
||||
: info.Rotate;
|
||||
|
||||
if (info.Channel is InputChannel.Linkshell1 && info.Rotate != RotateMode.None) {
|
||||
var idx = this.Ui.Plugin.Functions.Chat.RotateLinkshellHistory(mode);
|
||||
this._tempChannel = info.Channel.Value + (uint) idx;
|
||||
} else if (info.Channel is InputChannel.CrossLinkshell1 && info.Rotate != RotateMode.None) {
|
||||
var idx = this.Ui.Plugin.Functions.Chat.RotateCrossLinkshellHistory(mode);
|
||||
this._tempChannel = info.Channel.Value + (uint) idx;
|
||||
}
|
||||
}
|
||||
|
||||
if (info.Text != null && this.Chat.Length == 0) {
|
||||
|
@ -266,19 +275,27 @@ internal sealed class ChatLog : IUiComponent {
|
|||
|
||||
ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, Vector2.Zero);
|
||||
try {
|
||||
if (this._tempChannel != null) {
|
||||
if (this._tellTarget != null) {
|
||||
var world = this.Ui.Plugin.DataManager.GetExcelSheet<World>()
|
||||
?.GetRow(this._tellTarget.World)
|
||||
?.Name
|
||||
?.RawString ?? "???";
|
||||
if (this._tellTarget != null) {
|
||||
var world = this.Ui.Plugin.DataManager.GetExcelSheet<World>()
|
||||
?.GetRow(this._tellTarget.World)
|
||||
?.Name
|
||||
?.RawString ?? "???";
|
||||
|
||||
this.DrawChunks(new List<Chunk> {
|
||||
new TextChunk(null, null, "Tell "),
|
||||
new TextChunk(null, null, this._tellTarget.Name),
|
||||
new IconChunk(null, null, BitmapFontIcon.CrossWorld),
|
||||
new TextChunk(null, null, world),
|
||||
});
|
||||
this.DrawChunks(new Chunk[] {
|
||||
new TextChunk(null, null, "Tell "),
|
||||
new TextChunk(null, null, this._tellTarget.Name),
|
||||
new IconChunk(null, null, BitmapFontIcon.CrossWorld),
|
||||
new TextChunk(null, null, world),
|
||||
});
|
||||
} else if (this._tempChannel != null) {
|
||||
if (this._tempChannel.Value.IsLinkshell()) {
|
||||
var idx = (uint) this._tempChannel.Value - (uint) InputChannel.Linkshell1;
|
||||
var lsName = this.Ui.Plugin.Functions.Chat.GetLinkshellName(idx);
|
||||
ImGui.TextUnformatted($"LS #{idx + 1}: {lsName}");
|
||||
} else if (this._tempChannel.Value.IsCrossLinkshell()) {
|
||||
var idx = (uint) this._tempChannel.Value - (uint) InputChannel.CrossLinkshell1;
|
||||
var cwlsName = this.Ui.Plugin.Functions.Chat.GetCrossLinkshellName(idx);
|
||||
ImGui.TextUnformatted($"CWLS [{idx + 1}]: {cwlsName}");
|
||||
} else {
|
||||
ImGui.TextUnformatted(this._tempChannel.Value.ToChatType().Name());
|
||||
}
|
||||
|
@ -312,7 +329,6 @@ internal sealed class ChatLog : IUiComponent {
|
|||
|
||||
if (ImGui.Selectable(name)) {
|
||||
this.Ui.Plugin.Functions.Chat.SetChannel(channel);
|
||||
this._tellIdx = 0;
|
||||
this._tellTarget = null;
|
||||
}
|
||||
}
|
||||
|
@ -394,7 +410,6 @@ internal sealed class ChatLog : IUiComponent {
|
|||
}
|
||||
|
||||
this._tempChannel = null;
|
||||
this._tellIdx = 0;
|
||||
}
|
||||
|
||||
if (inputColour != null) {
|
||||
|
|
Loading…
Reference in New Issue