Working tells for eureka and bozja
Implemented the same focus requirements that vanilla chat has, so as long as the input retains focus, on focus loss it automatically restore the previous channel
This commit is contained in:
+148
-103
@@ -25,49 +25,52 @@ internal sealed unsafe class Chat : IDisposable {
|
|||||||
// Functions
|
// Functions
|
||||||
|
|
||||||
[Signature("E8 ?? ?? ?? ?? 0F B7 44 37 ??", Fallibility = Fallibility.Fallible)]
|
[Signature("E8 ?? ?? ?? ?? 0F B7 44 37 ??", Fallibility = Fallibility.Fallible)]
|
||||||
private readonly delegate* unmanaged<RaptureShellModule*, int, uint, Utf8String*, byte, void> _changeChatChannel = null!;
|
private readonly delegate* unmanaged<RaptureShellModule*, int, uint, Utf8String*, byte, void> ChangeChatChannel = null!;
|
||||||
|
|
||||||
|
[Signature("48 89 5C 24 ?? 55 56 57 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? ?? ?? 48 8B 02", Fallibility = Fallibility.Fallible)]
|
||||||
|
private readonly delegate* unmanaged<RaptureShellModule*, Utf8String*, Utf8String*, ushort, ulong, ushort, byte, bool> SetChannelTargetTell = null!;
|
||||||
|
|
||||||
[Signature("4C 8B 81 ?? ?? ?? ?? 4D 85 C0 74 17", Fallibility = Fallibility.Fallible)]
|
[Signature("4C 8B 81 ?? ?? ?? ?? 4D 85 C0 74 17", Fallibility = Fallibility.Fallible)]
|
||||||
private readonly delegate* unmanaged<RaptureLogModule*, uint, ulong> _getContentIdForChatEntry = null!;
|
private readonly delegate* unmanaged<RaptureLogModule*, uint, ulong> GetContentIdForChatEntry = null!;
|
||||||
|
|
||||||
[Signature("E8 ?? ?? ?? ?? 48 8D 4D A0 8B F8")]
|
[Signature("E8 ?? ?? ?? ?? 48 8D 4D A0 8B F8")]
|
||||||
private readonly delegate* unmanaged<IntPtr, Utf8String*, IntPtr, uint> _getKeybind = null!;
|
private readonly delegate* unmanaged<IntPtr, Utf8String*, IntPtr, uint> GetKeybindNative = null!;
|
||||||
|
|
||||||
[Signature("E8 ?? ?? ?? ?? 48 3B F0 74 35")]
|
[Signature("E8 ?? ?? ?? ?? 48 3B F0 74 35")]
|
||||||
private readonly delegate* unmanaged<AtkStage*, IntPtr> _getFocus = null!;
|
private readonly delegate* unmanaged<AtkStage*, IntPtr> GetFocus = null!;
|
||||||
|
|
||||||
[Signature("44 8B 89 ?? ?? ?? ?? 4C 8B C1 45 85 C9")]
|
[Signature("44 8B 89 ?? ?? ?? ?? 4C 8B C1 45 85 C9")]
|
||||||
private readonly delegate* unmanaged<void*, int, IntPtr> _getTellHistory = null!;
|
private readonly delegate* unmanaged<void*, int, IntPtr> GetTellHistory = null!;
|
||||||
|
|
||||||
[Signature("E8 ?? ?? ?? ?? 48 8D 4D 50 E8 ?? ?? ?? ?? 48 8B 17")]
|
[Signature("E8 ?? ?? ?? ?? 48 8D 4D 50 E8 ?? ?? ?? ?? 48 8B 17")]
|
||||||
private readonly delegate* unmanaged<RaptureLogModule*, ushort, Utf8String*, Utf8String*, ulong, ushort, byte, int, byte, void> _printTell = null!;
|
private readonly delegate* unmanaged<RaptureLogModule*, ushort, Utf8String*, Utf8String*, ulong, ushort, byte, int, byte, void> PrintTell = null!;
|
||||||
|
|
||||||
[Signature("E8 ?? ?? ?? ?? 48 8D 4C 24 ?? E8 ?? ?? ?? ?? 48 8D 8C 24 ?? ?? ?? ?? E8 ?? ?? ?? ?? B0 01")]
|
[Signature("E8 ?? ?? ?? ?? 48 8D 4C 24 ?? E8 ?? ?? ?? ?? 48 8D 8C 24 ?? ?? ?? ?? E8 ?? ?? ?? ?? B0 01")]
|
||||||
private readonly delegate* unmanaged<IntPtr, ulong, ushort, Utf8String*, Utf8String*, byte, ulong, byte> _sendTell = null!;
|
private readonly delegate* unmanaged<IntPtr, ulong, ushort, Utf8String*, Utf8String*, byte, ulong, byte> SendTellNative = null!;
|
||||||
|
|
||||||
[Signature("E8 ?? ?? ?? ?? F6 43 0A 40")]
|
[Signature("E8 ?? ?? ?? ?? F6 43 0A 40")]
|
||||||
private readonly delegate* unmanaged<Framework*, IntPtr> _getNetworkModule = null!;
|
private readonly delegate* unmanaged<Framework*, IntPtr> GetNetworkModule = null!;
|
||||||
|
|
||||||
[Signature("E8 ?? ?? ?? ?? 48 8B C8 E8 ?? ?? ?? ?? 45 8D 46 FB")]
|
[Signature("E8 ?? ?? ?? ?? 48 8B C8 E8 ?? ?? ?? ?? 45 8D 46 FB")]
|
||||||
private readonly delegate* unmanaged<IntPtr, uint, Utf8String*> _getCrossLinkshellName = null!;
|
private readonly delegate* unmanaged<IntPtr, uint, Utf8String*> GetCrossLinkshellNameNative = null!;
|
||||||
|
|
||||||
[Signature("3B 51 10 73 0F 8B C2 48 83 C0 0B")]
|
[Signature("3B 51 10 73 0F 8B C2 48 83 C0 0B")]
|
||||||
private readonly delegate* unmanaged<IntPtr, uint, ulong*> _getLinkshellInfo = null!;
|
private readonly delegate* unmanaged<IntPtr, uint, ulong*> GetLinkshellInfo = null!;
|
||||||
|
|
||||||
[Signature("E8 ?? ?? ?? ?? 4C 8B C8 44 8D 47 01")]
|
[Signature("E8 ?? ?? ?? ?? 4C 8B C8 44 8D 47 01")]
|
||||||
private readonly delegate* unmanaged<IntPtr, ulong, byte*> _getLinkshellName = null!;
|
private readonly delegate* unmanaged<IntPtr, ulong, byte*> GetLinkshellNameNative = null!;
|
||||||
|
|
||||||
[Signature("40 56 41 54 41 55 41 57 48 83 EC 28 48 8B 01", Fallibility = Fallibility.Fallible)]
|
[Signature("40 56 41 54 41 55 41 57 48 83 EC 28 48 8B 01", Fallibility = Fallibility.Fallible)]
|
||||||
private readonly delegate* unmanaged<UIModule*, int, ulong> _rotateLinkshellHistory;
|
private readonly delegate* unmanaged<UIModule*, int, ulong> RotateLinkshellHistoryNative;
|
||||||
|
|
||||||
[Signature("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 56 41 57 48 83 EC 20 48 8B 01 44 8B F2", Fallibility = Fallibility.Fallible)]
|
[Signature("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 56 41 57 48 83 EC 20 48 8B 01 44 8B F2", Fallibility = Fallibility.Fallible)]
|
||||||
private readonly delegate* unmanaged<UIModule*, int, ulong> _rotateCrossLinkshellHistory;
|
private readonly delegate* unmanaged<UIModule*, int, ulong> RotateCrossLinkshellHistoryNative;
|
||||||
|
|
||||||
[Signature("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 8B F2 48 8D B9")]
|
[Signature("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 8B F2 48 8D B9")]
|
||||||
private readonly delegate* unmanaged<IntPtr, uint, IntPtr> _getColourInfo = null!;
|
private readonly delegate* unmanaged<IntPtr, uint, IntPtr> GetColourInfo = null!;
|
||||||
|
|
||||||
[Signature("E8 ?? ?? ?? ?? EB 0A 48 8D 4C 24 ?? E8 ?? ?? ?? ?? 48 8D 8D")]
|
[Signature("E8 ?? ?? ?? ?? EB 0A 48 8D 4C 24 ?? E8 ?? ?? ?? ?? 48 8D 8D")]
|
||||||
private readonly delegate* unmanaged<Utf8String*, int, IntPtr, void> _sanitiseString = null!;
|
private readonly delegate* unmanaged<Utf8String*, int, IntPtr, void> SanitiseString = null!;
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
|
|
||||||
@@ -79,6 +82,8 @@ internal sealed unsafe class Chat : IDisposable {
|
|||||||
|
|
||||||
private delegate byte SetChatLogTellTarget(IntPtr a1, Utf8String* name, Utf8String* a3, ushort world, ulong contentId, ushort a6, byte a7);
|
private delegate byte SetChatLogTellTarget(IntPtr a1, Utf8String* name, Utf8String* a3, ushort world, ulong contentId, ushort a6, byte a7);
|
||||||
|
|
||||||
|
private delegate void EurekaContextMenuTellDelegate(RaptureShellModule* param1, Utf8String* playerName, Utf8String* worldName, ushort world, ulong contentId, ushort param6);
|
||||||
|
|
||||||
[Signature(
|
[Signature(
|
||||||
"40 53 56 57 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? ?? ?? 49 8B F0 8B FA",
|
"40 53 56 57 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? ?? ?? 49 8B F0 8B FA",
|
||||||
DetourName = nameof(ChatLogRefreshDetour)
|
DetourName = nameof(ChatLogRefreshDetour)
|
||||||
@@ -103,31 +108,37 @@ internal sealed unsafe class Chat : IDisposable {
|
|||||||
)]
|
)]
|
||||||
private Hook<SetChatLogTellTarget>? SetChatLogTellTargetHook { get; init; }
|
private Hook<SetChatLogTellTarget>? SetChatLogTellTargetHook { get; init; }
|
||||||
|
|
||||||
|
[Signature(
|
||||||
|
"E8 ?? ?? ?? ?? EB 8A 48 8B 1D",
|
||||||
|
DetourName = nameof(EurekaContextMenuTell)
|
||||||
|
)]
|
||||||
|
private Hook<EurekaContextMenuTellDelegate>? EurekaContextMenuTellHook { get; init; }
|
||||||
|
|
||||||
// Offsets
|
// Offsets
|
||||||
|
|
||||||
#pragma warning disable 0649
|
#pragma warning disable 0649
|
||||||
|
|
||||||
[Signature("8B B9 ?? ?? ?? ?? 48 8B D9 83 FF FE 0F 84", Offset = 2)]
|
[Signature("8B B9 ?? ?? ?? ?? 48 8B D9 83 FF FE 0F 84", Offset = 2)]
|
||||||
private readonly int? _replyChannelOffset;
|
private readonly int? ReplyChannelOffset;
|
||||||
|
|
||||||
[Signature("89 83 ?? ?? ?? ?? 48 8B 01 83 FE 13 7C 05 41 8B D4 EB 03 83 CA FF FF 90", Offset = 2)]
|
[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;
|
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)]
|
[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;
|
private readonly int? LinkshellCycleOffset;
|
||||||
|
|
||||||
[Signature("BA ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 8B F0 48 85 C0 0F 84 ?? ?? ?? ?? 48 8B 10 33", Offset = 1)]
|
[Signature("BA ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 8B F0 48 85 C0 0F 84 ?? ?? ?? ?? 48 8B 10 33", Offset = 1)]
|
||||||
private readonly uint? _linkshellInfoProxyIdx;
|
private readonly uint? LinkshellInfoProxyIdx;
|
||||||
|
|
||||||
[Signature("BA ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 89 6C 24 ?? 4C 8B E0 48 89 74 24", Offset = 1)]
|
[Signature("BA ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 89 6C 24 ?? 4C 8B E0 48 89 74 24", Offset = 1)]
|
||||||
private readonly uint? _crossLinkshellInfoProxyIdx;
|
private readonly uint? CrossLinkshellInfoProxyIdx;
|
||||||
|
|
||||||
#pragma warning restore 0649
|
#pragma warning restore 0649
|
||||||
|
|
||||||
// Pointers
|
// Pointers
|
||||||
|
|
||||||
[Signature("48 8D 15 ?? ?? ?? ?? 0F B6 C8 48 8D 05", ScanType = ScanType.StaticAddress)]
|
[Signature("48 8D 15 ?? ?? ?? ?? 0F B6 C8 48 8D 05", ScanType = ScanType.StaticAddress)]
|
||||||
private readonly char* _currentCharacter = null!;
|
private readonly char* CurrentCharacter = null!;
|
||||||
|
|
||||||
[Signature("48 8D 0D ?? ?? ?? ?? 8B 14 ?? 85 D2 7E ?? 48 8B 0D ?? ?? ?? ?? 48 83 C1 10 E8 ?? ?? ?? ?? 8B 70 ?? 41 8D 4D", ScanType = ScanType.StaticAddress)]
|
[Signature("48 8D 0D ?? ?? ?? ?? 8B 14 ?? 85 D2 7E ?? 48 8B 0D ?? ?? ?? ?? 48 83 C1 10 E8 ?? ?? ?? ?? 8B 70 ?? 41 8D 4D", ScanType = ScanType.StaticAddress)]
|
||||||
private IntPtr ColourLookup { get; init; }
|
private IntPtr ColourLookup { get; init; }
|
||||||
@@ -141,76 +152,81 @@ internal sealed unsafe class Chat : IDisposable {
|
|||||||
private Plugin Plugin { get; }
|
private Plugin Plugin { get; }
|
||||||
internal (InputChannel channel, List<Chunk> name) Channel { get; private set; }
|
internal (InputChannel channel, List<Chunk> name) Channel { get; private set; }
|
||||||
|
|
||||||
|
internal bool UsesTellTempChannel { get; set; }
|
||||||
|
internal InputChannel? PreviousChannel { get; private set; }
|
||||||
|
|
||||||
internal Chat(Plugin plugin) {
|
internal Chat(Plugin plugin) {
|
||||||
this.Plugin = plugin;
|
Plugin = plugin;
|
||||||
Plugin.GameInteropProvider.InitializeFromAttributes(this);
|
Plugin.GameInteropProvider.InitializeFromAttributes(this);
|
||||||
|
|
||||||
this.ChatLogRefreshHook?.Enable();
|
ChatLogRefreshHook?.Enable();
|
||||||
this.ChangeChannelNameHook?.Enable();
|
ChangeChannelNameHook?.Enable();
|
||||||
this.ReplyInSelectedChatModeHook?.Enable();
|
ReplyInSelectedChatModeHook?.Enable();
|
||||||
this.SetChatLogTellTargetHook?.Enable();
|
SetChatLogTellTargetHook?.Enable();
|
||||||
|
EurekaContextMenuTellHook?.Enable();
|
||||||
|
|
||||||
Plugin.Framework.Update += this.InterceptKeybinds;
|
Plugin.Framework.Update += InterceptKeybinds;
|
||||||
Plugin.ClientState.Login += this.Login;
|
Plugin.ClientState.Login += Login;
|
||||||
this.Login();
|
Login();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose() {
|
public void Dispose() {
|
||||||
Plugin.ClientState.Login -= this.Login;
|
Plugin.ClientState.Login -= Login;
|
||||||
Plugin.Framework.Update -= this.InterceptKeybinds;
|
Plugin.Framework.Update -= InterceptKeybinds;
|
||||||
|
|
||||||
this.SetChatLogTellTargetHook?.Dispose();
|
SetChatLogTellTargetHook?.Dispose();
|
||||||
this.ReplyInSelectedChatModeHook?.Dispose();
|
ReplyInSelectedChatModeHook?.Dispose();
|
||||||
this.ChangeChannelNameHook?.Dispose();
|
ChangeChannelNameHook?.Dispose();
|
||||||
this.ChatLogRefreshHook?.Dispose();
|
ChatLogRefreshHook?.Dispose();
|
||||||
|
EurekaContextMenuTellHook?.Dispose();
|
||||||
|
|
||||||
this.Activated = null;
|
Activated = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal string? GetLinkshellName(uint idx) {
|
internal string? GetLinkshellName(uint idx) {
|
||||||
if (this._linkshellInfoProxyIdx is not { } proxyIdx) {
|
if (LinkshellInfoProxyIdx is not { } proxyIdx) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var infoProxy = this.Plugin.Functions.GetInfoProxyByIndex(proxyIdx);
|
var infoProxy = Plugin.Functions.GetInfoProxyByIndex(proxyIdx);
|
||||||
if (infoProxy == IntPtr.Zero) {
|
if (infoProxy == IntPtr.Zero) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var lsInfo = this._getLinkshellInfo(infoProxy, idx);
|
var lsInfo = GetLinkshellInfo(infoProxy, idx);
|
||||||
if (lsInfo == null) {
|
if (lsInfo == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var utf = this._getLinkshellName(infoProxy, *lsInfo);
|
var utf = GetLinkshellNameNative(infoProxy, *lsInfo);
|
||||||
return utf == null ? null : MemoryHelper.ReadStringNullTerminated((IntPtr) utf);
|
return utf == null ? null : MemoryHelper.ReadStringNullTerminated((IntPtr) utf);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal string? GetCrossLinkshellName(uint idx) {
|
internal string? GetCrossLinkshellName(uint idx) {
|
||||||
if (this._crossLinkshellInfoProxyIdx is not { } proxyIdx) {
|
if (CrossLinkshellInfoProxyIdx is not { } proxyIdx) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var infoProxy = this.Plugin.Functions.GetInfoProxyByIndex(proxyIdx);
|
var infoProxy = Plugin.Functions.GetInfoProxyByIndex(proxyIdx);
|
||||||
if (infoProxy == IntPtr.Zero) {
|
if (infoProxy == IntPtr.Zero) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var utf = this._getCrossLinkshellName(infoProxy, idx);
|
var utf = GetCrossLinkshellNameNative(infoProxy, idx);
|
||||||
return utf == null ? null : utf->ToString();
|
return utf == null ? null : utf->ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal ulong RotateLinkshellHistory(RotateMode mode) {
|
internal ulong RotateLinkshellHistory(RotateMode mode) {
|
||||||
if (mode == RotateMode.None && this._linkshellCycleOffset != null) {
|
if (mode == RotateMode.None && LinkshellCycleOffset != null) {
|
||||||
// for the branch at 6.08: 5E1680
|
// for the branch at 6.08: 5E1680
|
||||||
var uiModule = (IntPtr) Framework.Instance()->GetUiModule();
|
var uiModule = (IntPtr) Framework.Instance()->GetUiModule();
|
||||||
*(int*) (uiModule + this._linkshellCycleOffset.Value) = -1;
|
*(int*) (uiModule + LinkshellCycleOffset.Value) = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return RotateLinkshellHistoryInternal(this._rotateLinkshellHistory, mode);
|
return RotateLinkshellHistoryInternal(RotateLinkshellHistoryNative, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal ulong RotateCrossLinkshellHistory(RotateMode mode) => RotateLinkshellHistoryInternal(this._rotateCrossLinkshellHistory, mode);
|
internal ulong RotateCrossLinkshellHistory(RotateMode mode) => RotateLinkshellHistoryInternal(RotateCrossLinkshellHistoryNative, mode);
|
||||||
|
|
||||||
private static ulong RotateLinkshellHistoryInternal(delegate* unmanaged<UIModule*, int, ulong> func, RotateMode mode) {
|
private static ulong RotateLinkshellHistoryInternal(delegate* unmanaged<UIModule*, int, ulong> func, RotateMode mode) {
|
||||||
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
|
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
|
||||||
@@ -232,7 +248,7 @@ internal sealed unsafe class Chat : IDisposable {
|
|||||||
//
|
//
|
||||||
// If this function would ever return 0, it returns null instead.
|
// If this function would ever return 0, it returns null instead.
|
||||||
internal uint? GetChannelColour(ChatType type) {
|
internal uint? GetChannelColour(ChatType type) {
|
||||||
if (this._getColourInfo == null || this.ColourLookup == IntPtr.Zero) {
|
if (GetColourInfo == null || ColourLookup == IntPtr.Zero) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,8 +268,8 @@ internal sealed unsafe class Chat : IDisposable {
|
|||||||
|
|
||||||
var framework = (IntPtr) Framework.Instance();
|
var framework = (IntPtr) Framework.Instance();
|
||||||
|
|
||||||
var lookupResult = *(uint*) (this.ColourLookup + (int) parent * 4);
|
var lookupResult = *(uint*) (ColourLookup + (int) parent * 4);
|
||||||
var info = this._getColourInfo(framework + 16, lookupResult);
|
var info = GetColourInfo(framework + 16, lookupResult);
|
||||||
var rgb = *(uint*) (info + 32) & 0xFFFFFF;
|
var rgb = *(uint*) (info + 32) & 0xFFFFFF;
|
||||||
|
|
||||||
if (rgb == 0) {
|
if (rgb == 0) {
|
||||||
@@ -264,7 +280,7 @@ internal sealed unsafe class Chat : IDisposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private readonly Dictionary<string, Keybind> _keybinds = new();
|
private readonly Dictionary<string, Keybind> _keybinds = new();
|
||||||
internal IReadOnlyDictionary<string, Keybind> Keybinds => this._keybinds;
|
internal IReadOnlyDictionary<string, Keybind> Keybinds => _keybinds;
|
||||||
|
|
||||||
internal static readonly IReadOnlyDictionary<string, ChannelSwitchInfo> KeybindsToIntercept = new Dictionary<string, ChannelSwitchInfo> {
|
internal static readonly IReadOnlyDictionary<string, ChannelSwitchInfo> KeybindsToIntercept = new Dictionary<string, ChannelSwitchInfo> {
|
||||||
["CMD_CHAT"] = new(null),
|
["CMD_CHAT"] = new(null),
|
||||||
@@ -336,10 +352,10 @@ internal sealed unsafe class Chat : IDisposable {
|
|||||||
|
|
||||||
private void CheckFocus() {
|
private void CheckFocus() {
|
||||||
void Decrement() {
|
void Decrement() {
|
||||||
if (this._graceFrames > 0) {
|
if (_graceFrames > 0) {
|
||||||
this._graceFrames -= 1;
|
_graceFrames -= 1;
|
||||||
} else {
|
} else {
|
||||||
this._inputFocused = false;
|
_inputFocused = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -351,8 +367,8 @@ internal sealed unsafe class Chat : IDisposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (*isTextInputActivePtr) {
|
if (*isTextInputActivePtr) {
|
||||||
this._inputFocused = true;
|
_inputFocused = true;
|
||||||
this._graceFrames = 60;
|
_graceFrames = 60;
|
||||||
} else {
|
} else {
|
||||||
Decrement();
|
Decrement();
|
||||||
}
|
}
|
||||||
@@ -360,20 +376,20 @@ internal sealed unsafe class Chat : IDisposable {
|
|||||||
|
|
||||||
private void UpdateKeybinds() {
|
private void UpdateKeybinds() {
|
||||||
foreach (var name in KeybindsToIntercept.Keys) {
|
foreach (var name in KeybindsToIntercept.Keys) {
|
||||||
var keybind = this.GetKeybind(name);
|
var keybind = GetKeybind(name);
|
||||||
if (keybind is null) {
|
if (keybind is null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._keybinds[name] = keybind;
|
_keybinds[name] = keybind;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InterceptKeybinds(IFramework framework1) {
|
private void InterceptKeybinds(IFramework framework1) {
|
||||||
this.CheckFocus();
|
CheckFocus();
|
||||||
this.UpdateKeybinds();
|
UpdateKeybinds();
|
||||||
|
|
||||||
if (this._inputFocused) {
|
if (_inputFocused) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -387,7 +403,7 @@ internal sealed unsafe class Chat : IDisposable {
|
|||||||
|
|
||||||
var turnedOff = new Dictionary<VirtualKey, (uint, string)>();
|
var turnedOff = new Dictionary<VirtualKey, (uint, string)>();
|
||||||
foreach (var toIntercept in KeybindsToIntercept.Keys) {
|
foreach (var toIntercept in KeybindsToIntercept.Keys) {
|
||||||
if (!this.Keybinds.TryGetValue(toIntercept, out var keybind)) {
|
if (!Keybinds.TryGetValue(toIntercept, out var keybind)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -396,7 +412,7 @@ internal sealed unsafe class Chat : IDisposable {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var modifierPressed = this.Plugin.Config.KeybindMode switch {
|
var modifierPressed = Plugin.Config.KeybindMode switch {
|
||||||
KeybindMode.Strict => modifier == modifierState,
|
KeybindMode.Strict => modifier == modifierState,
|
||||||
KeybindMode.Flexible => modifierState.HasFlag(modifier),
|
KeybindMode.Flexible => modifierState.HasFlag(modifier),
|
||||||
_ => false,
|
_ => false,
|
||||||
@@ -427,7 +443,7 @@ internal sealed unsafe class Chat : IDisposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.Activated?.Invoke(new ChatActivatedArgs(info) {
|
Activated?.Invoke(new ChatActivatedArgs(info) {
|
||||||
TellReason = TellReason.Reply,
|
TellReason = TellReason.Reply,
|
||||||
});
|
});
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
@@ -437,7 +453,7 @@ internal sealed unsafe class Chat : IDisposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void Login() {
|
private void Login() {
|
||||||
if (this.ChangeChannelNameHook == null) {
|
if (ChangeChannelNameHook == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -446,19 +462,19 @@ internal sealed unsafe class Chat : IDisposable {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.ChangeChannelNameDetour((IntPtr) agent);
|
ChangeChannelNameDetour((IntPtr) agent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte ChatLogRefreshDetour(IntPtr log, ushort eventId, AtkValue* value) {
|
private byte ChatLogRefreshDetour(IntPtr log, ushort eventId, AtkValue* value) {
|
||||||
if (eventId != 0x31 || value == null || value->UInt is not (0x05 or 0x0C)) {
|
if (eventId != 0x31 || value == null || value->UInt is not (0x05 or 0x0C)) {
|
||||||
return this.ChatLogRefreshHook!.Original(log, eventId, value);
|
return ChatLogRefreshHook!.Original(log, eventId, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
string? input = null;
|
string? input = null;
|
||||||
if (Plugin.GameConfig.TryGet(UiControlOption.DirectChat, out bool option) && option) {
|
if (Plugin.GameConfig.TryGet(UiControlOption.DirectChat, out bool option) && option) {
|
||||||
if (this._currentCharacter != null) {
|
if (CurrentCharacter != null) {
|
||||||
// FIXME: this whole system sucks
|
// FIXME: this whole system sucks
|
||||||
var c = *this._currentCharacter;
|
var c = *CurrentCharacter;
|
||||||
if (c != '\0' && !char.IsControl(c)) {
|
if (c != '\0' && !char.IsControl(c)) {
|
||||||
input = c.ToString();
|
input = c.ToString();
|
||||||
}
|
}
|
||||||
@@ -480,7 +496,7 @@ internal sealed unsafe class Chat : IDisposable {
|
|||||||
AddIfNotPresent = addIfNotPresent,
|
AddIfNotPresent = addIfNotPresent,
|
||||||
Input = input,
|
Input = input,
|
||||||
};
|
};
|
||||||
this.Activated?.Invoke(args);
|
Activated?.Invoke(args);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Plugin.Log.Error(ex, "Error in chat Activated event");
|
Plugin.Log.Error(ex, "Error in chat Activated event");
|
||||||
}
|
}
|
||||||
@@ -494,7 +510,7 @@ internal sealed unsafe class Chat : IDisposable {
|
|||||||
// +0x40 = chat channel (byte or uint?)
|
// +0x40 = chat channel (byte or uint?)
|
||||||
// channel is 17 (maybe 18?) for tells
|
// channel is 17 (maybe 18?) for tells
|
||||||
// +0x48 = pointer to channel name string
|
// +0x48 = pointer to channel name string
|
||||||
var ret = this.ChangeChannelNameHook!.Original(agent);
|
var ret = ChangeChannelNameHook!.Original(agent);
|
||||||
if (agent == IntPtr.Zero) {
|
if (agent == IntPtr.Zero) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -507,8 +523,8 @@ internal sealed unsafe class Chat : IDisposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var channel = 0u;
|
var channel = 0u;
|
||||||
if (this._shellChannelOffset != null) {
|
if (ShellChannelOffset != null) {
|
||||||
channel = *(uint*) (shellModule + this._shellChannelOffset.Value);
|
channel = *(uint*) (shellModule + ShellChannelOffset.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// var channel = *(uint*) (agent + 0x40);
|
// var channel = *(uint*) (agent + 0x40);
|
||||||
@@ -535,32 +551,32 @@ internal sealed unsafe class Chat : IDisposable {
|
|||||||
text.Content = text.Content.TrimStart('\uE01E').TrimStart();
|
text.Content = text.Content.TrimStart('\uE01E').TrimStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.Channel = ((InputChannel) channel, nameChunks);
|
Channel = ((InputChannel) channel, nameChunks);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ReplyInSelectedChatModeDetour(AgentInterface* agent) {
|
private void ReplyInSelectedChatModeDetour(AgentInterface* agent) {
|
||||||
if (this._replyChannelOffset == null) {
|
if (ReplyChannelOffset == null) {
|
||||||
goto Original;
|
goto Original;
|
||||||
}
|
}
|
||||||
|
|
||||||
var replyMode = *(int*) ((IntPtr) agent + this._replyChannelOffset.Value);
|
var replyMode = *(int*) ((IntPtr) agent + ReplyChannelOffset.Value);
|
||||||
if (replyMode == -2) {
|
if (replyMode == -2) {
|
||||||
goto Original;
|
goto Original;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.SetChannel((InputChannel) replyMode);
|
SetChannel((InputChannel) replyMode);
|
||||||
|
|
||||||
Original:
|
Original:
|
||||||
this.ReplyInSelectedChatModeHook!.Original(agent);
|
ReplyInSelectedChatModeHook!.Original(agent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte SetChatLogTellTargetDetour(IntPtr a1, Utf8String* name, Utf8String* a3, ushort world, ulong contentId, ushort reason, byte a7) {
|
private byte SetChatLogTellTargetDetour(IntPtr a1, Utf8String* name, Utf8String* a3, ushort world, ulong contentId, ushort reason, byte a7) {
|
||||||
if (name != null) {
|
if (name != null) {
|
||||||
try {
|
try {
|
||||||
var target = new TellTarget(name->ToString(), world, contentId, (TellReason) reason);
|
var target = new TellTarget(name->ToString(), world, contentId, (TellReason) reason);
|
||||||
this.Activated?.Invoke(new ChatActivatedArgs(new ChannelSwitchInfo(InputChannel.Tell)) {
|
Activated?.Invoke(new ChatActivatedArgs(new ChannelSwitchInfo(InputChannel.Tell)) {
|
||||||
TellReason = (TellReason) reason,
|
TellReason = (TellReason) reason,
|
||||||
TellTarget = target,
|
TellTarget = target,
|
||||||
});
|
});
|
||||||
@@ -569,31 +585,65 @@ internal sealed unsafe class Chat : IDisposable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.SetChatLogTellTargetHook!.Original(a1, name, a3, world, contentId, reason, a7);
|
return SetChatLogTellTargetHook!.Original(a1, name, a3, world, contentId, reason, a7);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EurekaContextMenuTell(RaptureShellModule* param1, Utf8String* playerName, Utf8String* worldName, ushort world, ulong id, ushort param6)
|
||||||
|
{
|
||||||
|
if (!UsesTellTempChannel)
|
||||||
|
{
|
||||||
|
UsesTellTempChannel = true;
|
||||||
|
PreviousChannel = Channel.channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SetChannelTargetTell != null)
|
||||||
|
SetChannelTargetTell(param1, playerName, worldName, world, id, param6, 0);
|
||||||
|
|
||||||
|
EurekaContextMenuTellHook!.Original(param1, playerName, worldName, world, id, param6);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal ulong? GetContentIdForEntry(uint index) {
|
internal ulong? GetContentIdForEntry(uint index) {
|
||||||
if (this._getContentIdForChatEntry == null) {
|
if (GetContentIdForChatEntry == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._getContentIdForChatEntry(Framework.Instance()->GetUiModule()->GetRaptureLogModule(), index);
|
return GetContentIdForChatEntry(Framework.Instance()->GetUiModule()->GetRaptureLogModule(), index);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void SetChannel(InputChannel channel, string? tellTarget = null) {
|
internal void SetChannel(InputChannel channel, string? tellTarget = null) {
|
||||||
if (this._changeChatChannel == null) {
|
if (ChangeChatChannel == null)
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
var target = Utf8String.FromString(tellTarget ?? "");
|
var target = Utf8String.FromString(tellTarget ?? "");
|
||||||
var idx = channel.LinkshellIndex();
|
var idx = channel.LinkshellIndex();
|
||||||
if (idx == uint.MaxValue) {
|
if (idx == uint.MaxValue)
|
||||||
idx = 0;
|
idx = 0;
|
||||||
|
|
||||||
|
ChangeChatChannel(RaptureShellModule.Instance(), (int) channel, idx, target, 1);
|
||||||
|
target->Dtor(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._changeChatChannel(RaptureShellModule.Instance(), (int) channel, idx, target, 1);
|
internal void SetEurekaTellChannel(string name, string worldName, ushort worldId, ulong objectId, ushort param6, byte param7)
|
||||||
target->Dtor();
|
{
|
||||||
IMemorySpace.Free(target);
|
// param6 is 0 for contentId and 1 for objectId
|
||||||
|
// param7 is always 0 ?
|
||||||
|
|
||||||
|
if (SetChannelTargetTell == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!UsesTellTempChannel)
|
||||||
|
{
|
||||||
|
UsesTellTempChannel = true;
|
||||||
|
PreviousChannel = Channel.channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
var utfName = Utf8String.FromString(name);
|
||||||
|
var utfWorld = Utf8String.FromString(worldName);
|
||||||
|
|
||||||
|
SetChannelTargetTell(RaptureShellModule.Instance(), utfName, utfWorld, worldId, objectId, param6, param7);
|
||||||
|
|
||||||
|
utfName->Dtor(true);
|
||||||
|
utfWorld->Dtor(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static VirtualKey GetKeyForModifier(ModifierFlag modifierFlag) => modifierFlag switch {
|
private static VirtualKey GetKeyForModifier(ModifierFlag modifierFlag) => modifierFlag switch {
|
||||||
@@ -616,9 +666,8 @@ internal sealed unsafe class Chat : IDisposable {
|
|||||||
|
|
||||||
var outData = stackalloc byte[32];
|
var outData = stackalloc byte[32];
|
||||||
var idString = Utf8String.FromString(id);
|
var idString = Utf8String.FromString(id);
|
||||||
this._getKeybind((IntPtr) a1, idString, (IntPtr) outData);
|
GetKeybindNative((IntPtr) a1, idString, (IntPtr) outData);
|
||||||
idString->Dtor();
|
idString->Dtor(true);
|
||||||
IMemorySpace.Free(idString);
|
|
||||||
|
|
||||||
var key1 = (VirtualKey) outData[0];
|
var key1 = (VirtualKey) outData[0];
|
||||||
if (key1 is VirtualKey.F23) {
|
if (key1 is VirtualKey.F23) {
|
||||||
@@ -644,7 +693,7 @@ internal sealed unsafe class Chat : IDisposable {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var ptr = this._getTellHistory(acquaintanceModule, index);
|
var ptr = GetTellHistory(acquaintanceModule, index);
|
||||||
if (ptr == IntPtr.Zero) {
|
if (ptr == IntPtr.Zero) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -660,28 +709,24 @@ internal sealed unsafe class Chat : IDisposable {
|
|||||||
var uName = Utf8String.FromString(name);
|
var uName = Utf8String.FromString(name);
|
||||||
var uMessage = Utf8String.FromString(message);
|
var uMessage = Utf8String.FromString(message);
|
||||||
|
|
||||||
var networkModule = this._getNetworkModule(Framework.Instance());
|
var networkModule = GetNetworkModule(Framework.Instance());
|
||||||
var a1 = *(IntPtr*) (networkModule + 8);
|
var a1 = *(IntPtr*) (networkModule + 8);
|
||||||
var logModule = Framework.Instance()->GetUiModule()->GetRaptureLogModule();
|
var logModule = Framework.Instance()->GetUiModule()->GetRaptureLogModule();
|
||||||
|
|
||||||
this._printTell(logModule, 33, uName, uMessage, contentId, homeWorld, 255, 0, 0);
|
PrintTell(logModule, 33, uName, uMessage, contentId, homeWorld, 255, 0, 0);
|
||||||
this._sendTell(a1, contentId, homeWorld, uName, uMessage, (byte) reason, homeWorld);
|
SendTellNative(a1, contentId, homeWorld, uName, uMessage, (byte) reason, homeWorld);
|
||||||
|
|
||||||
uName->Dtor();
|
uName->Dtor(true);
|
||||||
IMemorySpace.Free(uName);
|
uMessage->Dtor(true);
|
||||||
|
|
||||||
uMessage->Dtor();
|
|
||||||
IMemorySpace.Free(uMessage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal bool IsCharValid(char c) {
|
internal bool IsCharValid(char c) {
|
||||||
var uC = Utf8String.FromString(c.ToString());
|
var uC = Utf8String.FromString(c.ToString());
|
||||||
|
|
||||||
this._sanitiseString(uC, 0x27F, IntPtr.Zero);
|
SanitiseString(uC, 0x27F, IntPtr.Zero);
|
||||||
var wasValid = uC->ToString().Length > 0;
|
var wasValid = uC->ToString().Length > 0;
|
||||||
|
|
||||||
uC->Dtor();
|
uC->Dtor(true);
|
||||||
IMemorySpace.Free(uC);
|
|
||||||
|
|
||||||
return wasValid;
|
return wasValid;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,9 @@ using Dalamud.Interface.Utility;
|
|||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
|
using Lumina.Excel;
|
||||||
using Lumina.Excel.GeneratedSheets;
|
using Lumina.Excel.GeneratedSheets;
|
||||||
|
|
||||||
using Action = System.Action;
|
using Action = System.Action;
|
||||||
using DalamudPartyFinderPayload = Dalamud.Game.Text.SeStringHandling.Payloads.PartyFinderPayload;
|
using DalamudPartyFinderPayload = Dalamud.Game.Text.SeStringHandling.Payloads.PartyFinderPayload;
|
||||||
using ChatTwoPartyFinderPayload = ChatTwo.Util.PartyFinderPayload;
|
using ChatTwoPartyFinderPayload = ChatTwo.Util.PartyFinderPayload;
|
||||||
@@ -26,7 +28,6 @@ public sealed class PayloadHandler {
|
|||||||
private const string PopupId = "chat2-context-popup";
|
private const string PopupId = "chat2-context-popup";
|
||||||
|
|
||||||
private ChatLogWindow LogWindow { get; }
|
private ChatLogWindow LogWindow { get; }
|
||||||
|
|
||||||
private (Chunk, Payload?)? Popup { get; set; }
|
private (Chunk, Payload?)? Popup { get; set; }
|
||||||
|
|
||||||
private bool _handleTooltips;
|
private bool _handleTooltips;
|
||||||
@@ -34,8 +35,18 @@ public sealed class PayloadHandler {
|
|||||||
private uint _hoverCounter;
|
private uint _hoverCounter;
|
||||||
private uint _lastHoverCounter;
|
private uint _lastHoverCounter;
|
||||||
|
|
||||||
|
private readonly ExcelSheet<Item> ItemSheet;
|
||||||
|
private readonly ExcelSheet<EventItem> EventItemSheet;
|
||||||
|
private readonly ExcelSheet<TerritoryType> TerritorySheet;
|
||||||
|
private readonly ExcelSheet<EventItemHelp> EventItemHelpSheet;
|
||||||
|
|
||||||
internal PayloadHandler(ChatLogWindow logWindow) {
|
internal PayloadHandler(ChatLogWindow logWindow) {
|
||||||
LogWindow = logWindow;
|
LogWindow = logWindow;
|
||||||
|
|
||||||
|
ItemSheet = Plugin.DataManager.GetExcelSheet<Item>()!;
|
||||||
|
EventItemSheet = Plugin.DataManager.GetExcelSheet<EventItem>()!;
|
||||||
|
TerritorySheet = Plugin.DataManager.GetExcelSheet<TerritoryType>()!;
|
||||||
|
EventItemHelpSheet = Plugin.DataManager.GetExcelSheet<EventItemHelp>()!;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Draw() {
|
internal void Draw() {
|
||||||
@@ -303,7 +314,7 @@ public sealed class PayloadHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void HoverEventItem(ItemPayload payload) {
|
private void HoverEventItem(ItemPayload payload) {
|
||||||
var item = Plugin.DataManager.GetExcelSheet<EventItem>()?.GetRow(payload.RawItemId);
|
var item = EventItemSheet.GetRow(payload.RawItemId);
|
||||||
if (item == null) {
|
if (item == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -316,7 +327,7 @@ public sealed class PayloadHandler {
|
|||||||
LogWindow.DrawChunks(name.ToList());
|
LogWindow.DrawChunks(name.ToList());
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
|
|
||||||
var help = Plugin.DataManager.GetExcelSheet<EventItemHelp>()?.GetRow(payload.RawItemId);
|
var help = EventItemHelpSheet.GetRow(payload.RawItemId);
|
||||||
if (help != null) {
|
if (help != null) {
|
||||||
var desc = ChunkUtil.ToChunks(help.Description.ToDalamudString(), ChunkSource.None, null);
|
var desc = ChunkUtil.ToChunks(help.Description.ToDalamudString(), ChunkSource.None, null);
|
||||||
LogWindow.DrawChunks(desc.ToList());
|
LogWindow.DrawChunks(desc.ToList());
|
||||||
@@ -402,7 +413,7 @@ public sealed class PayloadHandler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var item = Plugin.DataManager.GetExcelSheet<Item>()?.GetRow(payload.ItemId);
|
var item = ItemSheet.GetRow(payload.ItemId);
|
||||||
if (item == null) {
|
if (item == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -459,7 +470,7 @@ public sealed class PayloadHandler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var item = Plugin.DataManager.GetExcelSheet<EventItem>()?.GetRow(payload.ItemId);
|
var item = EventItemSheet.GetRow(payload.ItemId);
|
||||||
if (item == null) {
|
if (item == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -506,17 +517,27 @@ public sealed class PayloadHandler {
|
|||||||
LogWindow.DrawChunks(name, false);
|
LogWindow.DrawChunks(name, false);
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
|
|
||||||
if (ImGui.Selectable(Language.Context_SendTell)) {
|
var validContentId = chunk.Message?.ContentId is not (null or 0);
|
||||||
|
if (ImGui.Selectable(Language.Context_SendTell))
|
||||||
|
{
|
||||||
|
// Eureka and Bozja need special handling as tells work different
|
||||||
|
if (TerritorySheet.GetRow(Plugin.ClientState.TerritoryType)?.TerritoryIntendedUse != 41)
|
||||||
|
{
|
||||||
LogWindow.Chat = $"/tell {player.PlayerName}";
|
LogWindow.Chat = $"/tell {player.PlayerName}";
|
||||||
if (world.IsPublic) {
|
if (world.IsPublic) {
|
||||||
LogWindow.Chat += $"@{world.Name}";
|
LogWindow.Chat += $"@{world.Name}";
|
||||||
}
|
}
|
||||||
|
|
||||||
LogWindow.Chat += " ";
|
LogWindow.Chat += " ";
|
||||||
|
}
|
||||||
|
else if (validContentId)
|
||||||
|
{
|
||||||
|
LogWindow.Plugin.Functions.Chat.SetEurekaTellChannel(player.PlayerName, world.Name.ToString(), (ushort) world.RowId, chunk.Message!.ContentId, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
LogWindow.Activate = true;
|
LogWindow.Activate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var validContentId = chunk.Message?.ContentId is not (null or 0);
|
|
||||||
if (world.IsPublic) {
|
if (world.IsPublic) {
|
||||||
var party = Plugin.PartyList;
|
var party = Plugin.PartyList;
|
||||||
var leader = (ulong?) party[(int) party.PartyLeaderIndex]?.ContentId;
|
var leader = (ulong?) party[(int) party.PartyLeaderIndex]?.ContentId;
|
||||||
@@ -524,7 +545,7 @@ public sealed class PayloadHandler {
|
|||||||
var member = party.FirstOrDefault(member => member.Name.TextValue == player.PlayerName && member.World.Id == world.RowId);
|
var member = party.FirstOrDefault(member => member.Name.TextValue == player.PlayerName && member.World.Id == world.RowId);
|
||||||
var isInParty = member != default;
|
var isInParty = member != default;
|
||||||
var inInstance = LogWindow.Plugin.Functions.IsInInstance();
|
var inInstance = LogWindow.Plugin.Functions.IsInInstance();
|
||||||
var inPartyInstance = Plugin.DataManager.GetExcelSheet<TerritoryType>()!.GetRow(Plugin.ClientState.TerritoryType)?.TerritoryIntendedUse is (41 or 47 or 48 or 52 or 53);
|
var inPartyInstance = TerritorySheet.GetRow(Plugin.ClientState.TerritoryType)?.TerritoryIntendedUse is (41 or 47 or 48 or 52 or 53);
|
||||||
if (isLeader) {
|
if (isLeader) {
|
||||||
if (!isInParty) {
|
if (!isInParty) {
|
||||||
if (inInstance && inPartyInstance) {
|
if (inInstance && inPartyInstance) {
|
||||||
|
|||||||
+23
-13
@@ -144,10 +144,8 @@ public sealed class ChatLogWindow : Window, IUiComponent {
|
|||||||
if (info.Channel is InputChannel.Tell) {
|
if (info.Channel is InputChannel.Tell) {
|
||||||
if (info.Rotate != RotateMode.None) {
|
if (info.Rotate != RotateMode.None) {
|
||||||
var idx = prevTemp != InputChannel.Tell
|
var idx = prevTemp != InputChannel.Tell
|
||||||
? 0
|
? 0 : info.Rotate == RotateMode.Reverse
|
||||||
: info.Rotate == RotateMode.Reverse
|
? -1 : 1;
|
||||||
? -1
|
|
||||||
: 1;
|
|
||||||
|
|
||||||
var tellInfo = Plugin.Functions.Chat.GetTellHistoryInfo(idx);
|
var tellInfo = Plugin.Functions.Chat.GetTellHistoryInfo(idx);
|
||||||
if (tellInfo != null && reason != null) {
|
if (tellInfo != null && reason != null) {
|
||||||
@@ -164,9 +162,7 @@ public sealed class ChatLogWindow : Window, IUiComponent {
|
|||||||
_tellTarget = null;
|
_tellTarget = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var mode = prevTemp == null
|
var mode = prevTemp == null ? RotateMode.None : info.Rotate;
|
||||||
? RotateMode.None
|
|
||||||
: info.Rotate;
|
|
||||||
|
|
||||||
if (info.Channel is InputChannel.Linkshell1 && info.Rotate != RotateMode.None) {
|
if (info.Channel is InputChannel.Linkshell1 && info.Rotate != RotateMode.None) {
|
||||||
var idx = Plugin.Functions.Chat.RotateLinkshellHistory(mode);
|
var idx = Plugin.Functions.Chat.RotateLinkshellHistory(mode);
|
||||||
@@ -439,7 +435,6 @@ public sealed class ChatLogWindow : Window, IUiComponent {
|
|||||||
DrawAutoComplete();
|
DrawAutoComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <returns>true if window was rendered</returns>
|
|
||||||
private unsafe void DrawChatLog()
|
private unsafe void DrawChatLog()
|
||||||
{
|
{
|
||||||
var resized = LastWindowSize != ImGui.GetWindowSize();
|
var resized = LastWindowSize != ImGui.GetWindowSize();
|
||||||
@@ -595,13 +590,24 @@ public sealed class ChatLogWindow : Window, IUiComponent {
|
|||||||
if (ImGui.IsItemDeactivated()) {
|
if (ImGui.IsItemDeactivated()) {
|
||||||
if (ImGui.IsKeyDown(ImGuiKey.Escape)) {
|
if (ImGui.IsKeyDown(ImGuiKey.Escape)) {
|
||||||
Chat = chatCopy;
|
Chat = chatCopy;
|
||||||
|
|
||||||
|
if (Plugin.Functions.Chat.UsesTellTempChannel)
|
||||||
|
{
|
||||||
|
Plugin.Functions.Chat.UsesTellTempChannel = false;
|
||||||
|
Plugin.Functions.Chat.SetChannel(Plugin.Functions.Chat.PreviousChannel ?? InputChannel.Say);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var enter = ImGui.IsKeyDown(ImGuiKey.Enter)
|
var enter = ImGui.IsKeyDown(ImGuiKey.Enter) || ImGui.IsKeyDown(ImGuiKey.KeypadEnter);
|
||||||
|| ImGui.IsKeyDown(ImGuiKey.KeypadEnter);
|
|
||||||
if (enter) {
|
if (enter) {
|
||||||
Plugin.CommandHelpWindow.IsOpen = false;
|
Plugin.CommandHelpWindow.IsOpen = false;
|
||||||
SendChatBox(activeTab);
|
SendChatBox(activeTab);
|
||||||
|
|
||||||
|
if (Plugin.Functions.Chat.UsesTellTempChannel)
|
||||||
|
{
|
||||||
|
Plugin.Functions.Chat.UsesTellTempChannel = false;
|
||||||
|
Plugin.Functions.Chat.SetChannel(Plugin.Functions.Chat.PreviousChannel ?? InputChannel.Say);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -615,6 +621,11 @@ public sealed class ChatLogWindow : Window, IUiComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_tempChannel = null;
|
_tempChannel = null;
|
||||||
|
if (Plugin.Functions.Chat.UsesTellTempChannel)
|
||||||
|
{
|
||||||
|
Plugin.Functions.Chat.UsesTellTempChannel = false;
|
||||||
|
Plugin.Functions.Chat.SetChannel(Plugin.Functions.Chat.PreviousChannel ?? InputChannel.Say);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui.BeginPopupContextItem()) {
|
if (ImGui.BeginPopupContextItem()) {
|
||||||
@@ -677,12 +688,11 @@ public sealed class ChatLogWindow : Window, IUiComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (_tempChannel != null) {
|
if (_tempChannel != null)
|
||||||
trimmed = $"{_tempChannel.Value.Prefix()} {trimmed}";
|
trimmed = $"{_tempChannel.Value.Prefix()} {trimmed}";
|
||||||
} else if (activeTab is { Channel: { } channel }) {
|
else if (activeTab is { Channel: { } channel })
|
||||||
trimmed = $"{channel.Prefix()} {trimmed}";
|
trimmed = $"{channel.Prefix()} {trimmed}";
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
var bytes = Encoding.UTF8.GetBytes(trimmed);
|
var bytes = Encoding.UTF8.GetBytes(trimmed);
|
||||||
AutoTranslate.ReplaceWithPayload(Plugin.DataManager, ref bytes);
|
AutoTranslate.ReplaceWithPayload(Plugin.DataManager, ref bytes);
|
||||||
|
|||||||
Reference in New Issue
Block a user