From b4cb8b25ecf0f526b8e88395f13434b49fe94c52 Mon Sep 17 00:00:00 2001 From: Infi Date: Thu, 30 Apr 2026 02:59:58 +0200 Subject: [PATCH] - API 15 - Migrate config for API 15 - Migrate database for API 15 - Allow usage of new target source - Implement first tell target option --- ChatTwo.Tests/MessageStoreTest.cs | 9 +- ChatTwo/ChatTwo.csproj | 12 +- ChatTwo/Chunk.cs | 15 +- ChatTwo/Code/ChatCode.cs | 109 +++------ ChatTwo/Code/ChatSource.cs | 47 +++- ChatTwo/Code/ChatSourceExt.cs | 20 +- ChatTwo/Code/ChatTypeExt.cs | 59 +++++ ChatTwo/Code/InputChannelExt.cs | 42 ++-- ChatTwo/Configuration.cs | 113 +++++---- ChatTwo/EmoteCache.cs | 20 +- ChatTwo/FontManager.cs | 21 +- ChatTwo/GameFunctions/Chat.cs | 28 +-- ChatTwo/GameFunctions/ChatBox.cs | 7 +- ChatTwo/GameFunctions/GameFunctions.cs | 6 +- ChatTwo/GameFunctions/Types/ModifierFlag.cs | 2 +- ChatTwo/GameFunctions/Types/TellReason.cs | 2 +- ChatTwo/GameFunctions/Types/TellTarget.cs | 36 ++- ChatTwo/Http/MessageProtocol/DataStructure.cs | 2 +- ChatTwo/Http/Processing.cs | 2 +- ChatTwo/Ipc/ExtraChat.cs | 10 +- ChatTwo/Ipc/TypingIpc.cs | 2 +- ChatTwo/Message.cs | 95 ++------ ChatTwo/MessageManager.cs | 42 ++-- ChatTwo/MessageStore.cs | 104 +++++--- ChatTwo/PayloadHandler.cs | 14 +- ChatTwo/Plugin.cs | 76 ++++-- ChatTwo/Resources/Language.Designer.cs | 81 +++++++ ChatTwo/Resources/Language.ca.resx | 27 +++ ChatTwo/Resources/Language.de.resx | 27 +++ ChatTwo/Resources/Language.es.resx | 27 +++ ChatTwo/Resources/Language.fr.resx | 27 +++ ChatTwo/Resources/Language.it.resx | 27 +++ ChatTwo/Resources/Language.ja.resx | 27 +++ ChatTwo/Resources/Language.ko.resx | 27 +++ ChatTwo/Resources/Language.nl.resx | 27 +++ ChatTwo/Resources/Language.pt-BR.resx | 27 +++ ChatTwo/Resources/Language.resx | 27 +++ ChatTwo/Resources/Language.ro.resx | 27 +++ ChatTwo/Resources/Language.ru.resx | 27 +++ ChatTwo/Resources/Language.sv.resx | 27 +++ ChatTwo/Resources/Language.zh-Hans.resx | 27 +++ ChatTwo/Resources/Language.zh-Hant.resx | 27 +++ ChatTwo/Sheets.cs | 11 +- ChatTwo/Ui/ChatLogWindow.cs | 28 ++- ChatTwo/Ui/DbViewer.cs | 26 +- ChatTwo/Ui/InputPreview.cs | 2 +- ChatTwo/Ui/SettingsTabs/Database.cs | 5 +- ChatTwo/Ui/SettingsTabs/Display.cs | 13 +- ChatTwo/Ui/SettingsTabs/Fonts.cs | 6 +- ChatTwo/Ui/SettingsTabs/Tabs.cs | 78 +++++- ChatTwo/Util/ChunkUtil.cs | 85 ++++--- ChatTwo/Util/ColourUtil.cs | 7 +- ChatTwo/Util/ExtraPayload.cs | 2 + ChatTwo/Util/ImGuiUtil.cs | 28 ++- ChatTwo/Util/TabsUtil.cs | 224 +++++++++--------- ChatTwo/packages.lock.json | 6 +- 56 files changed, 1286 insertions(+), 616 deletions(-) diff --git a/ChatTwo.Tests/MessageStoreTest.cs b/ChatTwo.Tests/MessageStoreTest.cs index 81b3a18..6e517b0 100644 --- a/ChatTwo.Tests/MessageStoreTest.cs +++ b/ChatTwo.Tests/MessageStoreTest.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using ChatTwo.Code; using ChatTwo.Util; +using Dalamud.Game.Text; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Game.Text.SeStringHandling.Payloads; using JetBrains.Annotations; @@ -207,17 +208,17 @@ public class MessageStoreTest { new TextChunk(ChunkSource.Content, new UriPayload(new Uri("https://dalamud.dev")), "chat 2 uri"), ]).ToList(); + var chatCode = new ChatCode((XivChatType)46, XivChatRelationKind.LocalPlayer, XivChatRelationKind.EngagedEnemy); return new Message( uniqId ? Guid.NewGuid() : Guid.Parse("f011343e-6a21-49e5-a6f9-238f0f1f8c2c"), receiver, 54321, dateTime ?? DateTimeOffset.FromUnixTimeMilliseconds(1713520182440), - new ChatCode(12345), + chatCode, ChunkUtil.ToChunks(senderSeString, ChunkSource.Sender, ChatType.Debug).ToList(), contentChunks, senderSeString, contentSeString, - new SortCode(ChatType.Crafting, ChatSource.AlliancePet), extraChatId ); } @@ -230,10 +231,10 @@ public class MessageStoreTest { // Assert time is within 1 second var timeDifference = Math.Abs(input.Date.ToUniversalTime().Subtract(output.Date.ToUniversalTime()).TotalSeconds); Assert.IsTrue(timeDifference < 1); - Assert.AreEqual(input.Code.Raw, output.Code.Raw); + Assert.AreEqual(input.Code, output.Code); Assert.AreEqual($"{input.SenderSource.Encode():X}", $"{output.SenderSource.Encode():X}"); Assert.AreEqual($"{input.ContentSource.Encode():X}", $"{output.ContentSource.Encode():X}"); - Assert.AreEqual(input.SortCode, output.SortCode); + Assert.AreEqual(input.SortCodeV2, output.SortCodeV2); Assert.AreEqual(input.ExtraChatChannel, output.ExtraChatChannel); // Check chunks. diff --git a/ChatTwo/ChatTwo.csproj b/ChatTwo/ChatTwo.csproj index ecd69f2..618a6a3 100755 --- a/ChatTwo/ChatTwo.csproj +++ b/ChatTwo/ChatTwo.csproj @@ -1,15 +1,7 @@ - + - 1.34.7 + 1.35.0 enable - enable - true - false - true - true - latest - true - full diff --git a/ChatTwo/Chunk.cs b/ChatTwo/Chunk.cs index 54b57a4..bfde9a7 100755 --- a/ChatTwo/Chunk.cs +++ b/ChatTwo/Chunk.cs @@ -57,16 +57,11 @@ public enum ChunkSource [MessagePackObject(AllowPrivate = true)] public class TextChunk : Chunk { - [Key(2)] - public ChatType? FallbackColour { get; set; } - [Key(3)] - public uint? Foreground { get; set; } - [Key(4)] - public uint? Glow { get; set; } - [Key(5)] - public bool Italic { get; set; } - [Key(6)] - public string Content { get; set; } + [Key(2)] public ChatType? FallbackColour; + [Key(3)] public uint? Foreground; + [Key(4)] public uint? Glow; + [Key(5)] public bool Italic; + [Key(6)] public string Content; private TextChunk(Chunk chunk, string content) : base(chunk.Source, chunk.Link) { diff --git a/ChatTwo/Code/ChatCode.cs b/ChatTwo/Code/ChatCode.cs index 59cdf4f..94a06f0 100755 --- a/ChatTwo/Code/ChatCode.cs +++ b/ChatTwo/Code/ChatCode.cs @@ -1,84 +1,24 @@ +using Dalamud.Game.Text; + namespace ChatTwo.Code; -internal class ChatCode +public class ChatCode { - private const ushort Clear7 = ~(~0 << 7); + public ChatType Type { get; } + public XivChatRelationKind Source { get; } + public XivChatRelationKind Target { get; } - internal ushort Raw { get; } - - internal ChatType Type { get; } - internal ChatSource Source { get; } - internal ChatSource Target { get; } - private ChatSource SourceFrom(ushort shift) => (ChatSource) (1 << ((Raw >> shift) & 0xF)); - - internal ChatCode(ushort raw) + public ChatCode(XivChatType type, XivChatRelationKind source, XivChatRelationKind target) { - Raw = raw; - Type = (ChatType) (Raw & Clear7); - Source = SourceFrom(11); - Target = SourceFrom(7); + Type = (ChatType)type; + Source = source; + Target = target; } - internal ChatType Parent() => Type switch - { - ChatType.Say => ChatType.Say, - ChatType.GmSay => ChatType.Say, - ChatType.Shout => ChatType.Shout, - ChatType.GmShout => ChatType.Shout, - ChatType.TellOutgoing => ChatType.TellOutgoing, - ChatType.TellIncoming => ChatType.TellOutgoing, - ChatType.GmTell => ChatType.TellOutgoing, - ChatType.Party => ChatType.Party, - ChatType.CrossParty => ChatType.Party, - ChatType.GmParty => ChatType.Party, - ChatType.Linkshell1 => ChatType.Linkshell1, - ChatType.GmLinkshell1 => ChatType.Linkshell1, - ChatType.Linkshell2 => ChatType.Linkshell2, - ChatType.GmLinkshell2 => ChatType.Linkshell2, - ChatType.Linkshell3 => ChatType.Linkshell3, - ChatType.GmLinkshell3 => ChatType.Linkshell3, - ChatType.Linkshell4 => ChatType.Linkshell4, - ChatType.GmLinkshell4 => ChatType.Linkshell4, - ChatType.Linkshell5 => ChatType.Linkshell5, - ChatType.GmLinkshell5 => ChatType.Linkshell5, - ChatType.Linkshell6 => ChatType.Linkshell6, - ChatType.GmLinkshell6 => ChatType.Linkshell6, - ChatType.Linkshell7 => ChatType.Linkshell7, - ChatType.GmLinkshell7 => ChatType.Linkshell7, - ChatType.Linkshell8 => ChatType.Linkshell8, - ChatType.GmLinkshell8 => ChatType.Linkshell8, - ChatType.FreeCompany => ChatType.FreeCompany, - ChatType.GmFreeCompany => ChatType.FreeCompany, - ChatType.NoviceNetwork => ChatType.NoviceNetwork, - ChatType.GmNoviceNetwork => ChatType.NoviceNetwork, - ChatType.CustomEmote => ChatType.CustomEmote, - ChatType.StandardEmote => ChatType.StandardEmote, - ChatType.Yell => ChatType.Yell, - ChatType.GmYell => ChatType.Yell, - ChatType.GainBuff => ChatType.GainBuff, - ChatType.LoseBuff => ChatType.GainBuff, - ChatType.GainDebuff => ChatType.GainDebuff, - ChatType.LoseDebuff => ChatType.GainDebuff, - ChatType.System => ChatType.System, - ChatType.Alarm => ChatType.System, - ChatType.GlamourNotifications => ChatType.System, - ChatType.RetainerSale => ChatType.System, - ChatType.PeriodicRecruitmentNotification => ChatType.System, - ChatType.Sign => ChatType.System, - ChatType.Orchestrion => ChatType.System, - ChatType.MessageBook => ChatType.System, - ChatType.NpcDialogue => ChatType.NpcDialogue, - ChatType.NpcAnnouncement => ChatType.NpcDialogue, - ChatType.LootRoll => ChatType.LootRoll, - ChatType.RandomNumber => ChatType.LootRoll, - ChatType.FreeCompanyAnnouncement => ChatType.FreeCompanyAnnouncement, - ChatType.FreeCompanyLoginLogout => ChatType.FreeCompanyAnnouncement, - ChatType.PvpTeamAnnouncement => ChatType.PvpTeamAnnouncement, - ChatType.PvpTeamLoginLogout => ChatType.PvpTeamAnnouncement, - _ => Type, - }; + public ChatCode(byte type, byte source, byte target) + : this((XivChatType)type, (XivChatRelationKind)source, (XivChatRelationKind)target) {} - internal bool IsBattle() + public bool IsBattle() { switch (Type) { @@ -101,7 +41,7 @@ internal class ChatCode } } - internal bool IsPlayerMessage() + public bool IsPlayerMessage() { switch (Type) { @@ -143,4 +83,25 @@ internal class ChatCode return false; } } + + public int ToSortCodeV2() + { + return (byte)Type << 16 | (byte)Source << 8 | (byte)Target; + } + + public override bool Equals(object? obj) + { + if (obj == null) + return false; + + if (obj is not ChatCode code) + return false; + + return GetHashCode() == code.GetHashCode(); + } + + public override int GetHashCode() + { + return (byte)Type << 16 | (byte)Source << 8 | (byte)Target; + } } diff --git a/ChatTwo/Code/ChatSource.cs b/ChatTwo/Code/ChatSource.cs index 37b8ed5..d2f4fe5 100755 --- a/ChatTwo/Code/ChatSource.cs +++ b/ChatTwo/Code/ChatSource.cs @@ -1,17 +1,42 @@ +using Dalamud.Game.Text; + namespace ChatTwo.Code; [Flags] public enum ChatSource : ushort { - Self = 2, - PartyMember = 4, - AllianceMember = 8, - Other = 16, - EngagedEnemy = 32, - UnengagedEnemy = 64, - FriendlyNpc = 128, - SelfPet = 256, - PartyPet = 512, - AlliancePet = 1024, - OtherPet = 2048, + None = 0, + + /// The player currently controlled by the local client. + LocalPlayer = 1 << XivChatRelationKind.LocalPlayer, + + /// A player in the same 4-man or 8-man party as the local player. + PartyMember = 1 << XivChatRelationKind.PartyMember, + + /// A player in the same alliance raid. + AllianceMember = 1 << XivChatRelationKind.AllianceMember, + + /// A player not in the local player's party or alliance. + OtherPlayer = 1 << XivChatRelationKind.OtherPlayer, + + /// An enemy entity that is currently in combat with the player or party. + EngagedEnemy = 1 << XivChatRelationKind.EngagedEnemy, + + /// An enemy entity that is not yet in combat or claimed. + UnengagedEnemy = 1 << XivChatRelationKind.UnengagedEnemy, + + /// An NPC that is friendly or neutral to the player (e.g., EventNPCs). + FriendlyNpc = 1 << XivChatRelationKind.FriendlyNpc, + + /// A pet (Summoner/Scholar) or companion (Chocobo) belonging to the local player. + PetOrCompanion = 1 << XivChatRelationKind.PetOrCompanion, + + /// A pet or companion belonging to a member of the local player's party. + PetOrCompanionParty = 1 << XivChatRelationKind.PetOrCompanionParty, + + /// A pet or companion belonging to a member of the alliance. + PetOrCompanionAlliance = 1 << XivChatRelationKind.PetOrCompanionAlliance, + + /// A pet or companion belonging to a player not in the party or alliance. + PetOrCompanionOther = 1 << XivChatRelationKind.PetOrCompanionOther, } diff --git a/ChatTwo/Code/ChatSourceExt.cs b/ChatTwo/Code/ChatSourceExt.cs index d6df7b0..eeb7908 100755 --- a/ChatTwo/Code/ChatSourceExt.cs +++ b/ChatTwo/Code/ChatSourceExt.cs @@ -5,24 +5,24 @@ namespace ChatTwo.Code; internal static class ChatSourceExt { internal const ChatSource All = - ChatSource.Self | ChatSource.PartyMember | ChatSource.AllianceMember | - ChatSource.Other | ChatSource.EngagedEnemy | ChatSource.UnengagedEnemy | - ChatSource.FriendlyNpc | ChatSource.SelfPet | ChatSource.PartyPet | - ChatSource.AlliancePet | ChatSource.OtherPet; + ChatSource.LocalPlayer | ChatSource.PartyMember | ChatSource.AllianceMember | + ChatSource.OtherPlayer | ChatSource.EngagedEnemy | ChatSource.UnengagedEnemy | + ChatSource.FriendlyNpc | ChatSource.PetOrCompanion | ChatSource.PetOrCompanionParty | + ChatSource.PetOrCompanionAlliance | ChatSource.PetOrCompanionOther; internal static string Name(this ChatSource source) => source switch { - ChatSource.Self => Language.ChatSource_Self, + ChatSource.LocalPlayer => Language.ChatSource_Self, ChatSource.PartyMember => Language.ChatSource_PartyMember, ChatSource.AllianceMember => Language.ChatSource_AllianceMember, - ChatSource.Other => Language.ChatSource_Other, + ChatSource.OtherPlayer => Language.ChatSource_Other, ChatSource.EngagedEnemy => Language.ChatSource_EngagedEnemy, ChatSource.UnengagedEnemy => Language.ChatSource_UnengagedEnemy, ChatSource.FriendlyNpc => Language.ChatSource_FriendlyNpc, - ChatSource.SelfPet => Language.ChatSource_SelfPet, - ChatSource.PartyPet => Language.ChatSource_PartyPet, - ChatSource.AlliancePet => Language.ChatSource_AlliancePet, - ChatSource.OtherPet => Language.ChatSource_OtherPet, + ChatSource.PetOrCompanion => Language.ChatSource_SelfPet, + ChatSource.PetOrCompanionParty => Language.ChatSource_PartyPet, + ChatSource.PetOrCompanionAlliance => Language.ChatSource_AlliancePet, + ChatSource.PetOrCompanionOther => Language.ChatSource_OtherPet, _ => throw new ArgumentOutOfRangeException(nameof(source), source, null), }; } diff --git a/ChatTwo/Code/ChatTypeExt.cs b/ChatTwo/Code/ChatTypeExt.cs index 616b192..5d54d30 100755 --- a/ChatTwo/Code/ChatTypeExt.cs +++ b/ChatTwo/Code/ChatTypeExt.cs @@ -424,4 +424,63 @@ internal static class ChatTypeExt ChatType.PvpTeamLoginLogout => true, _ => false, }; + + internal static ChatType Parent(this ChatType type) => type switch + { + ChatType.Say => ChatType.Say, + ChatType.GmSay => ChatType.Say, + ChatType.Shout => ChatType.Shout, + ChatType.GmShout => ChatType.Shout, + ChatType.TellOutgoing => ChatType.TellOutgoing, + ChatType.TellIncoming => ChatType.TellOutgoing, + ChatType.GmTell => ChatType.TellOutgoing, + ChatType.Party => ChatType.Party, + ChatType.CrossParty => ChatType.Party, + ChatType.GmParty => ChatType.Party, + ChatType.Linkshell1 => ChatType.Linkshell1, + ChatType.GmLinkshell1 => ChatType.Linkshell1, + ChatType.Linkshell2 => ChatType.Linkshell2, + ChatType.GmLinkshell2 => ChatType.Linkshell2, + ChatType.Linkshell3 => ChatType.Linkshell3, + ChatType.GmLinkshell3 => ChatType.Linkshell3, + ChatType.Linkshell4 => ChatType.Linkshell4, + ChatType.GmLinkshell4 => ChatType.Linkshell4, + ChatType.Linkshell5 => ChatType.Linkshell5, + ChatType.GmLinkshell5 => ChatType.Linkshell5, + ChatType.Linkshell6 => ChatType.Linkshell6, + ChatType.GmLinkshell6 => ChatType.Linkshell6, + ChatType.Linkshell7 => ChatType.Linkshell7, + ChatType.GmLinkshell7 => ChatType.Linkshell7, + ChatType.Linkshell8 => ChatType.Linkshell8, + ChatType.GmLinkshell8 => ChatType.Linkshell8, + ChatType.FreeCompany => ChatType.FreeCompany, + ChatType.GmFreeCompany => ChatType.FreeCompany, + ChatType.NoviceNetwork => ChatType.NoviceNetwork, + ChatType.GmNoviceNetwork => ChatType.NoviceNetwork, + ChatType.CustomEmote => ChatType.CustomEmote, + ChatType.StandardEmote => ChatType.StandardEmote, + ChatType.Yell => ChatType.Yell, + ChatType.GmYell => ChatType.Yell, + ChatType.GainBuff => ChatType.GainBuff, + ChatType.LoseBuff => ChatType.GainBuff, + ChatType.GainDebuff => ChatType.GainDebuff, + ChatType.LoseDebuff => ChatType.GainDebuff, + ChatType.System => ChatType.System, + ChatType.Alarm => ChatType.System, + ChatType.GlamourNotifications => ChatType.System, + ChatType.RetainerSale => ChatType.System, + ChatType.PeriodicRecruitmentNotification => ChatType.System, + ChatType.Sign => ChatType.System, + ChatType.Orchestrion => ChatType.System, + ChatType.MessageBook => ChatType.System, + ChatType.NpcDialogue => ChatType.NpcDialogue, + ChatType.NpcAnnouncement => ChatType.NpcDialogue, + ChatType.LootRoll => ChatType.LootRoll, + ChatType.RandomNumber => ChatType.LootRoll, + ChatType.FreeCompanyAnnouncement => ChatType.FreeCompanyAnnouncement, + ChatType.FreeCompanyLoginLogout => ChatType.FreeCompanyAnnouncement, + ChatType.PvpTeamAnnouncement => ChatType.PvpTeamAnnouncement, + ChatType.PvpTeamLoginLogout => ChatType.PvpTeamAnnouncement, + _ => type, + }; } diff --git a/ChatTwo/Code/InputChannelExt.cs b/ChatTwo/Code/InputChannelExt.cs index f5a2555..3e15a3b 100755 --- a/ChatTwo/Code/InputChannelExt.cs +++ b/ChatTwo/Code/InputChannelExt.cs @@ -80,25 +80,25 @@ internal static class InputChannelExt InputChannel.Alliance => "/alliance", InputChannel.Yell => "/yell", InputChannel.Shout => "/shout", - InputChannel.FreeCompany => "/freecompany", - InputChannel.PvpTeam => "/pvpteam", - InputChannel.NoviceNetwork => "/beginner", - InputChannel.CrossLinkshell1 => "/cwlinkshell1", - InputChannel.CrossLinkshell2 => "/cwlinkshell2", - InputChannel.CrossLinkshell3 => "/cwlinkshell3", - InputChannel.CrossLinkshell4 => "/cwlinkshell4", - InputChannel.CrossLinkshell5 => "/cwlinkshell5", - InputChannel.CrossLinkshell6 => "/cwlinkshell6", - InputChannel.CrossLinkshell7 => "/cwlinkshell7", - InputChannel.CrossLinkshell8 => "/cwlinkshell8", - InputChannel.Linkshell1 => "/linkshell1", - InputChannel.Linkshell2 => "/linkshell2", - InputChannel.Linkshell3 => "/linkshell3", - InputChannel.Linkshell4 => "/linkshell4", - InputChannel.Linkshell5 => "/linkshell5", - InputChannel.Linkshell6 => "/linkshell6", - InputChannel.Linkshell7 => "/linkshell7", - InputChannel.Linkshell8 => "/linkshell8", + InputChannel.FreeCompany => "/fc", + InputChannel.PvpTeam => "/pt", + InputChannel.NoviceNetwork => "/n", + InputChannel.CrossLinkshell1 => "/cwl1", + InputChannel.CrossLinkshell2 => "/cwl2", + InputChannel.CrossLinkshell3 => "/cwl3", + InputChannel.CrossLinkshell4 => "/cwl4", + InputChannel.CrossLinkshell5 => "/cwl5", + InputChannel.CrossLinkshell6 => "/cwl6", + InputChannel.CrossLinkshell7 => "/cwl7", + InputChannel.CrossLinkshell8 => "/cwl8", + InputChannel.Linkshell1 => "/l1", + InputChannel.Linkshell2 => "/l2", + InputChannel.Linkshell3 => "/l3", + InputChannel.Linkshell4 => "/l4", + InputChannel.Linkshell5 => "/l5", + InputChannel.Linkshell6 => "/l6", + InputChannel.Linkshell7 => "/l7", + InputChannel.Linkshell8 => "/l8", InputChannel.ExtraChatLinkshell1 => "/ecl1", InputChannel.ExtraChatLinkshell2 => "/ecl2", InputChannel.ExtraChatLinkshell3 => "/ecl3", @@ -112,7 +112,7 @@ internal static class InputChannelExt public static IEnumerable? TextCommands(this InputChannel channel) { - var ids = channel switch + uint[] ids = channel switch { InputChannel.Tell => [104, 118], InputChannel.Say => [102], @@ -139,7 +139,7 @@ internal static class InputChannelExt InputChannel.Linkshell6 => [112], InputChannel.Linkshell7 => [113], InputChannel.Linkshell8 => [114], - _ => Array.Empty(), + _ => [] }; if (ids.Length == 0) diff --git a/ChatTwo/Configuration.cs b/ChatTwo/Configuration.cs index 8c6201c..50c4202 100755 --- a/ChatTwo/Configuration.cs +++ b/ChatTwo/Configuration.cs @@ -12,7 +12,7 @@ using Dalamud.Bindings.ImGui; namespace ChatTwo; [Serializable] -internal class ConfigKeyBind +public class ConfigKeyBind { public ModifierFlag Modifier; public VirtualKey Key; @@ -31,9 +31,9 @@ internal class ConfigKeyBind } [Serializable] -internal class Configuration : IPluginConfiguration +public class Configuration : IPluginConfiguration { - private const int LatestVersion = 5; + private const int LatestVersion = 6; public int Version { get; set; } = LatestVersion; @@ -46,7 +46,11 @@ internal class Configuration : IPluginConfiguration public bool HideWhenInactive; public int InactivityHideTimeout = 10; public bool InactivityHideActiveDuringBattle = true; - public Dictionary? InactivityHideChannels; + + [Obsolete("Use InactivityHideChannelsV2 instead")] + public Dictionary InactivityHideChannels = []; + + public Dictionary InactivityHideChannelsV2 = []; public bool InactivityHideExtraChatAll = true; public HashSet InactivityHideExtraChatChannels = []; public bool ShowHideButton = true; @@ -88,18 +92,18 @@ internal class Configuration : IPluginConfiguration public SingleFontSpec GlobalFontV2 = new() { // dalamud only ships KR as regular, which chat2 used previously for global fonts - FontId = new DalamudAssetFontAndFamilyId(DalamudAsset.NotoSansKrRegular), + FontId = new DalamudAssetFontAndFamilyId(DalamudAsset.NotoSansCjkRegular), SizePt = 12.75f, }; public SingleFontSpec JapaneseFontV2 = new() { - FontId = new DalamudAssetFontAndFamilyId(DalamudAsset.NotoSansJpMedium), + FontId = new DalamudAssetFontAndFamilyId(DalamudAsset.NotoSansCjkMedium), SizePt = 12.75f, }; public bool ItalicEnabled; public SingleFontSpec ItalicFontV2 = new() { - FontId = new DalamudAssetFontAndFamilyId(DalamudAsset.NotoSansKrRegular), + FontId = new DalamudAssetFontAndFamilyId(DalamudAsset.NotoSansCjkRegular), SizePt = 12.75f, }; @@ -122,7 +126,7 @@ internal class Configuration : IPluginConfiguration public HashSet AuthStore = []; public int WebinterfaceMaxLinesToSend = 1000; // 1-10000 - internal void UpdateFrom(Configuration other, bool backToOriginal) + public void UpdateFrom(Configuration other, bool backToOriginal) { if (backToOriginal) foreach (var tab in Tabs.Where(t => t.PopOut)) @@ -137,7 +141,7 @@ internal class Configuration : IPluginConfiguration HideWhenInactive = other.HideWhenInactive; InactivityHideTimeout = other.InactivityHideTimeout; InactivityHideActiveDuringBattle = other.InactivityHideActiveDuringBattle; - InactivityHideChannels = other.InactivityHideChannels?.ToDictionary(entry => entry.Key, entry => entry.Value); + InactivityHideChannelsV2 = other.InactivityHideChannelsV2.ToDictionary(pair => pair.Key, pair => pair.Value); InactivityHideExtraChatAll = other.InactivityHideExtraChatAll; InactivityHideExtraChatChannels = other.InactivityHideExtraChatChannels.ToHashSet(); ShowHideButton = other.ShowHideButton; @@ -195,14 +199,14 @@ internal class Configuration : IPluginConfiguration } [Serializable] -internal enum UnreadMode +public enum UnreadMode { All, Unseen, None, } -internal static class UnreadModeExt +public static class UnreadModeExt { internal static string Name(this UnreadMode mode) => mode switch { @@ -222,10 +226,14 @@ internal static class UnreadModeExt } [Serializable] -internal class Tab +public class Tab { public string Name = Language.Tab_DefaultName; + + [Obsolete("Removed in favor of SelectedChannels")] public Dictionary ChatCodes = new(); + + public Dictionary SelectedChannels = new(); public bool ExtraChatAll; public HashSet ExtraChatChannels = []; @@ -249,6 +257,10 @@ internal class Tab public bool HideInBattle; public bool HideWhenInactive; + public bool IsTempTab; + public bool AllSenderMessages; + public TellTarget TellTarget = TellTarget.Empty(); + [NonSerialized] public uint Unread; [NonSerialized] public uint LastSendUnread; [NonSerialized] public long LastActivity; @@ -258,27 +270,31 @@ internal class Tab [NonSerialized] public Guid Identifier = Guid.NewGuid(); - internal bool Matches(Message message) => message.Matches(ChatCodes, ExtraChatAll, ExtraChatChannels); + public bool Matches(Message message) + { + return message.Matches(SelectedChannels, ExtraChatAll, ExtraChatChannels); + } - internal void AddMessage(Message message, bool unread = true) + public void AddMessage(Message message, bool unread = true) { Messages.AddPrune(message, MessageManager.MessageDisplayLimit); if (!unread) return; Unread += 1; - if (message.Matches(Plugin.Config.InactivityHideChannels!, Plugin.Config.InactivityHideExtraChatAll, Plugin.Config.InactivityHideExtraChatChannels)) + if (message.Matches(Plugin.Config.InactivityHideChannelsV2, Plugin.Config.InactivityHideExtraChatAll, Plugin.Config.InactivityHideExtraChatChannels)) LastActivity = Environment.TickCount64; } - internal void Clear() => Messages.Clear(); + public void Clear() + => Messages.Clear(); - internal Tab Clone() + public Tab Clone() { return new Tab { Name = Name, - ChatCodes = ChatCodes.ToDictionary(entry => entry.Key, entry => entry.Value), + SelectedChannels = SelectedChannels.ToDictionary(pair => pair.Key, pair => pair.Value), ExtraChatAll = ExtraChatAll, ExtraChatChannels = ExtraChatChannels.ToHashSet(), UnreadMode = UnreadMode, @@ -302,6 +318,9 @@ internal class Tab HideInLoadingScreens = HideInLoadingScreens, HideInBattle = HideInBattle, HideWhenInactive = HideWhenInactive, + IsTempTab = IsTempTab, + AllSenderMessages = AllSenderMessages, + TellTarget = TellTarget.From(TellTarget), }; } @@ -309,7 +328,7 @@ internal class Tab /// MessageList provides an ordered list of messages with duplicate ID /// tracking, sorting and mutex protection. /// - internal class MessageList + public class MessageList { private readonly SemaphoreSlim LockSlim = new(1, 1); @@ -422,7 +441,7 @@ internal class Tab return new RLockedMessageList(LockSlim, Messages); } - internal class RLockedMessageList(SemaphoreSlim lockSlim, List messages) : IReadOnlyList, IDisposable + public class RLockedMessageList(SemaphoreSlim lockSlim, List messages) : IReadOnlyList, IDisposable { public IEnumerator GetEnumerator() { @@ -446,31 +465,31 @@ internal class Tab } } -internal class UsedChannel +public class UsedChannel { - internal InputChannel Channel = InputChannel.Invalid; - internal List Name = []; - internal TellTarget? TellTarget; + public InputChannel Channel = InputChannel.Invalid; + public List Name = []; + public TellTarget? TellTarget; - internal bool UseTempChannel; - internal InputChannel TempChannel = InputChannel.Invalid; - internal TellTarget? TempTellTarget; + public bool UseTempChannel; + public InputChannel TempChannel = InputChannel.Invalid; + public TellTarget? TempTellTarget; - internal void ResetTempChannel() + public void ResetTempChannel() { UseTempChannel = false; TempTellTarget = null; TempChannel = InputChannel.Invalid; } - internal void SetChannel(InputChannel channel) + public void SetChannel(InputChannel channel) { Channel = channel; } } [Serializable] -internal enum PreviewPosition +public enum PreviewPosition { None, Inside, @@ -479,9 +498,9 @@ internal enum PreviewPosition Tooltip, } -internal static class PreviewPositionExt +public static class PreviewPositionExt { - internal static string Name(this PreviewPosition position) => position switch + public static string Name(this PreviewPosition position) => position switch { PreviewPosition.None => Language.Options_Preview_None, PreviewPosition.Inside => Language.Options_Preview_Inside, @@ -493,16 +512,16 @@ internal static class PreviewPositionExt } [Serializable] -internal enum CommandHelpSide +public enum CommandHelpSide { None, Left, Right, } -internal static class CommandHelpSideExt +public static class CommandHelpSideExt { - internal static string Name(this CommandHelpSide side) => side switch + public static string Name(this CommandHelpSide side) => side switch { CommandHelpSide.None => Language.CommandHelpSide_None, CommandHelpSide.Left => Language.CommandHelpSide_Left, @@ -512,22 +531,22 @@ internal static class CommandHelpSideExt } [Serializable] -internal enum KeybindMode +public enum KeybindMode { Flexible, Strict, } -internal static class KeybindModeExt +public static class KeybindModeExt { - internal static string Name(this KeybindMode mode) => mode switch + public static string Name(this KeybindMode mode) => mode switch { KeybindMode.Flexible => Language.KeybindMode_Flexible_Name, KeybindMode.Strict => Language.KeybindMode_Strict_Name, _ => throw new ArgumentOutOfRangeException(nameof(mode), mode, null), }; - internal static string? Tooltip(this KeybindMode mode) => mode switch + public static string? Tooltip(this KeybindMode mode) => mode switch { KeybindMode.Flexible => Language.KeybindMode_Flexible_Tooltip, KeybindMode.Strict => Language.KeybindMode_Strict_Tooltip, @@ -536,7 +555,7 @@ internal static class KeybindModeExt } [Serializable] -internal enum LanguageOverride +public enum LanguageOverride { None, ChineseSimplified, @@ -559,9 +578,9 @@ internal enum LanguageOverride Swedish, } -internal static class LanguageOverrideExt +public static class LanguageOverrideExt { - internal static string Name(this LanguageOverride mode) => mode switch + public static string Name(this LanguageOverride mode) => mode switch { LanguageOverride.None => Language.LanguageOverride_None, LanguageOverride.ChineseSimplified => "简体中文", @@ -583,7 +602,7 @@ internal static class LanguageOverrideExt _ => throw new ArgumentOutOfRangeException(nameof(mode), mode, null), }; - internal static string Code(this LanguageOverride mode) => mode switch + public static string Code(this LanguageOverride mode) => mode switch { LanguageOverride.None => "", LanguageOverride.ChineseSimplified => "zh-hans", @@ -608,7 +627,7 @@ internal static class LanguageOverrideExt [Serializable] [Flags] -internal enum ExtraGlyphRanges +public enum ExtraGlyphRanges { ChineseFull = 1 << 0, ChineseSimplifiedCommon = 1 << 1, @@ -619,9 +638,9 @@ internal enum ExtraGlyphRanges Vietnamese = 1 << 6, } -internal static class ExtraGlyphRangesExt +public static class ExtraGlyphRangesExt { - internal static string Name(this ExtraGlyphRanges ranges) => ranges switch + public static string Name(this ExtraGlyphRanges ranges) => ranges switch { ExtraGlyphRanges.ChineseFull => Language.ExtraGlyphRanges_ChineseFull_Name, ExtraGlyphRanges.ChineseSimplifiedCommon => Language.ExtraGlyphRanges_ChineseSimplifiedCommon_Name, @@ -633,7 +652,7 @@ internal static class ExtraGlyphRangesExt _ => throw new ArgumentOutOfRangeException(nameof(ranges), ranges, null), }; - internal static unsafe nint Range(this ExtraGlyphRanges ranges) => ranges switch + public static unsafe nint Range(this ExtraGlyphRanges ranges) => ranges switch { ExtraGlyphRanges.ChineseFull => (nint)ImGui.GetIO().Fonts.GetGlyphRangesChineseFull(), ExtraGlyphRanges.ChineseSimplifiedCommon => (nint)ImGui.GetIO().Fonts.GetGlyphRangesChineseSimplifiedCommon(), diff --git a/ChatTwo/EmoteCache.cs b/ChatTwo/EmoteCache.cs index 77913a7..05cbe95 100644 --- a/ChatTwo/EmoteCache.cs +++ b/ChatTwo/EmoteCache.cs @@ -28,24 +28,28 @@ public static class EmoteCache private const string Top100Emotes = "{0}/emotes/shared/top?before={1}&limit=100"; private const string EmotePath = "https://cdn.betterttv.net/emote/{0}/3x"; - private struct Top100 + [Serializable] + private struct Top100() { [JsonPropertyName("emote")] - public Emote Emote { get; set; } + public Emote Emote = default; [JsonPropertyName("id")] - public string Id { get; set; } + public string Id = string.Empty; } - public struct Emote + [Serializable] + public struct Emote() { [JsonPropertyName("id")] - public string Id { get; set; } + public string Id = string.Empty; + [JsonPropertyName("code")] - public string Code { get; set; } + public string Code = string.Empty; + [JsonPropertyName("imageType")] - public string ImageType { get; set; } - }; + public string ImageType = string.Empty; + } public enum LoadingState { diff --git a/ChatTwo/FontManager.cs b/ChatTwo/FontManager.cs index 6aa3f45..4bd4742 100644 --- a/ChatTwo/FontManager.cs +++ b/ChatTwo/FontManager.cs @@ -8,18 +8,18 @@ namespace ChatTwo; public class FontManager { - internal IFontHandle Axis { get; private set; } - internal IFontHandle AxisItalic { get; private set; } + internal IFontHandle Axis = null!; + internal IFontHandle AxisItalic = null!; - internal IFontHandle RegularFont { get; private set; } - internal IFontHandle? ItalicFont { get; private set; } + internal IFontHandle RegularFont = null!; + internal IFontHandle? ItalicFont; - internal IFontHandle FontAwesome { get; private set; } + internal IFontHandle FontAwesome = null!; internal readonly byte[] GameSymFont; - private ushort[] Ranges; - private ushort[] JpRange; + private ushort[] Ranges = []; + private ushort[] JpRange = []; public static readonly HashSet AxisFontSizeList = [ @@ -69,10 +69,15 @@ public class FontManager } } + // Ingame supported ranges + var reader = new FdtReader(Plugin.DataManager.GetFile("common/font/axis_12.fdt")!.Data); + foreach (var c in reader.Glyphs) + builder.AddChar(c.Char); + // various symbols // French // Romanian - builder.AddText("←→↑↓《》■※☀★★☆♥♡ヅツッシ☀☁☂℃℉°♀♂♠♣♦♣♧®©™€$£♯♭♪✓√◎◆◇♦■□〇●△▽▼▲‹›≤≥<«“”─\~"); + // builder.AddText("←→↑↓《》■※☀★★☆♥♡ヅツッシ☀☁☂℃℉°♀♂♠♣♦♣♧®©™€$£♯♭♪✓√◎◆◇♦■□〇●△▽▼▲‹›≤≥<«“”─\~"); builder.AddText("Œœ"); builder.AddText("ĂăÂâÎîȘșȚț"); diff --git a/ChatTwo/GameFunctions/Chat.cs b/ChatTwo/GameFunctions/Chat.cs index b5acae5..0e890f1 100755 --- a/ChatTwo/GameFunctions/Chat.cs +++ b/ChatTwo/GameFunctions/Chat.cs @@ -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? ChatLogRefreshHook { get; init; } + private Hook? 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? ContextMenuTellInForayHook { get; set; } + private Hook? ContextMenuTellInForayHook = null!; private delegate void ContextMenuTellInForayDelegate(RaptureShellModule* module, Utf8String* playerName, Utf8String* worldName, ushort worldId, ulong accountId, ulong contentId, ushort reason); - private Hook ChangeChannelNameHook { get; init; } - private Hook? ReplyInSelectedChatModeHook { get; init; } - private Hook? SetChatLogTellTargetHook { get; init; } + private readonly Hook? ChangeChannelNameHook; + private readonly Hook? ReplyInSelectedChatModeHook; + private readonly Hook? 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); } } diff --git a/ChatTwo/GameFunctions/ChatBox.cs b/ChatTwo/GameFunctions/ChatBox.cs index 5a4248e..3cbc6e7 100644 --- a/ChatTwo/GameFunctions/ChatBox.cs +++ b/ChatTwo/GameFunctions/ChatBox.cs @@ -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); } diff --git a/ChatTwo/GameFunctions/GameFunctions.cs b/ChatTwo/GameFunctions/GameFunctions.cs index fdf9ee2..ad7bff7 100755 --- a/ChatTwo/GameFunctions/GameFunctions.cs +++ b/ChatTwo/GameFunctions/GameFunctions.cs @@ -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? ResolveTextCommandPlaceholderHook { get; init; } + private Hook? 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); } diff --git a/ChatTwo/GameFunctions/Types/ModifierFlag.cs b/ChatTwo/GameFunctions/Types/ModifierFlag.cs index 395055e..3a8c459 100755 --- a/ChatTwo/GameFunctions/Types/ModifierFlag.cs +++ b/ChatTwo/GameFunctions/Types/ModifierFlag.cs @@ -1,7 +1,7 @@ namespace ChatTwo.GameFunctions.Types; [Flags] -internal enum ModifierFlag +public enum ModifierFlag { None = 0, Shift = 1 << 0, diff --git a/ChatTwo/GameFunctions/Types/TellReason.cs b/ChatTwo/GameFunctions/Types/TellReason.cs index 1a2906d..f779633 100755 --- a/ChatTwo/GameFunctions/Types/TellReason.cs +++ b/ChatTwo/GameFunctions/Types/TellReason.cs @@ -1,6 +1,6 @@ namespace ChatTwo.GameFunctions.Types; -internal enum TellReason +public enum TellReason { Direct = 0, PartyFinder = 1, diff --git a/ChatTwo/GameFunctions/Types/TellTarget.cs b/ChatTwo/GameFunctions/Types/TellTarget.cs index 894bf05..03be83d 100755 --- a/ChatTwo/GameFunctions/Types/TellTarget.cs +++ b/ChatTwo/GameFunctions/Types/TellTarget.cs @@ -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); } diff --git a/ChatTwo/Http/MessageProtocol/DataStructure.cs b/ChatTwo/Http/MessageProtocol/DataStructure.cs index 2856fdf..a92bae2 100644 --- a/ChatTwo/Http/MessageProtocol/DataStructure.cs +++ b/ChatTwo/Http/MessageProtocol/DataStructure.cs @@ -63,7 +63,7 @@ public struct MessageResponse() { [JsonProperty("id")] public Guid Id = Guid.Empty; [JsonProperty("timestamp")] public string Timestamp = ""; - [JsonProperty("templates")] public MessageTemplate[] Templates; + [JsonProperty("templates")] public MessageTemplate[] Templates = []; } /// diff --git a/ChatTwo/Http/Processing.cs b/ChatTwo/Http/Processing.cs index 9e0e706..6925b45 100644 --- a/ChatTwo/Http/Processing.cs +++ b/ChatTwo/Http/Processing.cs @@ -69,7 +69,7 @@ public class Processing color ??= 0; - var userContent = text.Content ?? string.Empty; + var userContent = text.Content; if (HostContext.Core.Plugin.ChatLogWindow.ScreenshotMode) { if (chunk.Link is PlayerPayload playerPayload) diff --git a/ChatTwo/Ipc/ExtraChat.cs b/ChatTwo/Ipc/ExtraChat.cs index b594bcc..70061da 100644 --- a/ChatTwo/Ipc/ExtraChat.cs +++ b/ChatTwo/Ipc/ExtraChat.cs @@ -2,8 +2,9 @@ using Dalamud.Plugin.Ipc; namespace ChatTwo.Ipc; -internal sealed class ExtraChat : IDisposable +public sealed class ExtraChat : IDisposable { +#pragma warning disable CS0649 // Assigned through IPC [Serializable] private struct OverrideInfo { @@ -11,8 +12,7 @@ internal sealed class ExtraChat : IDisposable public ushort UiColour; public uint Rgba; } - - private Plugin Plugin { get; } +#pragma warning restore CS0649 private ICallGateSubscriber OverrideChannelGate { get; } private ICallGateSubscriber, Dictionary> ChannelCommandColoursGate { get; } @@ -26,10 +26,8 @@ internal sealed class ExtraChat : IDisposable private Dictionary ChannelNamesInternal { get; set; } = new(); internal IReadOnlyDictionary ChannelNames => ChannelNamesInternal; - internal ExtraChat(Plugin plugin) + internal ExtraChat() { - Plugin = plugin; - OverrideChannelGate = Plugin.Interface.GetIpcSubscriber("ExtraChat.OverrideChannelColour"); ChannelCommandColoursGate = Plugin.Interface.GetIpcSubscriber, Dictionary>("ExtraChat.ChannelCommandColours"); ChannelNamesGate = Plugin.Interface.GetIpcSubscriber, Dictionary>("ExtraChat.ChannelNames"); diff --git a/ChatTwo/Ipc/TypingIpc.cs b/ChatTwo/Ipc/TypingIpc.cs index 4cd3a3c..a367da6 100644 --- a/ChatTwo/Ipc/TypingIpc.cs +++ b/ChatTwo/Ipc/TypingIpc.cs @@ -34,7 +34,7 @@ internal sealed class TypingIpc : IDisposable var channelType = inputChannel.ToChatType(); return (InputVisible: !log.IsHidden, - InputFocused: log.InputFocused, + log.InputFocused, HasText: log.Chat.Length > 0, IsTyping: log is { InputFocused: true, Chat.Length: > 0 }, TextLength: log.Chat.Length, diff --git a/ChatTwo/Message.cs b/ChatTwo/Message.cs index 7a53b81..d52eb5b 100755 --- a/ChatTwo/Message.cs +++ b/ChatTwo/Message.cs @@ -10,74 +10,30 @@ using FFXIVClientStructs.FFXIV.Client.UI.Agent; namespace ChatTwo; -internal class SortCode +public partial class Message { - internal ChatType Type { get; } - internal ChatSource Source { get; } + public Guid Id { get; } = Guid.NewGuid(); + public ulong Receiver { get; } + public ulong ContentId { get; set; } + public ulong AccountId { get; set; } // 0 if not set - public SortCode(ChatType type, ChatSource source) - { - Type = type; - Source = source; - } + public DateTimeOffset Date { get; } + public ChatCode Code { get; } + public List Sender { get; } + public List Content { get; private set; } - internal SortCode(uint raw) - { - Type = (ChatType)(raw >> 16); - Source = (ChatSource)(raw & 0xFFFF); - } + public SeString SenderSource { get; } + public SeString ContentSource { get; } - internal uint Encode() - { - return ((uint) Type << 16) | (uint) Source; - } - - private bool Equals(SortCode other) - { - return Type == other.Type && Source == other.Source; - } - - public override bool Equals(object? obj) - { - if (ReferenceEquals(null, obj)) - return false; - - if (ReferenceEquals(this, obj)) - return true; - - return obj.GetType() == GetType() && Equals((SortCode) obj); - } - - public override int GetHashCode() - { - unchecked { return ((int) Type * 397) ^ (int) Source; } - } -} - -internal partial class Message -{ - internal Guid Id { get; } = Guid.NewGuid(); - internal ulong Receiver { get; } - internal ulong ContentId { get; set; } - internal ulong AccountId { get; set; } // 0 if not set - - internal DateTimeOffset Date { get; } - internal ChatCode Code { get; } - internal List Sender { get; } - internal List Content { get; private set; } - - internal SeString SenderSource { get; } - internal SeString ContentSource { get; } - - internal SortCode SortCode { get; } - internal Guid ExtraChatChannel { get; } + public int SortCodeV2 { get; } + public Guid ExtraChatChannel { get; } // Not stored in the database: - internal int Hash { get; } - internal Dictionary Height { get; } = new(); - internal Dictionary IsVisible { get; } = new(); + public int Hash { get; } + public Dictionary Height { get; } = new(); + public Dictionary IsVisible { get; } = new(); - internal Message(ulong receiver, ulong contentId, ulong accountId, ChatCode code, List sender, List content, SeString senderSource, SeString contentSource) + public Message(ulong receiver, ulong contentId, ulong accountId, ChatCode code, List sender, List content, SeString senderSource, SeString contentSource) { var extraChatChannel = ExtractExtraChatChannel(contentSource); Receiver = receiver; @@ -89,7 +45,7 @@ internal partial class Message Content = CheckMessageContent(content, extraChatChannel); SenderSource = senderSource; ContentSource = contentSource; - SortCode = new SortCode(Code.Type, Code.Source); + SortCodeV2 = Code.ToSortCodeV2(); ExtraChatChannel = extraChatChannel; Hash = GenerateHash(); @@ -97,7 +53,7 @@ internal partial class Message chunk.Message = this; } - internal Message(Guid id, ulong receiver, ulong contentId, DateTimeOffset date, ChatCode code, List sender, List content, SeString senderSource, SeString contentSource, SortCode sortCode, Guid extraChatChannel) + public Message(Guid id, ulong receiver, ulong contentId, DateTimeOffset date, ChatCode code, List sender, List content, SeString senderSource, SeString contentSource, Guid extraChatChannel) { Id = id; Receiver = receiver; @@ -110,7 +66,7 @@ internal partial class Message Content = content; SenderSource = senderSource; ContentSource = contentSource; - SortCode = sortCode; + SortCodeV2 = code.ToSortCodeV2(); ExtraChatChannel = extraChatChannel; Hash = GenerateHash(); @@ -118,25 +74,26 @@ internal partial class Message chunk.Message = this; } - internal static Message FakeMessage(List content, ChatCode code) + public static Message FakeMessage(List content, ChatCode code) { return new Message(0, 0, 0, code, [], content, new SeString(), new SeString()); } - internal bool Matches(Dictionary channels, bool allExtraChatChannels, HashSet extraChatChannels) + public bool Matches(Dictionary channels, bool allExtraChatChannels, HashSet extraChatChannels) { if (ExtraChatChannel != Guid.Empty) return allExtraChatChannels || extraChatChannels.Contains(ExtraChatChannel); + var source = (ChatSource)(1 << (int)Code.Source); + var target = (ChatSource)(1 << (int)Code.Target); return Code.Type.IsGm() || channels.TryGetValue(Code.Type, out var sources) - && (Code.Source is 0 or (ChatSource) 1 - || sources.HasFlag(Code.Source)); + && (Code.Source is 0 || sources.Source.HasFlag(source) || sources.Target.HasFlag(target)); } private int GenerateHash() { - var hash = SortCode.GetHashCode() + var hash = SortCodeV2.GetHashCode() ^ ExtraChatChannel.GetHashCode() ^ string.Join("", Sender.Select(c => c.StringValue())).GetHashCode() ^ string.Join("", Content.Select(c => c.StringValue())).GetHashCode(); diff --git a/ChatTwo/MessageManager.cs b/ChatTwo/MessageManager.cs index c1300ea..9be0440 100644 --- a/ChatTwo/MessageManager.cs +++ b/ChatTwo/MessageManager.cs @@ -4,12 +4,12 @@ using System.Text; using ChatTwo.Code; using ChatTwo.Resources; using ChatTwo.Util; +using Dalamud.Game.Chat; using Dalamud.Game.Text; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Hooking; using Dalamud.Interface.ImGuiNotification; using Dalamud.Plugin.Services; -using Dalamud.Utility; using FFXIVClientStructs.FFXIV.Client.UI.Misc; using Lumina.Text.Expressions; using Lumina.Text.Payloads; @@ -191,19 +191,19 @@ internal class MessageManager : IAsyncDisposable } public (SeString? Sender, SeString? Message) LastMessage = (null, null); - private void ChatMessage(XivChatType type, int timestamp, SeString sender, SeString message) + private void ChatMessage(IChatMessage message) { - LastMessage = (sender, message); + LastMessage = (message.Sender, message.Message); var pendingMessage = new PendingMessage { - ReceiverId = CurrentContentId, ContentId = 0, AccountId = 0, - Type = type, - Timestamp = timestamp, - Sender = sender, - Content = message, + LogKind = message.LogKind, + SourceKind = message.SourceKind, + TargetKind = message.TargetKind, + Sender = message.Sender, + Content = message.Message, }; // Update colour codes. @@ -238,7 +238,7 @@ internal class MessageManager : IAsyncDisposable private void ProcessMessage(PendingMessage pendingMessage) { - var chatCode = new ChatCode((ushort)pendingMessage.Type); + var chatCode = new ChatCode(pendingMessage.LogKind, pendingMessage.SourceKind, pendingMessage.TargetKind); NameFormatting? formatting = null; if (pendingMessage.Sender.Payloads.Count > 0) @@ -247,15 +247,9 @@ internal class MessageManager : IAsyncDisposable var senderChunks = new List(); if (formatting is { IsPresent: true }) { - senderChunks.Add(new TextChunk(ChunkSource.None, null, formatting.Before) - { - FallbackColour = chatCode.Type, - }); + senderChunks.Add(new TextChunk(ChunkSource.None, null, formatting.Before) { FallbackColour = chatCode.Type }); senderChunks.AddRange(ChunkUtil.ToChunks(pendingMessage.Sender, ChunkSource.Sender, chatCode.Type)); - senderChunks.Add(new TextChunk(ChunkSource.None, null, formatting.After) - { - FallbackColour = chatCode.Type, - }); + senderChunks.Add(new TextChunk(ChunkSource.None, null, formatting.After) { FallbackColour = chatCode.Type }); } var contentChunks = ChunkUtil.ToChunks(pendingMessage.Content, ChunkSource.Content, chatCode.Type).ToList(); @@ -342,12 +336,12 @@ internal class MessageManager : IAsyncDisposable private class PendingMessage { - internal ulong ReceiverId { get; set; } - internal ulong ContentId { get; set; } // 0 if unknown - internal ulong AccountId { get; set; } // 0 if unknown - internal XivChatType Type { get; set; } - internal int Timestamp { get; set; } - internal SeString Sender { get; set; } - internal SeString Content { get; set; } + public ulong ContentId; // 0 if unknown + public ulong AccountId; // 0 if unknown + public XivChatType LogKind; + public XivChatRelationKind SourceKind; + public XivChatRelationKind TargetKind; + public required SeString Sender; + public required SeString Content; } } diff --git a/ChatTwo/MessageStore.cs b/ChatTwo/MessageStore.cs index a23d77f..2f37a61 100644 --- a/ChatTwo/MessageStore.cs +++ b/ChatTwo/MessageStore.cs @@ -98,11 +98,11 @@ public class PayloadMessagePackFormatter : IMessagePackFormatter } } -public class SeStringMessagePackFormatter : IMessagePackFormatter +public class SeStringMessagePackFormatter : IMessagePackFormatter { - public void Serialize(ref MessagePackWriter writer, SeString value, MessagePackSerializerOptions options) + public void Serialize(ref MessagePackWriter writer, SeString? value, MessagePackSerializerOptions options) { - options.Resolver.GetFormatter>()!.Serialize(ref writer, value.Payloads, options); + options.Resolver.GetFormatter>()!.Serialize(ref writer, value?.Payloads ?? [], options); } public SeString Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) @@ -176,6 +176,9 @@ internal class MessageStore : IDisposable case 1: migrationsToDo.Add(Migrate2); break; + case 2: + migrationsToDo.Add(Migrate3); + break; } foreach (var migration in migrationsToDo) @@ -227,6 +230,38 @@ internal class MessageStore : IDisposable SetMigrationVersion(2); } + private void Migrate3() + { + Connection.Execute(@" + -- Migration 3: Fix log kinds to fit the new format + -- Add new ChatType, SourceKind, TargetKind (byte), SortCodeV2 + -- Migrate OldChatColumn + -- ChatType = OldChatColumn & 0x7f + -- SourceKind = log2(1 << ((OldChatColumn >> 11) & 0xF)) + -- TargetKind = trunc(log2(1 << ((OldChatColumn >> 7) & 0xF))) + -- Virtual SortCodeV2 = ChatType << 16 | SourceKind << 8 | TargetKind + -- Delete OldChatColumn, Virtual Channel + + ALTER TABLE messages ADD COLUMN ChatType INTEGER; + CREATE INDEX IF NOT EXISTS idx_messages_chat_type ON messages (ChatType); + ALTER TABLE messages ADD COLUMN SourceKind INTEGER; + ALTER TABLE messages ADD COLUMN TargetKind INTEGER; + + UPDATE messages SET + ChatType = Code & 0x7f, + SourceKind = trunc(log2(1 << ((Code >> 11) & 0xF))), + TargetKind = trunc(log2(1 << ((Code >> 7) & 0xF))) + WHERE true; + + DROP INDEX idx_messages_channel; + ALTER TABLE messages DROP COLUMN Channel; + ALTER TABLE messages DROP COLUMN Code; + ALTER TABLE messages DROP COLUMN SortCode; + "); + + SetMigrationVersion(3); + } + private void SetMigrationVersion(int version) { using var cmd = Connection.CreateCommand(); @@ -271,12 +306,13 @@ internal class MessageStore : IDisposable Receiver, ContentId, Date, - Code, + ChatType, + SourceKind, + TargetKind, Sender, Content, SenderSource, ContentSource, - SortCode, ExtraChatChannel, Deleted ) VALUES ( @@ -284,12 +320,13 @@ internal class MessageStore : IDisposable $Receiver, $ContentId, $Date, - $Code, + $ChatType, + $SourceKind, + $TargetKind, $Sender, $Content, $SenderSource, $ContentSource, - $SortCode, $ExtraChatChannel, false ) @@ -297,27 +334,28 @@ internal class MessageStore : IDisposable Receiver = excluded.Receiver, ContentId = excluded.ContentId, Date = excluded.Date, - Code = excluded.Code, + ChatType = excluded.ChatType, + SourceKind = excluded.SourceKind, + TargetKind = excluded.TargetKind, Sender = excluded.Sender, Content = excluded.Content, SenderSource = excluded.SenderSource, ContentSource = excluded.ContentSource, - SortCode = excluded.SortCode, ExtraChatChannel = excluded.ExtraChatChannel, - Deleted = false - ; + Deleted = false; "; cmd.Parameters.AddWithValue("$Id", message.Id); cmd.Parameters.AddWithValue("$Receiver", message.Receiver); cmd.Parameters.AddWithValue("$ContentId", message.ContentId); cmd.Parameters.AddWithValue("$Date", message.Date.ToUnixTimeMilliseconds()); - cmd.Parameters.AddWithValue("$Code", message.Code.Raw); + cmd.Parameters.AddWithValue("$ChatType", message.Code.Type); + cmd.Parameters.AddWithValue("$SourceKind", message.Code.Source); + cmd.Parameters.AddWithValue("$TargetKind", message.Code.Target); cmd.Parameters.AddWithValue("$Sender", MessagePackSerializer.Serialize(message.Sender, MsgPackOptions)); cmd.Parameters.AddWithValue("$Content", MessagePackSerializer.Serialize(message.Content, MsgPackOptions)); cmd.Parameters.AddWithValue("$SenderSource", MessagePackSerializer.Serialize(message.SenderSource, MsgPackOptions)); cmd.Parameters.AddWithValue("$ContentSource", MessagePackSerializer.Serialize(message.ContentSource, MsgPackOptions)); - cmd.Parameters.AddWithValue("$SortCode", message.SortCode.Encode()); cmd.Parameters.AddWithValue("$ExtraChatChannel", message.ExtraChatChannel); cmd.ExecuteNonQuery(); @@ -350,12 +388,13 @@ internal class MessageStore : IDisposable Receiver, ContentId, Date, - Code, + ChatType, + SourceKind, + TargetKind, Sender, Content, SenderSource, ContentSource, - SortCode, ExtraChatChannel FROM messages " + whereClause + @" @@ -387,14 +426,14 @@ internal class MessageStore : IDisposable cmd.ExecuteNonQuery(); } - internal long CountDateRange(DateTime after, DateTime before, IEnumerable channels, ulong? receiver = null) + internal long CountDateRange(DateTime after, DateTime before, IEnumerable channels, ulong? receiver = null) { List whereClauses = ["deleted = false"]; if (receiver != null) whereClauses.Add("Receiver = $Receiver"); whereClauses.Add("Date BETWEEN $After AND $Before"); - whereClauses.Add($"Channel IN ({string.Join(", ", channels)})"); + whereClauses.Add($"ChatType IN ({string.Join(", ", channels)})"); var whereClause = "WHERE " + string.Join(" AND ", whereClauses); @@ -416,14 +455,14 @@ internal class MessageStore : IDisposable return (long) cmd.ExecuteScalar()!; } - internal MessageEnumerator GetDateRange(DateTime after, DateTime before, IEnumerable channels, ulong? receiver = null) + internal MessageEnumerator GetDateRange(DateTime after, DateTime before, IEnumerable channels, ulong? receiver = null) { List whereClauses = ["deleted = false"]; if (receiver != null) whereClauses.Add("Receiver = $Receiver"); whereClauses.Add("Date BETWEEN $After AND $Before"); - whereClauses.Add($"Channel IN ({string.Join(", ", channels)})"); + whereClauses.Add($"ChatType IN ({string.Join(", ", channels)})"); var whereClause = $"WHERE {string.Join(" AND ", whereClauses)}"; @@ -436,12 +475,13 @@ internal class MessageStore : IDisposable Receiver, ContentId, Date, - Code, + ChatType, + SourceKind, + TargetKind, Sender, Content, SenderSource, ContentSource, - SortCode, ExtraChatChannel FROM messages " + whereClause; @@ -456,14 +496,14 @@ internal class MessageStore : IDisposable return new MessageEnumerator(cmd.ExecuteReader()); } - internal MessageEnumerator GetPagedDateRange(DateTime after, DateTime before, IEnumerable channels, ulong? receiver = null, int page = 0) + internal MessageEnumerator GetPagedDateRange(DateTime after, DateTime before, IEnumerable channels, ulong? receiver = null, int page = 0) { List whereClauses = ["deleted = false"]; if (receiver != null) whereClauses.Add("Receiver = $Receiver"); whereClauses.Add("Date BETWEEN $After AND $Before"); - whereClauses.Add($"Channel IN ({string.Join(", ", channels)})"); + whereClauses.Add($"ChatType IN ({string.Join(", ", channels)})"); var whereClause = $"WHERE {string.Join(" AND ", whereClauses)}"; @@ -476,12 +516,13 @@ internal class MessageStore : IDisposable Receiver, ContentId, Date, - Code, + ChatType, + SourceKind, + TargetKind, Sender, Content, SenderSource, ContentSource, - SortCode, ExtraChatChannel FROM messages " + whereClause + @" @@ -525,13 +566,12 @@ internal class MessageEnumerator(DbDataReader reader) : IEnumerable, ID (ulong)reader.GetInt64(1), (ulong)reader.GetInt64(2), DateTimeOffset.FromUnixTimeMilliseconds(reader.GetInt64(3)), - new ChatCode((ushort)reader.GetInt32(4)), - MessagePackSerializer.Deserialize>(reader.GetFieldValue(5), MessageStore.MsgPackOptions), - MessagePackSerializer.Deserialize>(reader.GetFieldValue(6), MessageStore.MsgPackOptions), - MessagePackSerializer.Deserialize(reader.GetFieldValue(7), MessageStore.MsgPackOptions), - MessagePackSerializer.Deserialize(reader.GetFieldValue(8), MessageStore.MsgPackOptions), - new SortCode((uint)reader.GetInt32(9)), - reader.GetGuid(10) + new ChatCode((byte)reader.GetInt32(4), (byte)reader.GetInt32(5), (byte)reader.GetInt32(6)), + MessagePackSerializer.Deserialize>(reader.GetFieldValue(7), MessageStore.MsgPackOptions), + MessagePackSerializer.Deserialize>(reader.GetFieldValue(8), MessageStore.MsgPackOptions), + MessagePackSerializer.Deserialize(reader.GetFieldValue(9), MessageStore.MsgPackOptions), + MessagePackSerializer.Deserialize(reader.GetFieldValue(10), MessageStore.MsgPackOptions), + reader.GetGuid(11) ); } catch (Exception e) diff --git a/ChatTwo/PayloadHandler.cs b/ChatTwo/PayloadHandler.cs index 41b1df2..899e79e 100755 --- a/ChatTwo/PayloadHandler.cs +++ b/ChatTwo/PayloadHandler.cs @@ -135,7 +135,7 @@ public sealed class PayloadHandler private void ContextFooter(bool didCustomContext, Chunk chunk) { - ImRaii.IEndObject? menu = null; + ImRaii.MenuDisposable menu = default; if (didCustomContext) { ImGui.Separator(); @@ -177,7 +177,7 @@ public sealed class PayloadHandler ImGui.TextUnformatted(message.Code.Type.Name()); } - menu?.Dispose(); + menu.Dispose(); } private static string StringifyMessage(Message? message, bool withSender = false) @@ -192,7 +192,7 @@ public sealed class PayloadHandler .Aggregate(string.Concat); } - internal void Click(Chunk chunk, Payload? payload, ImGuiMouseButton button) + internal unsafe void Click(Chunk chunk, Payload? payload, ImGuiMouseButton button) { if (Plugin.Config.PlaySounds) UIGlobals.PlaySoundEffect(PopupSfx); @@ -608,7 +608,7 @@ public sealed class PayloadHandler if (world.Value.IsPublic) { var party = Plugin.PartyList; - var leader = (ulong?) party[(int) party.PartyLeaderIndex]?.ContentId; + var leader = party[(int) party.PartyLeaderIndex]?.ContentId; var isLeader = party.Length == 0 || Plugin.PlayerState.ContentId == leader; var member = party.FirstOrDefault(member => member.Name.TextValue == player.PlayerName && member.World.RowId == world.RowId); var isInParty = member != null; @@ -640,10 +640,10 @@ public sealed class PayloadHandler if (isInParty && member != null && (!inInstance || (inInstance && inPartyInstance))) { if (ImGui.Selectable(Language.Context_Promote)) - GameFunctions.Party.Promote(player.PlayerName, (ulong) member.ContentId); + GameFunctions.Party.Promote(player.PlayerName, member.ContentId); if (ImGui.Selectable(Language.Context_KickFromParty)) - GameFunctions.Party.Kick(player.PlayerName, (ulong) member.ContentId); + GameFunctions.Party.Kick(player.PlayerName, member.ContentId); } } @@ -743,7 +743,7 @@ public sealed class PayloadHandler default: builder.AddUiForeground(nameValue, 1); break; - }; + } LogWindow.DrawChunks(ChunkUtil.ToChunks(builder.BuiltString, ChunkSource.None, null).ToList(), false); ImGui.Separator(); diff --git a/ChatTwo/Plugin.cs b/ChatTwo/Plugin.cs index 991f1bd..eaea12c 100755 --- a/ChatTwo/Plugin.cs +++ b/ChatTwo/Plugin.cs @@ -19,30 +19,30 @@ namespace ChatTwo; // ReSharper disable once ClassNeverInstantiated.Global public sealed class Plugin : IDalamudPlugin { - internal const string PluginName = "Chat 2"; + public const string PluginName = "Chat 2"; - [PluginService] internal static IPluginLog Log { get; private set; } = null!; - [PluginService] internal static IDalamudPluginInterface Interface { get; private set; } = null!; - [PluginService] internal static IChatGui ChatGui { get; private set; } = null!; - [PluginService] internal static IClientState ClientState { get; private set; } = null!; - [PluginService] internal static ICommandManager CommandManager { get; private set; } = null!; - [PluginService] internal static ICondition Condition { get; private set; } = null!; - [PluginService] internal static IDataManager DataManager { get; private set; } = null!; - [PluginService] internal static IFramework Framework { get; private set; } = null!; - [PluginService] internal static IGameGui GameGui { get; private set; } = null!; - [PluginService] internal static IKeyState KeyState { get; private set; } = null!; - [PluginService] internal static IObjectTable ObjectTable { get; private set; } = null!; - [PluginService] internal static IPartyList PartyList { get; private set; } = null!; - [PluginService] internal static ITargetManager TargetManager { get; private set; } = null!; - [PluginService] internal static ITextureProvider TextureProvider { get; private set; } = null!; - [PluginService] internal static IGameInteropProvider GameInteropProvider { get; private set; } = null!; - [PluginService] internal static IGameConfig GameConfig { get; private set; } = null!; - [PluginService] internal static INotificationManager Notification { get; private set; } = null!; - [PluginService] internal static IAddonLifecycle AddonLifecycle { get; private set; } = null!; - [PluginService] internal static IPlayerState PlayerState { get; private set; } = null!; - [PluginService] internal static ISeStringEvaluator Evaluator { get; private set; } = null!; + [PluginService] public static IPluginLog Log { get; private set; } = null!; + [PluginService] public static IDalamudPluginInterface Interface { get; private set; } = null!; + [PluginService] public static IChatGui ChatGui { get; private set; } = null!; + [PluginService] public static IClientState ClientState { get; private set; } = null!; + [PluginService] public static ICommandManager CommandManager { get; private set; } = null!; + [PluginService] public static ICondition Condition { get; private set; } = null!; + [PluginService] public static IDataManager DataManager { get; private set; } = null!; + [PluginService] public static IFramework Framework { get; private set; } = null!; + [PluginService] public static IGameGui GameGui { get; private set; } = null!; + [PluginService] public static IKeyState KeyState { get; private set; } = null!; + [PluginService] public static IObjectTable ObjectTable { get; private set; } = null!; + [PluginService] public static IPartyList PartyList { get; private set; } = null!; + [PluginService] public static ITargetManager TargetManager { get; private set; } = null!; + [PluginService] public static ITextureProvider TextureProvider { get; private set; } = null!; + [PluginService] public static IGameInteropProvider GameInteropProvider { get; private set; } = null!; + [PluginService] public static IGameConfig GameConfig { get; private set; } = null!; + [PluginService] public static INotificationManager Notification { get; private set; } = null!; + [PluginService] public static IAddonLifecycle AddonLifecycle { get; private set; } = null!; + [PluginService] public static IPlayerState PlayerState { get; private set; } = null!; + [PluginService] public static ISeStringEvaluator Evaluator { get; private set; } = null!; - internal static Configuration Config = null!; + public static Configuration Config = null!; public static FileDialogManager FileDialogManager { get; private set; } = null!; public readonly WindowSystem WindowSystem = new(PluginName); @@ -62,7 +62,7 @@ public sealed class Plugin : IDalamudPlugin internal TypingIpc TypingIpc { get; } internal FontManager FontManager { get; } - internal ServerCore ServerCore { get; } + public readonly ServerCore ServerCore; internal int DeferredSaveFrames = -1; @@ -88,23 +88,47 @@ public sealed class Plugin : IDalamudPlugin Config = Interface.GetPluginConfig() as Configuration ?? new Configuration(); +#pragma warning disable CS0618 // Type or member is obsolete + // TODO Remove after 01.07.2026 + // Migrate old channel values + if (Config.Version <= 5) + { + foreach (var tab in Config.Tabs) + { + if (tab.ChatCodes.Count > 0) + { + tab.SelectedChannels = tab.ChatCodes.ToDictionary(pair => pair.Key, pair => (pair.Value, pair.Value)); + tab.ChatCodes.Clear(); + } + + if (Config.InactivityHideChannels.Count > 0) + { + Config.InactivityHideChannelsV2 = Config.InactivityHideChannels.ToDictionary(pair => pair.Key, pair => (pair.Value, pair.Value)); + Config.InactivityHideChannels.Clear(); + } + + Config.Version = 6; + SaveConfig(); + } + } +#pragma warning restore CS0618 // Type or member is obsolete + if (Config.Tabs.Count == 0) Config.Tabs.Add(TabsUtil.VanillaGeneral); - Config.InactivityHideChannels ??= TabsUtil.AllChannels(); LanguageChanged(Interface.UiLanguage); ImGuiUtil.Initialize(this); FileDialogManager = new FileDialogManager(); - // Functions calls this in its ctor if the player is already logged in + // Function call this in its ctor if the player is already logged in ServerCore = new ServerCore(this); Commands = new Commands(); Functions = new GameFunctions.GameFunctions(this); Ipc = new IpcManager(); TypingIpc = new TypingIpc(this); - ExtraChat = new ExtraChat(this); + ExtraChat = new ExtraChat(); FontManager = new FontManager(); ChatLogWindow = new ChatLogWindow(this); diff --git a/ChatTwo/Resources/Language.Designer.cs b/ChatTwo/Resources/Language.Designer.cs index cebe6dc..82702f7 100755 --- a/ChatTwo/Resources/Language.Designer.cs +++ b/ChatTwo/Resources/Language.Designer.cs @@ -86,6 +86,33 @@ namespace ChatTwo.Resources { } } + /// + /// Looks up a localized string similar to message is empty. + /// + internal static string ChatBox_Error_Empty { + get { + return ResourceManager.GetString("ChatBox_Error_Empty", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to message contained invalid characters. + /// + internal static string ChatBox_Error_Invalid { + get { + return ResourceManager.GetString("ChatBox_Error_Invalid", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to message is longer than 500 bytes. + /// + internal static string ChatBox_Error_Too_Long { + get { + return ResourceManager.GetString("ChatBox_Error_Too_Long", resourceCulture); + } + } + /// /// Looks up a localized string similar to Input is disabled for this tab. /// @@ -1751,6 +1778,24 @@ namespace ChatTwo.Resources { } } + /// + /// Looks up a localized string similar to Source. + /// + internal static string ImGuiUtil_ChannelSelector_Source { + get { + return ResourceManager.GetString("ImGuiUtil_ChannelSelector_Source", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Target. + /// + internal static string ImGuiUtil_ChannelSelector_Target { + get { + return ResourceManager.GetString("ImGuiUtil_ChannelSelector_Target", resourceCulture); + } + } + /// /// Looks up a localized string similar to ESC to clear. /// @@ -2444,6 +2489,24 @@ namespace ChatTwo.Resources { } } + /// + /// Looks up a localized string similar to Target. + /// + internal static string Options_Header_Target { + get { + return ResourceManager.GetString("Options_Header_Target", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This disables the channel selection and shows all messages coming from the target.. + /// + internal static string Options_Help_SenderMessages { + get { + return ResourceManager.GetString("Options_Help_SenderMessages", resourceCulture); + } + } + /// /// Looks up a localized string similar to Hide the in-game chat window when the plugin is active.. /// @@ -3398,6 +3461,15 @@ namespace ChatTwo.Resources { } } + /// + /// Looks up a localized string similar to Show all messages send by target. + /// + internal static string Options_Tabs_SenderMessages { + get { + return ResourceManager.GetString("Options_Tabs_SenderMessages", resourceCulture); + } + } + /// /// Looks up a localized string similar to Show timestamps. /// @@ -3713,6 +3785,15 @@ namespace ChatTwo.Resources { } } + /// + /// Looks up a localized string similar to Tell Exclusive. + /// + internal static string Tabs_Presets_Tell { + get { + return ResourceManager.GetString("Tabs_Presets_Tell", resourceCulture); + } + } + /// /// Looks up a localized string similar to All. /// diff --git a/ChatTwo/Resources/Language.ca.resx b/ChatTwo/Resources/Language.ca.resx index 8972cc1..b091bea 100644 --- a/ChatTwo/Resources/Language.ca.resx +++ b/ChatTwo/Resources/Language.ca.resx @@ -391,6 +391,9 @@ Event + + Tell Exclusive + Nova pestanya @@ -1144,6 +1147,9 @@ Disable input for this channel + + Show all messages send by target + Input is disabled for this tab @@ -1399,4 +1405,25 @@ Usage Notice + + This disables the channel selection and shows all messages coming from the target. + + + Target + + + message is empty + + + message is longer than 500 bytes + + + message contained invalid characters + + + Source + + + Target + diff --git a/ChatTwo/Resources/Language.de.resx b/ChatTwo/Resources/Language.de.resx index cad22e2..a6fd989 100644 --- a/ChatTwo/Resources/Language.de.resx +++ b/ChatTwo/Resources/Language.de.resx @@ -392,6 +392,9 @@ Sie wurden gewarnt. Ereignis + + Tell Exclusive + Neuer Tab @@ -1145,6 +1148,9 @@ Sie wurden gewarnt. Eingabe für diesen Kanal deaktivieren + + Show all messages send by target + Eingabe ist für diesen Tab deaktiviert @@ -1402,4 +1408,25 @@ Nachdem du 'Aktiviert' angeklickt und auf 'Start' gedrückt hast, wird die einge Nutzungshinweis + + This disables the channel selection and shows all messages coming from the target. + + + Target + + + message is empty + + + message is longer than 500 bytes + + + message contained invalid characters + + + Source + + + Target + diff --git a/ChatTwo/Resources/Language.es.resx b/ChatTwo/Resources/Language.es.resx index 80cb479..0e825ba 100644 --- a/ChatTwo/Resources/Language.es.resx +++ b/ChatTwo/Resources/Language.es.resx @@ -391,6 +391,9 @@ Evento + + Tell Exclusive + Nueva pestaña @@ -1144,6 +1147,9 @@ Deshabilitar entrada para este canal + + Show all messages send by target + La entrada está deshabilitada para esta pestaña @@ -1399,4 +1405,25 @@ Aviso de Uso + + This disables the channel selection and shows all messages coming from the target. + + + Target + + + message is empty + + + message is longer than 500 bytes + + + message contained invalid characters + + + Source + + + Target + diff --git a/ChatTwo/Resources/Language.fr.resx b/ChatTwo/Resources/Language.fr.resx index 4382358..6c01043 100644 --- a/ChatTwo/Resources/Language.fr.resx +++ b/ChatTwo/Resources/Language.fr.resx @@ -391,6 +391,9 @@ Evénement + + Tell Exclusive + Nouvel onglet @@ -1144,6 +1147,9 @@ Désactiver la saisie pour ce canal + + Show all messages send by target + La saisie est désactivée pour cet onglet @@ -1399,4 +1405,25 @@ Notice d'utilisation + + This disables the channel selection and shows all messages coming from the target. + + + Target + + + message is empty + + + message is longer than 500 bytes + + + message contained invalid characters + + + Source + + + Target + diff --git a/ChatTwo/Resources/Language.it.resx b/ChatTwo/Resources/Language.it.resx index 88411dd..3d78395 100644 --- a/ChatTwo/Resources/Language.it.resx +++ b/ChatTwo/Resources/Language.it.resx @@ -391,6 +391,9 @@ Event + + Tell Exclusive + Nuova tab @@ -1144,6 +1147,9 @@ Disable input for this channel + + Show all messages send by target + Input is disabled for this tab @@ -1399,4 +1405,25 @@ Avviso Di Utilizzo + + This disables the channel selection and shows all messages coming from the target. + + + Target + + + message is empty + + + message is longer than 500 bytes + + + message contained invalid characters + + + Source + + + Target + diff --git a/ChatTwo/Resources/Language.ja.resx b/ChatTwo/Resources/Language.ja.resx index e9494fb..6b4a676 100644 --- a/ChatTwo/Resources/Language.ja.resx +++ b/ChatTwo/Resources/Language.ja.resx @@ -391,6 +391,9 @@ イベント + + Tell Exclusive + 新しいタブ @@ -1144,6 +1147,9 @@ このチャンネルの入力を無効にする + + Show all messages send by target + このタブの入力は無効です @@ -1399,4 +1405,25 @@ 使用上の注意 + + This disables the channel selection and shows all messages coming from the target. + + + Target + + + message is empty + + + message is longer than 500 bytes + + + message contained invalid characters + + + Source + + + Target + diff --git a/ChatTwo/Resources/Language.ko.resx b/ChatTwo/Resources/Language.ko.resx index 8f09ed0..b310b0a 100644 --- a/ChatTwo/Resources/Language.ko.resx +++ b/ChatTwo/Resources/Language.ko.resx @@ -391,6 +391,9 @@ 이벤트 + + Tell Exclusive + 새 탭 @@ -1144,6 +1147,9 @@ 이 탭에서 입력 비활성화 + + Show all messages send by target + 이 탭에서 입력이 비활성화된 상태입니다. @@ -1399,4 +1405,25 @@ Usage Notice + + This disables the channel selection and shows all messages coming from the target. + + + Target + + + message is empty + + + message is longer than 500 bytes + + + message contained invalid characters + + + Source + + + Target + diff --git a/ChatTwo/Resources/Language.nl.resx b/ChatTwo/Resources/Language.nl.resx index 21ee50f..481c511 100644 --- a/ChatTwo/Resources/Language.nl.resx +++ b/ChatTwo/Resources/Language.nl.resx @@ -391,6 +391,9 @@ Evenement + + Tell Exclusive + Nieuw Tabblad @@ -1144,6 +1147,9 @@ Invoer voor dit kanaal uitschakelen + + Show all messages send by target + Invoer is uitgeschakeld voor dit tabblad @@ -1399,4 +1405,25 @@ Gebruiksaanwijzing + + This disables the channel selection and shows all messages coming from the target. + + + Target + + + message is empty + + + message is longer than 500 bytes + + + message contained invalid characters + + + Source + + + Target + diff --git a/ChatTwo/Resources/Language.pt-BR.resx b/ChatTwo/Resources/Language.pt-BR.resx index ba291f0..5ac32b0 100644 --- a/ChatTwo/Resources/Language.pt-BR.resx +++ b/ChatTwo/Resources/Language.pt-BR.resx @@ -391,6 +391,9 @@ Evento + + Tell Exclusive + Nova aba @@ -1144,6 +1147,9 @@ Desativar a entrada para este canal + + Show all messages send by target + A entrada está desativada nesta aba @@ -1399,4 +1405,25 @@ Usage Notice + + This disables the channel selection and shows all messages coming from the target. + + + Target + + + message is empty + + + message is longer than 500 bytes + + + message contained invalid characters + + + Source + + + Target + diff --git a/ChatTwo/Resources/Language.resx b/ChatTwo/Resources/Language.resx index fdaf26c..c6f7441 100644 --- a/ChatTwo/Resources/Language.resx +++ b/ChatTwo/Resources/Language.resx @@ -391,6 +391,9 @@ Event + + Tell Exclusive + New tab @@ -1144,6 +1147,9 @@ Disable input for this channel + + Show all messages send by target + Input is disabled for this tab @@ -1399,4 +1405,25 @@ Usage Notice + + This disables the channel selection and shows all messages coming from the target. + + + Target + + + message is empty + + + message is longer than 500 bytes + + + message contained invalid characters + + + Source + + + Target + diff --git a/ChatTwo/Resources/Language.ro.resx b/ChatTwo/Resources/Language.ro.resx index e54073a..93ecb54 100644 --- a/ChatTwo/Resources/Language.ro.resx +++ b/ChatTwo/Resources/Language.ro.resx @@ -391,6 +391,9 @@ Eveniment + + Tell Exclusive + Tab nou @@ -1144,6 +1147,9 @@ Disable input for this channel + + Show all messages send by target + Input is disabled for this tab @@ -1399,4 +1405,25 @@ Usage Notice + + This disables the channel selection and shows all messages coming from the target. + + + Target + + + message is empty + + + message is longer than 500 bytes + + + message contained invalid characters + + + Source + + + Target + diff --git a/ChatTwo/Resources/Language.ru.resx b/ChatTwo/Resources/Language.ru.resx index 6d4a727..cd55afc 100644 --- a/ChatTwo/Resources/Language.ru.resx +++ b/ChatTwo/Resources/Language.ru.resx @@ -391,6 +391,9 @@ События + + Tell Exclusive + Новая вкладка @@ -1144,6 +1147,9 @@ Disable input for this channel + + Show all messages send by target + Input is disabled for this tab @@ -1399,4 +1405,25 @@ Usage Notice + + This disables the channel selection and shows all messages coming from the target. + + + Target + + + message is empty + + + message is longer than 500 bytes + + + message contained invalid characters + + + Source + + + Target + diff --git a/ChatTwo/Resources/Language.sv.resx b/ChatTwo/Resources/Language.sv.resx index 33f9692..8d1a59d 100644 --- a/ChatTwo/Resources/Language.sv.resx +++ b/ChatTwo/Resources/Language.sv.resx @@ -391,6 +391,9 @@ Event + + Tell Exclusive + Ny flik @@ -1144,6 +1147,9 @@ Disable input for this channel + + Show all messages send by target + Input is disabled for this tab @@ -1399,4 +1405,25 @@ Usage Notice + + This disables the channel selection and shows all messages coming from the target. + + + Target + + + message is empty + + + message is longer than 500 bytes + + + message contained invalid characters + + + Source + + + Target + diff --git a/ChatTwo/Resources/Language.zh-Hans.resx b/ChatTwo/Resources/Language.zh-Hans.resx index b37811a..2924706 100644 --- a/ChatTwo/Resources/Language.zh-Hans.resx +++ b/ChatTwo/Resources/Language.zh-Hans.resx @@ -391,6 +391,9 @@ 剧情 + + Tell Exclusive + 新建标签页 @@ -1144,6 +1147,9 @@ 禁用此频道的输入 + + Show all messages send by target + 此选项卡已禁用输入 @@ -1399,4 +1405,25 @@ 使用须知 + + This disables the channel selection and shows all messages coming from the target. + + + Target + + + message is empty + + + message is longer than 500 bytes + + + message contained invalid characters + + + Source + + + Target + diff --git a/ChatTwo/Resources/Language.zh-Hant.resx b/ChatTwo/Resources/Language.zh-Hant.resx index 2cdcc14..819f5da 100644 --- a/ChatTwo/Resources/Language.zh-Hant.resx +++ b/ChatTwo/Resources/Language.zh-Hant.resx @@ -391,6 +391,9 @@ 劇情 + + Tell Exclusive + 新建標籤頁 @@ -1145,6 +1148,9 @@ 禁用此頻道的輸入 + + Show all messages send by target + 詞選項卡已禁用輸入 @@ -1400,4 +1406,25 @@ Usage Notice + + This disables the channel selection and shows all messages coming from the target. + + + Target + + + message is empty + + + message is longer than 500 bytes + + + message contained invalid characters + + + Source + + + Target + diff --git a/ChatTwo/Sheets.cs b/ChatTwo/Sheets.cs index 3823ce0..80eb1e3 100644 --- a/ChatTwo/Sheets.cs +++ b/ChatTwo/Sheets.cs @@ -1,4 +1,5 @@ -using Lumina.Excel; +using Dalamud.Game.ClientState.Objects.SubKinds; +using Lumina.Excel; using Lumina.Excel.Sheets; namespace ChatTwo; @@ -8,7 +9,6 @@ public static class Sheets public static readonly ExcelSheet ItemSheet; public static readonly ExcelSheet WorldSheet; public static readonly ExcelSheet StatusSheet; - public static readonly ExcelSheet UIColorSheet; public static readonly ExcelSheet LogKindSheet; public static readonly ExcelSheet LogFilterSheet; public static readonly ExcelSheet EventItemSheet; @@ -22,7 +22,6 @@ public static class Sheets ItemSheet = Plugin.DataManager.GetExcelSheet(); WorldSheet = Plugin.DataManager.GetExcelSheet(); StatusSheet = Plugin.DataManager.GetExcelSheet(); - UIColorSheet = Plugin.DataManager.GetExcelSheet(); LogKindSheet = Plugin.DataManager.GetExcelSheet(); LogFilterSheet = Plugin.DataManager.GetExcelSheet(); EventItemSheet = Plugin.DataManager.GetExcelSheet(); @@ -35,4 +34,10 @@ public static class Sheets public static bool IsInForay() => TerritorySheet.TryGetRow(Plugin.ClientState.TerritoryType, out var row) && row.TerritoryIntendedUse.RowId is 41 or 61; + + public static IEnumerable WorldsOnDatacenter(IPlayerCharacter character) + { + var dcRow = character.HomeWorld.Value.DataCenter.Value.Region.RowId; + return WorldSheet.Where(world => world.IsPublic && world.DataCenter.Value.Region.RowId == dcRow); + } } \ No newline at end of file diff --git a/ChatTwo/Ui/ChatLogWindow.cs b/ChatTwo/Ui/ChatLogWindow.cs index c74e4ba..fbde765 100644 --- a/ChatTwo/Ui/ChatLogWindow.cs +++ b/ChatTwo/Ui/ChatLogWindow.cs @@ -639,8 +639,7 @@ public sealed class ChatLogWindow : Window { ImGui.SetNextWindowSize(new Vector2(500 * ImGuiHelpers.GlobalScale, -1)); using var tooltip = ImRaii.Tooltip(); - if (tooltip) - Plugin.InputPreview.DrawPreview(); + Plugin.InputPreview.DrawPreview(); } if (ImGui.IsItemDeactivated()) @@ -775,7 +774,7 @@ public sealed class ChatLogWindow : Window if (!currentChannel.SequenceEqual(PreviousChannel)) { PreviousChannel = currentChannel; - Plugin.ServerCore?.SendChannelSwitch(currentChannel); + Plugin.ServerCore.SendChannelSwitch(currentChannel); } DrawChunks(currentChannel); @@ -820,12 +819,19 @@ public sealed class ChatLogWindow : Window } else if (activeTab is { Channel: { } channel }) { - // We cannot lookup ExtraChat channel names from index over - // IPC so we just don't show the name if it's the tabs channel. - // - // We don't call channel.ToChatType().Name() as it has the - // long name as used in the settings window. - channelNameChunks = [new TextChunk(ChunkSource.None, null, channel.IsExtraChatLinkshell() ? $"ECLS [{channel.LinkshellIndex() + 1}]" : channel.ToChatType().Name())]; + if (channel == InputChannel.Tell && activeTab.TellTarget.IsSet()) + { + channelNameChunks = GenerateTellTargetName(activeTab.TellTarget); + } + else + { + // We cannot lookup ExtraChat channel names from index over + // IPC so we just don't show the name if it's the tabs channel. + // + // We don't call channel.ToChatType().Name() as it has the + // long name as used in the settings window. + channelNameChunks = [new TextChunk(ChunkSource.None, null, channel.IsExtraChatLinkshell() ? $"ECLS [{channel.LinkshellIndex() + 1}]" : channel.ToChatType().Name())]; + } } else if (Plugin.ExtraChat.ChannelOverride is var (overrideName, _)) { @@ -912,7 +918,7 @@ public sealed class ChatLogWindow : Window playerName = HashPlayer(tellTarget.Name, tellTarget.World); var world = Sheets.WorldSheet.TryGetRow(tellTarget.World, out var worldRow) - ? worldRow.Name.ExtractText() + ? worldRow.Name.ToString() : "???"; return @@ -954,7 +960,7 @@ public sealed class ChatLogWindow : Window if (!trimmed.StartsWith('/')) { - var target = activeTab.CurrentChannel.TempTellTarget ?? activeTab.CurrentChannel.TellTarget; + var target = activeTab.TellTarget.IsSet() ? activeTab.TellTarget : activeTab.CurrentChannel.TempTellTarget ?? activeTab.CurrentChannel.TellTarget; if (target != null) { // ContentId 0 is a case where we can't directly send messages, so we send a /tell formatted message and let the game handle it diff --git a/ChatTwo/Ui/DbViewer.cs b/ChatTwo/Ui/DbViewer.cs index ff2adfc..da4eba5 100644 --- a/ChatTwo/Ui/DbViewer.cs +++ b/ChatTwo/Ui/DbViewer.cs @@ -35,7 +35,7 @@ public class DbViewer : Window private int CurrentPage = 1; private string SimpleSearchTerm = ""; private bool OnlyCurrentCharacter = true; - private readonly Dictionary ChatCodes; + private readonly Dictionary SelectedChannels; private bool IsProcessing; private long ProcessingStart = Environment.TickCount64; @@ -58,12 +58,12 @@ public class DbViewer : Window public DbViewer(Plugin plugin) : base("DBViewer###chat2-dbviewer") { Plugin = plugin; - ChatCodes = TabsUtil.MostlyPlayer; + SelectedChannels = TabsUtil.MostlyPlayer; DateFormat = CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern; DateTimeFormat = "ddd, dd MMM yyy HH:mm:ss"; - LastProcessed = (AfterDate, BeforeDate, CurrentPage, OnlyCurrentCharacter, ChatCodes.Count); + LastProcessed = (AfterDate, BeforeDate, CurrentPage, OnlyCurrentCharacter, SelectedChannels.Count); DateReset(); SizeConstraints = new WindowSizeConstraints @@ -196,7 +196,7 @@ public class DbViewer : Window if (DateWidget.Validate(MinimalDate, ref AfterDate, ref BeforeDate)) DateRefresh(); - if (!IsProcessing && LastProcessed != (AfterDate, BeforeDate, CurrentPage, OnlyCurrentCharacter, ChatCodes.Count)) + if (!IsProcessing && LastProcessed != (AfterDate, BeforeDate, CurrentPage, OnlyCurrentCharacter, SelectedChannels.Count)) { // Page hasn't changed, so we reset it back to 1 if (LastProcessed.Page == CurrentPage) @@ -205,13 +205,13 @@ public class DbViewer : Window AdjustDates(); IsProcessing = true; ProcessingStart = Environment.TickCount64 + 1_000; // + 1 second - LastProcessed = (AfterDate, BeforeDate, CurrentPage, OnlyCurrentCharacter, ChatCodes.Count); + LastProcessed = (AfterDate, BeforeDate, CurrentPage, OnlyCurrentCharacter, SelectedChannels.Count); Task.Run(() => { try { ulong? character = OnlyCurrentCharacter ? Plugin.PlayerState.ContentId : null; - var channels = ChatCodes.Select(c => (uint) c.Key).ToArray(); + var channels = SelectedChannels.Select(pair => (byte) pair.Key).ToArray(); // We only want to fetch count if this is the first page if (CurrentPage == 1) @@ -263,7 +263,7 @@ public class DbViewer : Window ImGui.TableNextColumn(); var pos = ImGui.GetCursorPos(); - ImGuiUtil.CenterText($"{message.Code.Raw}"); + ImGuiUtil.CenterText($"{(byte)message.Code.Type}"); ImGui.SetCursorPos(pos); ImGui.Dummy(columnWidth); if (ImGui.IsItemHovered()) @@ -303,13 +303,13 @@ public class DbViewer : Window if (type.IsGm()) continue; - var enabled = ChatCodes.ContainsKey(type); + var enabled = SelectedChannels.ContainsKey(type); if (ImGui.Checkbox($"##{type.Name()}", ref enabled)) { if (enabled) - ChatCodes[type] = ChatSourceExt.All; + SelectedChannels[type] = (ChatSourceExt.All, ChatSourceExt.All); else - ChatCodes.Remove(type); + SelectedChannels.Remove(type); } ImGui.SameLine(); @@ -359,7 +359,7 @@ public class DbViewer : Window try { ulong? character = OnlyCurrentCharacter ? Plugin.PlayerState.ContentId : null; - var channels = ChatCodes.Select(c => (uint)c.Key).ToArray(); + var channels = SelectedChannels.Select(pair => (byte)pair.Key).ToArray(); var rangeMessageEnumerator = Plugin.MessageManager.Store.GetDateRange(AfterDate, BeforeDate, channels, character); var messageHistory = rangeMessageEnumerator.ToArray(); @@ -426,7 +426,7 @@ public class DbViewer : Window { try { - var channels = ChatCodes.Select(c => (uint)c.Key).ToArray(); + var channels = SelectedChannels.Select(pair => (byte)pair.Key).ToArray(); var rangeMessageEnumerator = Plugin.MessageManager.Store.GetDateRange(AfterDate, BeforeDate, channels); var messageHistory = rangeMessageEnumerator.ToArray(); @@ -536,7 +536,7 @@ public class DbViewer : Window color ??= 0; - var userContent = text.Content ?? string.Empty; + var userContent = text.Content; if (Plugin.ChatLogWindow.ScreenshotMode) { if (chunk.Link is PlayerPayload playerPayload) diff --git a/ChatTwo/Ui/InputPreview.cs b/ChatTwo/Ui/InputPreview.cs index 7a4c6ad..bc99be6 100644 --- a/ChatTwo/Ui/InputPreview.cs +++ b/ChatTwo/Ui/InputPreview.cs @@ -71,7 +71,7 @@ public partial class InputPreview : Window AutoTranslate.ReplaceWithPayload(ref bytes); var chunks = ChunkUtil.ToChunks(SeString.Parse(bytes), ChunkSource.Content, ChatType.Say).ToList(); - PreviewMessage = Message.FakeMessage(chunks, new ChatCode((ushort)XivChatType.Say)); + PreviewMessage = Message.FakeMessage(chunks, new ChatCode(XivChatType.Say, 0, 0)); PreviewMessage.DecodeTextParam(); } HasEvaluation = !Plugin.Config.OnlyPreviewIf || PreviewMessage.Content.Count > 1; diff --git a/ChatTwo/Ui/SettingsTabs/Database.cs b/ChatTwo/Ui/SettingsTabs/Database.cs index a6e3610..e38445d 100755 --- a/ChatTwo/Ui/SettingsTabs/Database.cs +++ b/ChatTwo/Ui/SettingsTabs/Database.cs @@ -7,6 +7,7 @@ using Dalamud.Game.Text.SeStringHandling.Payloads; using Dalamud.Interface.ImGuiNotification; using Dalamud.Interface.Utility.Raii; using Dalamud.Bindings.ImGui; +using Dalamud.Game.Text; namespace ChatTwo.Ui.SettingsTabs; @@ -180,17 +181,17 @@ internal sealed class Database : ISettingsTab .Build(); var contentChunks = ChunkUtil.ToChunks(contentSource, ChunkSource.Content, ChatType.Debug).ToList(); + var chatCode = new ChatCode(XivChatType.Say, 0, 0); messages.Add(new Message( Guid.NewGuid(), Plugin.MessageManager.CurrentContentId, Plugin.MessageManager.CurrentContentId, DateTimeOffset.UtcNow, - new ChatCode(10), + chatCode, senderChunks, contentChunks, senderSource, contentSource, - new SortCode(ChatType.Debug, ChatSource.Self), Guid.Empty )); } diff --git a/ChatTwo/Ui/SettingsTabs/Display.cs b/ChatTwo/Ui/SettingsTabs/Display.cs index 71da7e7..1f2ec64 100755 --- a/ChatTwo/Ui/SettingsTabs/Display.cs +++ b/ChatTwo/Ui/SettingsTabs/Display.cs @@ -1,4 +1,3 @@ -using ChatTwo.Code; using ChatTwo.Resources; using ChatTwo.Util; using Dalamud.Interface.Utility.Raii; @@ -67,26 +66,24 @@ internal sealed class Display : ISettingsTab using var channelTree = ImRaii.TreeNode(Language.Options_InactivityHideChannels_Name); if (channelTree.Success) { - if (ImGuiUtil.CtrlShiftButton(Language.Options_InactivityHideChannels_All_Label, - Language.Options_InactivityHideChannels_Button_Tooltip)) + if (ImGuiUtil.CtrlShiftButton(Language.Options_InactivityHideChannels_All_Label, Language.Options_InactivityHideChannels_Button_Tooltip)) { - Mutable.InactivityHideChannels = TabsUtil.AllChannels(); + Mutable.InactivityHideChannelsV2 = TabsUtil.AllChannels(); Mutable.InactivityHideExtraChatAll = true; Mutable.InactivityHideExtraChatChannels = []; } ImGui.SameLine(); - if (ImGuiUtil.CtrlShiftButton(Language.Options_InactivityHideChannels_None_Label, - Language.Options_InactivityHideChannels_Button_Tooltip)) + if (ImGuiUtil.CtrlShiftButton(Language.Options_InactivityHideChannels_None_Label, Language.Options_InactivityHideChannels_Button_Tooltip)) { - Mutable.InactivityHideChannels = new Dictionary(); + Mutable.InactivityHideChannelsV2 = []; Mutable.InactivityHideExtraChatAll = false; Mutable.InactivityHideExtraChatChannels = []; } ImGui.Spacing(); - ImGuiUtil.ChannelSelector(Language.Options_Tabs_Channels, Mutable.InactivityHideChannels!); + ImGuiUtil.ChannelSelector(Language.Options_Tabs_Channels, Mutable.InactivityHideChannelsV2); ImGuiUtil.ExtraChatSelector(Language.Options_Tabs_ExtraChatChannels, ref Mutable.InactivityHideExtraChatAll, Mutable.InactivityHideExtraChatChannels); } diff --git a/ChatTwo/Ui/SettingsTabs/Fonts.cs b/ChatTwo/Ui/SettingsTabs/Fonts.cs index be09180..a55543a 100755 --- a/ChatTwo/Ui/SettingsTabs/Fonts.cs +++ b/ChatTwo/Ui/SettingsTabs/Fonts.cs @@ -39,7 +39,7 @@ public class Fonts : ISettingsTab }); ImGui.SameLine(); if (ImGui.Button("Reset##global")) - Mutable.GlobalFontV2 = new SingleFontSpec{ FontId = new DalamudAssetFontAndFamilyId(DalamudAsset.NotoSansKrRegular), SizePt = 12.75f }; + Mutable.GlobalFontV2 = new SingleFontSpec{ FontId = new DalamudAssetFontAndFamilyId(DalamudAsset.NotoSansCjkRegular), SizePt = 12.75f }; ImGuiUtil.HelpText(string.Format(Language.Options_Font_Description, Plugin.PluginName)); ImGuiUtil.WarningText(Language.Options_Font_Warning); @@ -54,7 +54,7 @@ public class Fonts : ISettingsTab }); ImGui.SameLine(); if (ImGui.Button("Reset##japanese")) - Mutable.JapaneseFontV2 = new SingleFontSpec{ FontId = new DalamudAssetFontAndFamilyId(DalamudAsset.NotoSansJpMedium), SizePt = 12.75f }; + Mutable.JapaneseFontV2 = new SingleFontSpec{ FontId = new DalamudAssetFontAndFamilyId(DalamudAsset.NotoSansCjkMedium), SizePt = 12.75f }; ImGuiUtil.HelpText(string.Format(Language.Options_JapaneseFont_Description, Plugin.PluginName)); ImGui.Spacing(); @@ -69,7 +69,7 @@ public class Fonts : ISettingsTab if (ImGui.Button("Reset##italic")) { Mutable.ItalicEnabled = false; - Mutable.ItalicFontV2 = new SingleFontSpec{ FontId = new DalamudAssetFontAndFamilyId(DalamudAsset.NotoSansKrRegular), SizePt = 12.75f }; + Mutable.ItalicFontV2 = new SingleFontSpec{ FontId = new DalamudAssetFontAndFamilyId(DalamudAsset.NotoSansCjkRegular), SizePt = 12.75f }; } ImGuiUtil.HelpText(string.Format(Language.Options_Italic_Description, Plugin.PluginName)); diff --git a/ChatTwo/Ui/SettingsTabs/Tabs.cs b/ChatTwo/Ui/SettingsTabs/Tabs.cs index eab084c..dd57be1 100755 --- a/ChatTwo/Ui/SettingsTabs/Tabs.cs +++ b/ChatTwo/Ui/SettingsTabs/Tabs.cs @@ -4,6 +4,7 @@ using ChatTwo.Util; using Dalamud.Interface; using Dalamud.Interface.Utility.Raii; using Dalamud.Bindings.ImGui; +using Dalamud.Game.ClientState.Objects.SubKinds; namespace ChatTwo.Ui.SettingsTabs; @@ -43,6 +44,9 @@ internal sealed class Tabs : ISettingsTab if (ImGui.Selectable(string.Format(Language.Options_Tabs_Preset, Language.Tabs_Presets_Event))) Mutable.Tabs.Add(TabsUtil.VanillaEvent); + + if (ImGui.Selectable(string.Format(Language.Options_Tabs_Preset, Language.Tabs_Presets_Tell))) + Mutable.Tabs.Add(TabsUtil.VanillaTellExclusive); } } @@ -122,7 +126,7 @@ internal sealed class Tabs : ISettingsTab using (var combo = ImGuiUtil.BeginComboVertical(Language.Options_Tabs_UnreadMode, tab.UnreadMode.Name())) { - if (combo) + if (combo.Success) { foreach (var mode in Enum.GetValues()) { @@ -142,19 +146,75 @@ internal sealed class Tabs : ISettingsTab if (!tab.InputDisabled) { var input = tab.Channel?.ToChatType().Name() ?? Language.Options_Tabs_NoInputChannel; - using var combo = ImGuiUtil.BeginComboVertical(Language.Options_Tabs_InputChannel, input); - if (combo) + using (var combo = ImGuiUtil.BeginComboVertical(Language.Options_Tabs_InputChannel, input)) { - if (ImGui.Selectable(Language.Options_Tabs_NoInputChannel, tab.Channel == null)) - tab.Channel = null; + if (combo.Success) + { + if (ImGui.Selectable(Language.Options_Tabs_NoInputChannel, tab.Channel == null)) + tab.Channel = null; - foreach (var channel in Enum.GetValues()) - if (ImGui.Selectable(channel.ToChatType().Name(), tab.Channel == channel)) - tab.Channel = channel; + foreach (var channel in Enum.GetValues()) + if (ImGui.Selectable(channel.ToChatType().Name(), tab.Channel == channel)) + tab.Channel = channel; + } + } + + ImGui.Checkbox(Language.Options_Tabs_SenderMessages, ref tab.AllSenderMessages); + ImGuiUtil.HelpText(Language.Options_Help_SenderMessages); + + var player = Plugin.ObjectTable.LocalPlayer; + if (tab.Channel == InputChannel.Tell && player != null) + { + var worlds = Sheets.WorldsOnDatacenter(player).OrderByDescending(world => world.DataCenter.RowId).ThenBy(world => world.Name.ToString()).ToList(); + + using (ImRaii.ItemWidth(ImGui.GetWindowWidth() / 3f)) + { + ImGui.Text(Language.Options_Header_Target); + ImGui.SameLine(); + + var name = tab.TellTarget.Name; + if (ImGui.InputText("##targetInput", ref name, 21)) + tab.TellTarget.Name = name; + + ImGui.SameLine(); + + var selectedWorld = worlds.FindIndex(world => world.RowId == tab.TellTarget.World); + if (selectedWorld == -1) + selectedWorld = 0; + + using (var combo = ImRaii.Combo("###player-world", worlds[selectedWorld].Name.ToString())) + { + if (combo.Success) + { + var lastDc = worlds.First().DataCenter.RowId; + foreach (var (idx, world) in worlds.Index()) + { + if (ImGui.Selectable(world.Name.ToString(), selectedWorld == idx)) + { + selectedWorld = idx; + tab.TellTarget.World = worlds[selectedWorld].RowId; + } + + if (lastDc == world.DataCenter.RowId) + continue; + + lastDc = world.DataCenter.RowId; + ImGui.Separator(); + } + } + } + } + + var target = (Plugin.TargetManager.SoftTarget ?? Plugin.TargetManager.Target) as IPlayerCharacter; + using (ImRaii.Disabled(target == null)) + { + if (ImGui.Button("Set to target") && target != null) + tab.TellTarget.FromTarget(target); + } } } - ImGuiUtil.ChannelSelector(Language.Options_Tabs_Channels, tab.ChatCodes); + ImGuiUtil.ChannelSelector(Language.Options_Tabs_Channels, tab.SelectedChannels); ImGuiUtil.ExtraChatSelector(Language.Options_Tabs_ExtraChatChannels, ref tab.ExtraChatAll, tab.ExtraChatChannels); } diff --git a/ChatTwo/Util/ChunkUtil.cs b/ChatTwo/Util/ChunkUtil.cs index 26bf77d..6f4728c 100755 --- a/ChatTwo/Util/ChunkUtil.cs +++ b/ChatTwo/Util/ChunkUtil.cs @@ -1,11 +1,8 @@ -using System.Runtime.CompilerServices; using ChatTwo.Code; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Game.Text.SeStringHandling.Payloads; using System.Text; -using Lumina.Text.Expressions; using Lumina.Text.Payloads; -using Lumina.Text.ReadOnly; using PayloadType = Dalamud.Game.Text.SeStringHandling.PayloadType; @@ -417,46 +414,46 @@ internal static class ChunkUtil return BitConverter.ToUInt32(numArray, 0); } - private static bool TryResolveUInt(in ReadOnlySeExpressionSpan expression, out uint value) - { - if (expression.TryGetUInt(out value)) - return true; + // private static bool TryResolveUInt(in ReadOnlySeExpressionSpan expression, out uint value) + // { + // if (expression.TryGetUInt(out value)) + // return true; + // + // if (expression.TryGetParameterExpression(out var exprType, out var operand1)) + // { + // if (!TryResolveUInt(operand1, out var paramIndex)) + // return false; + // + // if (paramIndex == 0) + // return false; + // + // paramIndex--; + // if ((ExpressionType)exprType == ExpressionType.GlobalNumber) + // { + // value = (uint) GlobalParametersCache.GetValue((int)paramIndex); + // return true; + // } + // // return (ExpressionType)exprType switch + // // { + // // // ExpressionType.LocalNumber => context.TryGetLNum((int)paramIndex, out value), // lnum + // // ExpressionType.GlobalNumber => (uint) GlobalParametersCache.GetValue((int)paramIndex), // gnum + // // _ => false, // gstr, lstr + // // }; + // } + // + // return false; + // } - if (expression.TryGetParameterExpression(out var exprType, out var operand1)) - { - if (!TryResolveUInt(operand1, out var paramIndex)) - return false; - - if (paramIndex == 0) - return false; - - paramIndex--; - if ((ExpressionType)exprType == ExpressionType.GlobalNumber) - { - value = (uint) GlobalParametersCache.GetValue((int)paramIndex); - return true; - } - // return (ExpressionType)exprType switch - // { - // // ExpressionType.LocalNumber => context.TryGetLNum((int)paramIndex, out value), // lnum - // ExpressionType.GlobalNumber => (uint) GlobalParametersCache.GetValue((int)paramIndex), // gnum - // _ => false, // gstr, lstr - // }; - } - - return false; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool TryResolveInt(in ReadOnlySeExpressionSpan expression, out int value) - { - if (TryResolveUInt(expression, out var u32)) - { - value = (int)u32; - return true; - } - - value = 0; - return false; - } + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // private static bool TryResolveInt(in ReadOnlySeExpressionSpan expression, out int value) + // { + // if (TryResolveUInt(expression, out var u32)) + // { + // value = (int)u32; + // return true; + // } + // + // value = 0; + // return false; + // } } diff --git a/ChatTwo/Util/ColourUtil.cs b/ChatTwo/Util/ColourUtil.cs index c47f4d6..622bde0 100755 --- a/ChatTwo/Util/ColourUtil.cs +++ b/ChatTwo/Util/ColourUtil.cs @@ -4,20 +4,19 @@ using System.Numerics; namespace ChatTwo.Util; internal static class ColourUtil { - private static (byte r, byte g, byte b, byte a) RgbaToComponents(uint rgba) + private static (byte r, byte g, byte b) RgbaToRgbComponents(uint rgba) { var r = (byte) ((rgba & 0xFF000000) >> 24); var g = (byte) ((rgba & 0xFF0000) >> 16); var b = (byte) ((rgba & 0xFF00) >> 8); - var a = (byte) (rgba & 0xFF); - return (r, g, b, a); + return (r, g, b); } internal static uint RgbaToAbgr(uint rgba) => BinaryPrimitives.ReverseEndianness(rgba); internal static Vector3 RgbaToVector3(uint rgba) { - var (r, g, b, _) = RgbaToComponents(rgba); + var (r, g, b) = RgbaToRgbComponents(rgba); return new Vector3((float) r / 255, (float) g / 255, (float) b / 255); } diff --git a/ChatTwo/Util/ExtraPayload.cs b/ChatTwo/Util/ExtraPayload.cs index 8ee60b8..baa093a 100644 --- a/ChatTwo/Util/ExtraPayload.cs +++ b/ChatTwo/Util/ExtraPayload.cs @@ -37,7 +37,9 @@ public class ColorPayload { return v switch { + // ReSharper disable once LocalizableElement -1 => throw new ArgumentException("Encountered premature end of input (unexpected EOF).", nameof(v)), + // ReSharper disable once LocalizableElement 0 => throw new ArgumentException("Encountered premature end of input (unexpected null character).", nameof(v)), _ => (uint)v << shift }; diff --git a/ChatTwo/Util/ImGuiUtil.cs b/ChatTwo/Util/ImGuiUtil.cs index 384e9e5..dd354ce 100755 --- a/ChatTwo/Util/ImGuiUtil.cs +++ b/ChatTwo/Util/ImGuiUtil.cs @@ -225,7 +225,7 @@ internal static class ImGuiUtil ImGui.TextUnformatted(text); } - internal static ImRaii.IEndObject BeginComboVertical(string label, string previewValue, ImGuiComboFlags flags = ImGuiComboFlags.None) + internal static ImRaii.ComboDisposable BeginComboVertical(string label, string previewValue, ImGuiComboFlags flags = ImGuiComboFlags.None) { ImGui.TextUnformatted(label); ImGui.SetNextItemWidth(-1); @@ -542,7 +542,7 @@ internal static class ImGuiUtil return result != 0 || key == VirtualKey.NO_KEY; } - public static void ChannelSelector(string headerText, Dictionary chatCodes) + public static void ChannelSelector(string headerText, Dictionary chatCodes) { using var channelNode = ImRaii.TreeNode(headerText); if (!channelNode.Success) @@ -563,7 +563,7 @@ internal static class ImGuiUtil if (ImGui.Checkbox($"##{type.Name()}", ref enabled)) { if (enabled) - chatCodes[type] = ChatSourceExt.All; + chatCodes[type] = (ChatSourceExt.All, ChatSourceExt.All); else chatCodes.Remove(type); } @@ -580,12 +580,24 @@ internal static class ImGuiUtil if (!typeNode.Success) continue; - chatCodes.TryGetValue(type, out var sourcesEnum); - var sources = (uint)sourcesEnum; + ImGui.Text(Language.ImGuiUtil_ChannelSelector_Source); + ImGui.SameLine(400.0f * ImGuiHelpers.GlobalScale); + ImGui.Text(Language.ImGuiUtil_ChannelSelector_Target); - foreach (var source in Enum.GetValues()) - if (ImGui.CheckboxFlags(source.Name(), ref sources, (uint)source)) - chatCodes[type] = (ChatSource)sources; + chatCodes.TryGetValue(type, out var sourcesEnum); + var sources = (uint)sourcesEnum.Source; + var targets = (uint)sourcesEnum.Target; + + foreach (var kind in Enum.GetValues().Where(s => s != ChatSource.None)) + { + if (ImGui.CheckboxFlags($"{kind.Name()}##source", ref sources, (uint)kind)) + chatCodes[type] = ((ChatSource)sources, sourcesEnum.Target); + + ImGui.SameLine(400.0f * ImGuiHelpers.GlobalScale); + + if (ImGui.CheckboxFlags($"{kind.Name()}##target", ref targets, (uint)kind)) + chatCodes[type] = (sourcesEnum.Source, (ChatSource)targets); + } } } } diff --git a/ChatTwo/Util/TabsUtil.cs b/ChatTwo/Util/TabsUtil.cs index 5373242..984d168 100755 --- a/ChatTwo/Util/TabsUtil.cs +++ b/ChatTwo/Util/TabsUtil.cs @@ -3,135 +3,147 @@ using ChatTwo.Resources; namespace ChatTwo.Util; -internal static class TabsUtil +public static class TabsUtil { - internal static Dictionary AllChannels() + public static Dictionary AllChannels() { - var channels = new Dictionary(); + var channels = new Dictionary(); foreach (var chatType in Enum.GetValues()) - channels[chatType] = ChatSourceExt.All; + channels[chatType] = (ChatSourceExt.All, ChatSourceExt.All); return channels; } - internal static Tab VanillaGeneral => new() + public static Tab VanillaGeneral => new() { Name = Language.Tabs_Presets_General, - ChatCodes = new Dictionary + SelectedChannels = new Dictionary { // Special - [ChatType.Debug] = ChatSourceExt.All, - [ChatType.Urgent] = ChatSourceExt.All, - [ChatType.Notice] = ChatSourceExt.All, + [ChatType.Debug] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.Urgent] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.Notice] = (ChatSourceExt.All, ChatSourceExt.All), // Chat - [ChatType.Say] = ChatSourceExt.All, - [ChatType.Yell] = ChatSourceExt.All, - [ChatType.Shout] = ChatSourceExt.All, - [ChatType.TellIncoming] = ChatSourceExt.All, - [ChatType.TellOutgoing] = ChatSourceExt.All, - [ChatType.Party] = ChatSourceExt.All, - [ChatType.CrossParty] = ChatSourceExt.All, - [ChatType.Alliance] = ChatSourceExt.All, - [ChatType.FreeCompany] = ChatSourceExt.All, - [ChatType.PvpTeam] = ChatSourceExt.All, - [ChatType.CrossLinkshell1] = ChatSourceExt.All, - [ChatType.CrossLinkshell2] = ChatSourceExt.All, - [ChatType.CrossLinkshell3] = ChatSourceExt.All, - [ChatType.CrossLinkshell4] = ChatSourceExt.All, - [ChatType.CrossLinkshell5] = ChatSourceExt.All, - [ChatType.CrossLinkshell6] = ChatSourceExt.All, - [ChatType.CrossLinkshell7] = ChatSourceExt.All, - [ChatType.CrossLinkshell8] = ChatSourceExt.All, - [ChatType.Linkshell1] = ChatSourceExt.All, - [ChatType.Linkshell2] = ChatSourceExt.All, - [ChatType.Linkshell3] = ChatSourceExt.All, - [ChatType.Linkshell4] = ChatSourceExt.All, - [ChatType.Linkshell5] = ChatSourceExt.All, - [ChatType.Linkshell6] = ChatSourceExt.All, - [ChatType.Linkshell7] = ChatSourceExt.All, - [ChatType.Linkshell8] = ChatSourceExt.All, - [ChatType.NoviceNetwork] = ChatSourceExt.All, - [ChatType.StandardEmote] = ChatSourceExt.All, - [ChatType.CustomEmote] = ChatSourceExt.All, + [ChatType.Say] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.Yell] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.Shout] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.TellIncoming] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.TellOutgoing] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.Party] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.CrossParty] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.Alliance] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.FreeCompany] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.PvpTeam] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.CrossLinkshell1] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.CrossLinkshell2] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.CrossLinkshell3] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.CrossLinkshell4] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.CrossLinkshell5] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.CrossLinkshell6] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.CrossLinkshell7] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.CrossLinkshell8] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.Linkshell1] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.Linkshell2] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.Linkshell3] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.Linkshell4] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.Linkshell5] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.Linkshell6] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.Linkshell7] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.Linkshell8] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.NoviceNetwork] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.StandardEmote] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.CustomEmote] = (ChatSourceExt.All, ChatSourceExt.All), // Announcements - [ChatType.System] = ChatSourceExt.All, - [ChatType.GatheringSystem] = ChatSourceExt.All, - [ChatType.Error] = ChatSourceExt.All, - [ChatType.Echo] = ChatSourceExt.All, - [ChatType.NoviceNetworkSystem] = ChatSourceExt.All, - [ChatType.FreeCompanyAnnouncement] = ChatSourceExt.All, - [ChatType.PvpTeamAnnouncement] = ChatSourceExt.All, - [ChatType.FreeCompanyLoginLogout] = ChatSourceExt.All, - [ChatType.PvpTeamLoginLogout] = ChatSourceExt.All, - [ChatType.RetainerSale] = ChatSourceExt.All, - [ChatType.NpcAnnouncement] = ChatSourceExt.All, - [ChatType.LootNotice] = ChatSourceExt.All, - [ChatType.Progress] = ChatSourceExt.All, - [ChatType.LootRoll] = ChatSourceExt.All, - [ChatType.Crafting] = ChatSourceExt.All, - [ChatType.Gathering] = ChatSource.Self, - [ChatType.PeriodicRecruitmentNotification] = ChatSourceExt.All, - [ChatType.Sign] = ChatSourceExt.All, - [ChatType.RandomNumber] = ChatSourceExt.All, - [ChatType.Orchestrion] = ChatSourceExt.All, - [ChatType.MessageBook] = ChatSourceExt.All, - [ChatType.Alarm] = ChatSourceExt.All, - [ChatType.GlamourNotifications] = ChatSourceExt.All, + [ChatType.System] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.GatheringSystem] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.Error] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.Echo] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.NoviceNetworkSystem] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.FreeCompanyAnnouncement] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.PvpTeamAnnouncement] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.FreeCompanyLoginLogout] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.PvpTeamLoginLogout] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.RetainerSale] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.NpcAnnouncement] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.LootNotice] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.Progress] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.LootRoll] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.Crafting] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.Gathering] = (ChatSource.LocalPlayer, ChatSource.LocalPlayer), + [ChatType.PeriodicRecruitmentNotification] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.Sign] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.RandomNumber] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.Orchestrion] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.MessageBook] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.Alarm] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.GlamourNotifications] = (ChatSourceExt.All, ChatSourceExt.All), } }; - internal static Tab VanillaEvent => new() + public static Tab VanillaEvent => new() { Name = Language.Tabs_Presets_Event, - ChatCodes = new Dictionary { [ChatType.NpcDialogue] = ChatSourceExt.All, }, + SelectedChannels = new Dictionary { [ChatType.NpcDialogue] = (ChatSourceExt.All, ChatSourceExt.All), }, }; - internal static Dictionary MostlyPlayer => new() + public static Tab VanillaTellExclusive => new() + { + Name = Language.Tabs_Presets_Tell, + SelectedChannels = new Dictionary + { + [ChatType.TellIncoming] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.TellOutgoing] = (ChatSourceExt.All, ChatSourceExt.All), + }, + Channel = InputChannel.Tell, + AllSenderMessages = true, + }; + + public static Dictionary MostlyPlayer => new() { // Special - [ChatType.Debug] = ChatSourceExt.All, - [ChatType.Urgent] = ChatSourceExt.All, - [ChatType.Notice] = ChatSourceExt.All, + [ChatType.Debug] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.Urgent] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.Notice] = (ChatSourceExt.All, ChatSourceExt.All), // Chat - [ChatType.Say] = ChatSourceExt.All, - [ChatType.Yell] = ChatSourceExt.All, - [ChatType.Shout] = ChatSourceExt.All, - [ChatType.TellIncoming] = ChatSourceExt.All, - [ChatType.TellOutgoing] = ChatSourceExt.All, - [ChatType.Party] = ChatSourceExt.All, - [ChatType.CrossParty] = ChatSourceExt.All, - [ChatType.Alliance] = ChatSourceExt.All, - [ChatType.FreeCompany] = ChatSourceExt.All, - [ChatType.PvpTeam] = ChatSourceExt.All, - [ChatType.CrossLinkshell1] = ChatSourceExt.All, - [ChatType.CrossLinkshell2] = ChatSourceExt.All, - [ChatType.CrossLinkshell3] = ChatSourceExt.All, - [ChatType.CrossLinkshell4] = ChatSourceExt.All, - [ChatType.CrossLinkshell5] = ChatSourceExt.All, - [ChatType.CrossLinkshell6] = ChatSourceExt.All, - [ChatType.CrossLinkshell7] = ChatSourceExt.All, - [ChatType.CrossLinkshell8] = ChatSourceExt.All, - [ChatType.Linkshell1] = ChatSourceExt.All, - [ChatType.Linkshell2] = ChatSourceExt.All, - [ChatType.Linkshell3] = ChatSourceExt.All, - [ChatType.Linkshell4] = ChatSourceExt.All, - [ChatType.Linkshell5] = ChatSourceExt.All, - [ChatType.Linkshell6] = ChatSourceExt.All, - [ChatType.Linkshell7] = ChatSourceExt.All, - [ChatType.Linkshell8] = ChatSourceExt.All, - [ChatType.NoviceNetwork] = ChatSourceExt.All, - [ChatType.StandardEmote] = ChatSourceExt.All, - [ChatType.CustomEmote] = ChatSourceExt.All, + [ChatType.Say] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.Yell] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.Shout] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.TellIncoming] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.TellOutgoing] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.Party] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.CrossParty] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.Alliance] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.FreeCompany] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.PvpTeam] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.CrossLinkshell1] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.CrossLinkshell2] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.CrossLinkshell3] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.CrossLinkshell4] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.CrossLinkshell5] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.CrossLinkshell6] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.CrossLinkshell7] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.CrossLinkshell8] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.Linkshell1] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.Linkshell2] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.Linkshell3] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.Linkshell4] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.Linkshell5] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.Linkshell6] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.Linkshell7] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.Linkshell8] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.NoviceNetwork] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.StandardEmote] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.CustomEmote] = (ChatSourceExt.All, ChatSourceExt.All), // Announcements - [ChatType.System] = ChatSourceExt.All, - [ChatType.Error] = ChatSourceExt.All, - [ChatType.Echo] = ChatSourceExt.All, - [ChatType.NoviceNetworkSystem] = ChatSourceExt.All, - [ChatType.FreeCompanyAnnouncement] = ChatSourceExt.All, - [ChatType.PvpTeamAnnouncement] = ChatSourceExt.All, - [ChatType.FreeCompanyLoginLogout] = ChatSourceExt.All, - [ChatType.PvpTeamLoginLogout] = ChatSourceExt.All, - [ChatType.RandomNumber] = ChatSourceExt.All, - [ChatType.MessageBook] = ChatSourceExt.All, + [ChatType.System] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.Error] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.Echo] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.NoviceNetworkSystem] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.FreeCompanyAnnouncement] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.PvpTeamAnnouncement] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.FreeCompanyLoginLogout] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.PvpTeamLoginLogout] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.RandomNumber] = (ChatSourceExt.All, ChatSourceExt.All), + [ChatType.MessageBook] = (ChatSourceExt.All, ChatSourceExt.All), }; } diff --git a/ChatTwo/packages.lock.json b/ChatTwo/packages.lock.json index 480ffe7..24899aa 100644 --- a/ChatTwo/packages.lock.json +++ b/ChatTwo/packages.lock.json @@ -4,9 +4,9 @@ "net10.0-windows7.0": { "DalamudPackager": { "type": "Direct", - "requested": "[14.0.2, )", - "resolved": "14.0.2", - "contentHash": "dQJeq+8eyHzra4Cg5eZ/3LAeS3/UpvvLriYJGSncMK9LqJ7Q4B6jwcOsxo3PfxVd15xj+IzVFxkPqIBmPQu8/w==" + "requested": "[15.0.0, )", + "resolved": "15.0.0", + "contentHash": "411vwC8/X8Z/sQ2TI6v3SvOn66xFPeOjFn3Zn+h0d3Ox2t1kFm66AhDvmx/qcMwVrR+Hidxj0dadpQ2dgyXMBQ==" }, "DotNet.ReproducibleBuilds": { "type": "Direct",