- API 15
- Migrate config for API 15 - Migrate database for API 15 - Allow usage of new target source - Implement first tell target option
This commit is contained in:
@@ -20,7 +20,7 @@ using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using InteropGenerator.Runtime;
|
||||
using Lumina.Text.ReadOnly;
|
||||
|
||||
using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType;
|
||||
using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.AtkValueType;
|
||||
|
||||
namespace ChatTwo.GameFunctions;
|
||||
|
||||
@@ -35,21 +35,21 @@ internal sealed unsafe class Chat : IDisposable
|
||||
|
||||
// Client::UI::AddonChatLog.OnRefresh
|
||||
[Signature("40 53 57 41 57 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? ?? ?? 4D 8B F8", DetourName = nameof(ChatLogRefreshDetour))]
|
||||
private Hook<ChatLogRefreshDelegate>? ChatLogRefreshHook { get; init; }
|
||||
private Hook<ChatLogRefreshDelegate>? ChatLogRefreshHook = null!;
|
||||
private delegate byte ChatLogRefreshDelegate(nint log, ushort eventId, AtkValue* value);
|
||||
|
||||
// Replace with CS version later
|
||||
[Signature("48 89 5C 24 ?? 55 56 57 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? ?? ?? 83 B9", DetourName = nameof(ContextMenuTellInForayDetour))]
|
||||
private Hook<ContextMenuTellInForayDelegate>? ContextMenuTellInForayHook { get; set; }
|
||||
private Hook<ContextMenuTellInForayDelegate>? ContextMenuTellInForayHook = null!;
|
||||
private delegate void ContextMenuTellInForayDelegate(RaptureShellModule* module, Utf8String* playerName, Utf8String* worldName, ushort worldId, ulong accountId, ulong contentId, ushort reason);
|
||||
|
||||
private Hook<AgentChatLog.Delegates.ChangeChannelName> ChangeChannelNameHook { get; init; }
|
||||
private Hook<RaptureShellModule.Delegates.ReplyInSelectedChatMode>? ReplyInSelectedChatModeHook { get; init; }
|
||||
private Hook<RaptureShellModule.Delegates.SetContextTellTarget>? SetChatLogTellTargetHook { get; init; }
|
||||
private readonly Hook<AgentChatLog.Delegates.ChangeChannelName>? ChangeChannelNameHook;
|
||||
private readonly Hook<RaptureShellModule.Delegates.ReplyInSelectedChatMode>? ReplyInSelectedChatModeHook;
|
||||
private readonly Hook<RaptureShellModule.Delegates.SetContextTellTarget>? SetChatLogTellTargetHook;
|
||||
|
||||
// Pointers
|
||||
[Signature("48 8D 35 ?? ?? ?? ?? 8B 05", ScanType = ScanType.StaticAddress)]
|
||||
private readonly char* CurrentCharacter = null!;
|
||||
[Signature("48 8D 1D ?? ?? ?? ?? 8B 05", ScanType = ScanType.StaticAddress)]
|
||||
private readonly char* LastTypedCharacter = null!;
|
||||
|
||||
private Plugin Plugin { get; }
|
||||
|
||||
@@ -64,7 +64,7 @@ internal sealed unsafe class Chat : IDisposable
|
||||
private long LastPlayerNameDisplayTypeRefresh;
|
||||
private PlayerNameDisplayType CurrentPlayerNameDisplayType = PlayerNameDisplayType.FullName;
|
||||
|
||||
internal Chat(Plugin plugin)
|
||||
public Chat(Plugin plugin)
|
||||
{
|
||||
Plugin = plugin;
|
||||
Plugin.GameInteropProvider.InitializeFromAttributes(this);
|
||||
@@ -131,7 +131,7 @@ internal sealed unsafe class Chat : IDisposable
|
||||
// If this function ever returns 0, it returns null instead.
|
||||
internal uint? GetChannelColor(ChatType type)
|
||||
{
|
||||
var parent = new ChatCode((ushort) type).Parent();
|
||||
var parent = type.Parent();
|
||||
switch (parent)
|
||||
{
|
||||
case ChatType.Debug:
|
||||
@@ -169,7 +169,7 @@ internal sealed unsafe class Chat : IDisposable
|
||||
if (eventId != 0x31 || value == null || value->UInt is not (0x05 or 0x0C))
|
||||
return ChatLogRefreshHook!.Original(log, eventId, value);
|
||||
|
||||
if (Plugin.Functions.KeybindManager.DirectChat && CurrentCharacter != null)
|
||||
if (Plugin.Functions.KeybindManager.DirectChat && LastTypedCharacter != null)
|
||||
{
|
||||
// FIXME: this whole system sucks
|
||||
// FIXME v2: I hate everything about this, but it works
|
||||
@@ -177,7 +177,7 @@ internal sealed unsafe class Chat : IDisposable
|
||||
{
|
||||
string? input = null;
|
||||
|
||||
var utf8Bytes = MemoryHelper.ReadRaw((nint)CurrentCharacter+0x4, 2);
|
||||
var utf8Bytes = MemoryHelper.ReadRaw((nint)LastTypedCharacter+0x4, 2);
|
||||
var chars = Encoding.UTF8.GetString(utf8Bytes).ToCharArray();
|
||||
if (chars.Length == 0)
|
||||
return;
|
||||
@@ -230,7 +230,7 @@ internal sealed unsafe class Chat : IDisposable
|
||||
|
||||
private CStringPointer ChangeChannelNameDetour(AgentChatLog* agent)
|
||||
{
|
||||
var ret = ChangeChannelNameHook.Original(agent);
|
||||
var ret = ChangeChannelNameHook!.Original(agent);
|
||||
if (agent == null)
|
||||
return ret;
|
||||
|
||||
@@ -572,6 +572,6 @@ internal sealed unsafe class Chat : IDisposable
|
||||
// second before the cutscene actually starts, because the game sets
|
||||
// the cutscene conditions before processing the skip.
|
||||
var raptureAtkUnitManager = RaptureAtkUnitManager.Instance();
|
||||
return raptureAtkUnitManager == null || raptureAtkUnitManager->UiFlags.HasFlag(UIModule.UiFlags.Chat);
|
||||
return raptureAtkUnitManager == null || raptureAtkUnitManager->UiFlags.HasFlag(UiFlags.Chat);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Text;
|
||||
using ChatTwo.Resources;
|
||||
using Dalamud.Memory;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.String;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||
@@ -18,13 +19,13 @@ public unsafe class ChatBox
|
||||
{
|
||||
var bytes = Encoding.UTF8.GetBytes(message);
|
||||
if (bytes.Length == 0)
|
||||
throw new ArgumentException("message is empty", nameof(message));
|
||||
throw new ArgumentException(Language.ChatBox_Error_Empty, nameof(message));
|
||||
|
||||
if (bytes.Length > 500)
|
||||
throw new ArgumentException("message is longer than 500 bytes", nameof(message));
|
||||
throw new ArgumentException(Language.ChatBox_Error_Too_Long, nameof(message));
|
||||
|
||||
if (message.Length != SanitiseText(message).Length)
|
||||
throw new ArgumentException("message contained invalid characters", nameof(message));
|
||||
throw new ArgumentException(Language.ChatBox_Error_Invalid, nameof(message));
|
||||
|
||||
SendMessageUnsafe(bytes);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ using FFXIVClientStructs.FFXIV.Client.UI.Info;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using Lumina.Excel;
|
||||
using Lumina.Excel.Sheets;
|
||||
using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType;
|
||||
using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.AtkValueType;
|
||||
|
||||
namespace ChatTwo.GameFunctions;
|
||||
|
||||
@@ -22,7 +22,7 @@ internal unsafe class GameFunctions : IDisposable
|
||||
{
|
||||
#region Hooks
|
||||
[Signature("E8 ?? ?? ?? ?? 48 85 C0 0F 84 ?? ?? ?? ?? 48 8B D0 49 8D 4F", DetourName = nameof(ResolveTextCommandPlaceholderDetour))]
|
||||
private Hook<ResolveTextCommandPlaceholderDelegate>? ResolveTextCommandPlaceholderHook { get; init; }
|
||||
private Hook<ResolveTextCommandPlaceholderDelegate>? ResolveTextCommandPlaceholderHook = null!;
|
||||
private delegate nint ResolveTextCommandPlaceholderDelegate(nint a1, byte* placeholderText, byte a3, byte a4);
|
||||
#endregion
|
||||
|
||||
@@ -132,7 +132,7 @@ internal unsafe class GameFunctions : IDisposable
|
||||
agent->AddonId = addon->Id;
|
||||
|
||||
// Skips early return
|
||||
atkStage->TooltipManager.Flag1 |= 2;
|
||||
atkStage->TooltipManager.TooltipType |= 2;
|
||||
addon->Show(false, 15);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace ChatTwo.GameFunctions.Types;
|
||||
|
||||
[Flags]
|
||||
internal enum ModifierFlag
|
||||
public enum ModifierFlag
|
||||
{
|
||||
None = 0,
|
||||
Shift = 1 << 0,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace ChatTwo.GameFunctions.Types;
|
||||
|
||||
internal enum TellReason
|
||||
public enum TellReason
|
||||
{
|
||||
Direct = 0,
|
||||
PartyFinder = 1,
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
using Dalamud.Game.ClientState.Objects.SubKinds;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||
|
||||
namespace ChatTwo.GameFunctions.Types;
|
||||
|
||||
internal sealed class TellTarget
|
||||
[Serializable]
|
||||
public class TellTarget
|
||||
{
|
||||
internal string Name { get; }
|
||||
internal ushort World { get; }
|
||||
internal ulong ContentId { get; }
|
||||
internal TellReason Reason { get; }
|
||||
public string Name { get; set; }
|
||||
public uint World { get; set; }
|
||||
public ulong ContentId { get; private set; }
|
||||
public TellReason Reason { get; private set; }
|
||||
|
||||
internal TellTarget(string name, ushort world, ulong contentId, TellReason reason)
|
||||
public TellTarget(string name, uint world, ulong contentId, TellReason reason)
|
||||
{
|
||||
Name = name;
|
||||
World = world;
|
||||
@@ -15,8 +19,22 @@ internal sealed class TellTarget
|
||||
Reason = reason;
|
||||
}
|
||||
|
||||
public bool IsSet() => Name.Length > 0 && World > 0;
|
||||
public bool IsSet()
|
||||
=> Name.Length > 0 && World > 0;
|
||||
|
||||
public string ToWorldString() => Sheets.WorldSheet.TryGetRow(World, out var worldRow) ? worldRow.Name.ToString() : string.Empty;
|
||||
public string ToTargetString() => $"{Name}@{ToWorldString()}";
|
||||
public string ToWorldString()
|
||||
=> Sheets.WorldSheet.TryGetRow(World, out var worldRow) ? worldRow.Name.ToString() : string.Empty;
|
||||
|
||||
public string ToTargetString()
|
||||
=> $"{Name}@{ToWorldString()}";
|
||||
|
||||
public unsafe void FromTarget(IPlayerCharacter target)
|
||||
{
|
||||
Name = target.Name.TextValue;
|
||||
World = target.HomeWorld.RowId;
|
||||
ContentId = ((Character*)target.Address)->ContentId;
|
||||
}
|
||||
|
||||
public static TellTarget Empty() => new(string.Empty, 0, 0, TellReason.Direct);
|
||||
public static TellTarget From(TellTarget t) => new(t.Name, t.World, t.ContentId, t.Reason);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user